C2MV commited on
Commit
626f8a0
·
verified ·
1 Parent(s): edeffd7

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +325 -164
app.py CHANGED
@@ -25,6 +25,7 @@ client = anthropic.Anthropic()
25
  class AnalysisType(Enum):
26
  MATHEMATICAL_MODEL = "mathematical_model"
27
  DATA_FITTING = "data_fitting"
 
28
  UNKNOWN = "unknown"
29
 
30
  # Estructura modular para modelos
@@ -36,6 +37,7 @@ class MathematicalModel:
36
  application: str
37
  sources: List[str]
38
  category: str
 
39
 
40
  # Sistema de registro de modelos escalable
41
  class ModelRegistry:
@@ -66,7 +68,8 @@ class ModelRegistry:
66
  parameters=["μmax (h⁻¹)", "Ks (g/L)"],
67
  application="Crecimiento limitado por sustrato único",
68
  sources=["Cambridge", "MIT", "DTU"],
69
- category="crecimiento_biomasa"
 
70
  ))
71
 
72
  self.register_model(MathematicalModel(
@@ -75,7 +78,8 @@ class ModelRegistry:
75
  parameters=["μmax (h⁻¹)", "Xmax (g/L)"],
76
  application="Sistemas cerrados batch",
77
  sources=["Cranfield", "Swansea", "HAL Theses"],
78
- category="crecimiento_biomasa"
 
79
  ))
80
 
81
  self.register_model(MathematicalModel(
@@ -84,7 +88,8 @@ class ModelRegistry:
84
  parameters=["λ (h)", "μmax (h⁻¹)", "Xmax (g/L)"],
85
  application="Crecimiento con fase lag pronunciada",
86
  sources=["Lund University", "NC State"],
87
- category="crecimiento_biomasa"
 
88
  ))
89
 
90
  # Modelos enzimáticos
@@ -94,7 +99,8 @@ class ModelRegistry:
94
  parameters=["Vmax", "Km"],
95
  application="Cinética enzimática básica",
96
  sources=["Warsaw Univ Tech", "Food Processing"],
97
- category="consumo_sustrato"
 
98
  ))
99
 
100
  # Modelos de producto
@@ -104,7 +110,8 @@ class ModelRegistry:
104
  parameters=["α (asociado)", "β (no asociado)"],
105
  application="Producción mixta asociada/no asociada",
106
  sources=["Cambridge", "E-Century"],
107
- category="formacion_producto"
 
108
  ))
109
 
110
  # Instancia global del registro
@@ -187,16 +194,33 @@ class AIAnalyzer:
187
  def detect_analysis_type(self, content: Union[str, pd.DataFrame]) -> AnalysisType:
188
  """Detecta el tipo de análisis necesario"""
189
  if isinstance(content, pd.DataFrame):
190
- # Si es DataFrame, probablemente son datos para ajustar
191
- return AnalysisType.DATA_FITTING
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
192
 
193
  # Analizar texto para determinar tipo
194
  prompt = """
195
  Analiza este contenido y determina si es:
196
  1. Un artículo científico que describe modelos matemáticos biotecnológicos
197
- 2. Datos experimentales para ajuste de parámetros
 
198
 
199
- Responde solo con: "MODELO" o "DATOS"
200
  """
201
 
202
  try:
@@ -209,6 +233,8 @@ class AIAnalyzer:
209
  result = response.content[0].text.strip().upper()
210
  if "MODELO" in result:
211
  return AnalysisType.MATHEMATICAL_MODEL
 
 
212
  elif "DATOS" in result:
213
  return AnalysisType.DATA_FITTING
214
  else:
@@ -280,7 +306,7 @@ class AIAnalyzer:
280
  return {"error": str(e)}
281
 
282
  def analyze_fitting_data(self, data: pd.DataFrame, claude_model: str) -> Dict:
283
- """Analiza datos para ajuste de parámetros"""
284
  # Preparar resumen de datos
285
  data_summary = f"""
286
  Columnas: {list(data.columns)}
@@ -314,13 +340,86 @@ class AIAnalyzer:
314
  )
315
 
316
  return {
317
- "tipo": "Datos para Ajuste",
318
  "analisis": response.content[0].text,
319
  "resumen_datos": data_summary
320
  }
321
 
322
  except Exception as e:
323
  return {"error": str(e)}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
324
 
325
  def process_files(files, claude_model: str) -> str:
326
  """Procesa múltiples archivos"""
@@ -368,7 +467,13 @@ def process_files(files, claude_model: str) -> str:
368
  df = processor.read_excel(content)
369
 
370
  if df is not None:
371
- result = analyzer.analyze_fitting_data(df, claude_model)
 
 
 
 
 
 
372
  results.append(json.dumps(result, indent=2, ensure_ascii=False))
373
 
374
  results.append("\n---\n")
@@ -395,7 +500,15 @@ def process_files(files, claude_model: str) -> str:
395
  df = processor.read_excel(file_content)
396
 
397
  if df is not None:
398
- result = analyzer.analyze_fitting_data(df, claude_model)
 
 
 
 
 
 
 
 
399
  results.append(json.dumps(result, indent=2, ensure_ascii=False))
400
 
401
  results.append("\n---\n")
@@ -417,138 +530,138 @@ import seaborn as sns
417
  plt.style.use('seaborn-v0_8-darkgrid')
418
  sns.set_palette("husl")
419
 
420
- class BiotechModelFitter:
421
- \"\"\"Clase para ajuste de modelos biotecnológicos\"\"\"
422
 
423
  def __init__(self):
424
  self.models = {}
425
  self.fitted_params = {}
426
  self.results = {}
 
427
 
428
- def add_model(self, name, func, param_names):
429
- \"\"\"Registra un nuevo modelo\"\"\"
430
- self.models[name] = {
431
- 'function': func,
432
- 'parameters': param_names
433
- }
 
 
434
 
435
- def fit_model(self, model_name, x_data, y_data, bounds=None):
436
- \"\"\"Ajusta modelo a datos\"\"\"
437
- if model_name not in self.models:
438
- raise ValueError(f"Modelo {model_name} no registrado")
439
-
440
- model_func = self.models[model_name]['function']
441
-
442
- # Intentar ajuste con curve_fit
443
- try:
444
- if bounds:
445
- popt, pcov = curve_fit(model_func, x_data, y_data, bounds=bounds)
446
- else:
447
- popt, pcov = curve_fit(model_func, x_data, y_data)
448
-
449
- # Calcular métricas
450
- y_pred = model_func(x_data, *popt)
451
- r2 = r2_score(y_data, y_pred)
452
- rmse = np.sqrt(mean_squared_error(y_data, y_pred))
453
-
454
- self.fitted_params[model_name] = popt
455
- self.results[model_name] = {
456
- 'parameters': dict(zip(self.models[model_name]['parameters'], popt)),
457
- 'covariance': pcov,
458
- 'r2': r2,
459
- 'rmse': rmse
460
- }
461
 
462
- return True
463
-
464
- except Exception as e:
465
- print(f"Error en ajuste: {e}")
466
- # Intentar con optimización global
467
- return self._global_fit(model_name, x_data, y_data, bounds)
468
 
469
- def _global_fit(self, model_name, x_data, y_data, bounds):
470
- \"\"\"Ajuste global con differential evolution\"\"\"
471
- model_func = self.models[model_name]['function']
472
-
473
- def objective(params):
474
- y_pred = model_func(x_data, *params)
475
- return np.sum((y_data - y_pred)**2)
476
-
477
- if not bounds:
478
- # Bounds por defecto
479
- n_params = len(self.models[model_name]['parameters'])
480
- bounds = [(0, 100)] * n_params
481
-
482
- result = differential_evolution(objective, bounds)
483
-
484
- if result.success:
485
- popt = result.x
486
- y_pred = model_func(x_data, *popt)
487
- r2 = r2_score(y_data, y_pred)
488
- rmse = np.sqrt(mean_squared_error(y_data, y_pred))
489
-
490
- self.fitted_params[model_name] = popt
491
- self.results[model_name] = {
492
- 'parameters': dict(zip(self.models[model_name]['parameters'], popt)),
493
- 'r2': r2,
494
- 'rmse': rmse,
495
- 'optimization_result': result
496
  }
497
-
498
- return True
499
 
500
- return False
 
 
 
 
501
 
502
- def plot_results(self, x_data, y_data, models_to_plot=None):
503
- \"\"\"Visualiza resultados del ajuste\"\"\"
504
- plt.figure(figsize=(12, 8))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
505
 
506
- # Datos experimentales
507
- plt.scatter(x_data, y_data, label='Datos experimentales',
508
- s=50, alpha=0.7, edgecolors='black')
509
 
510
- # Modelos ajustados
511
- if models_to_plot is None:
512
- models_to_plot = self.fitted_params.keys()
513
 
514
- x_smooth = np.linspace(x_data.min(), x_data.max(), 300)
 
 
 
 
515
 
516
- for model_name in models_to_plot:
517
- if model_name in self.fitted_params:
518
- model_func = self.models[model_name]['function']
519
- params = self.fitted_params[model_name]
520
- y_smooth = model_func(x_smooth, *params)
521
-
522
- r2 = self.results[model_name]['r2']
523
- plt.plot(x_smooth, y_smooth,
524
- label=f'{model_name} (R² = {r2:.4f})',
525
- linewidth=2.5)
526
-
527
- plt.xlabel('Variable Independiente', fontsize=12)
528
- plt.ylabel('Variable Dependiente', fontsize=12)
529
- plt.title('Ajuste de Modelos Biotecnológicos', fontsize=14, fontweight='bold')
530
- plt.legend(loc='best', frameon=True, shadow=True)
531
- plt.grid(True, alpha=0.3)
532
- plt.tight_layout()
533
 
534
- return plt.gcf()
535
-
536
- def generate_report(self):
537
- \"\"\"Genera reporte de resultados\"\"\"
538
- report = "# Reporte de Ajuste de Modelos\\n\\n"
 
539
 
540
- for model_name, results in self.results.items():
541
- report += f"## Modelo: {model_name}\\n\\n"
542
- report += f"### Parámetros ajustados:\\n"
543
-
544
- for param, value in results['parameters'].items():
545
- report += f"- **{param}**: {value:.6f}\\n"
546
-
547
- report += f"\\n### Métricas de ajuste:\\n"
548
- report += f"- **R²**: {results['r2']:.6f}\\n"
549
- report += f"- **RMSE**: {results['rmse']:.6f}\\n\\n"
550
 
551
- return report
 
552
 
553
  # Modelos predefinidos comunes
554
  def monod_model(S, mu_max, Ks):
@@ -563,18 +676,24 @@ def gompertz_model(t, A, mu, lambda_param):
563
  def michaelis_menten(S, Vmax, Km):
564
  return Vmax * S / (Km + S)
565
 
566
- # Ejemplo de uso
567
  if __name__ == "__main__":
568
- # Crear instancia del ajustador
569
- fitter = BiotechModelFitter()
 
 
 
570
 
571
- # Registrar modelos
572
- fitter.add_model('Monod', monod_model, ['mu_max', 'Ks'])
573
- fitter.add_model('Michaelis-Menten', michaelis_menten, ['Vmax', 'Km'])
574
- fitter.add_model('Logistic', logistic_growth, ['K', 'r', 't0'])
575
 
576
- print("Sistema de ajuste listo para usar!")
577
- print("Carga tus datos y utiliza fitter.fit_model()")
 
 
 
 
 
578
  """
579
 
580
  return code
@@ -588,31 +707,44 @@ def create_interface():
588
  .gradio-container {
589
  font-family: 'Arial', sans-serif;
590
  }
 
 
 
 
 
 
591
  """
592
  ) as demo:
593
 
594
  gr.Markdown("""
595
  # 🧬 Analizador Inteligente de Modelos Biotecnológicos
596
 
597
- ### 🎯 Capacidades:
598
- - **Detección automática** del tipo de documento (artículo científico vs datos experimentales)
599
- - **Análisis de PDFs** con modelos matemáticos biotecnológicos
600
- - **Procesamiento de datos** CSV/Excel para ajuste de parámetros
601
- - **Soporte para múltiples archivos** y archivos ZIP
602
- - **Generación de código** Python para implementación
 
603
 
604
  ### 📁 Tipos de archivo soportados:
605
- - PDF (artículos científicos o reportes de datos)
606
- - CSV/Excel (datos experimentales)
607
- - ZIP (múltiples archivos)
 
 
 
 
 
 
608
  """)
609
 
610
  with gr.Row():
611
  with gr.Column(scale=1):
612
  files_input = gr.File(
613
- label="📁 Subir archivos",
614
  file_count="multiple",
615
- file_types=[".pdf", ".csv", ".xlsx", ".xls", ".zip"],
616
  type="filepath"
617
  )
618
 
@@ -624,7 +756,7 @@ def create_interface():
624
  )
625
 
626
  analyze_btn = gr.Button(
627
- "🚀 Analizar",
628
  variant="primary",
629
  size="lg"
630
  )
@@ -650,11 +782,12 @@ def create_interface():
650
 
651
  with gr.Column(scale=2):
652
  analysis_output = gr.Markdown(
653
- label="📊 Resultados del Análisis"
 
654
  )
655
 
656
  code_output = gr.Code(
657
- label="💻 Código de Implementación",
658
  language="python",
659
  interactive=True
660
  )
@@ -662,34 +795,62 @@ def create_interface():
662
  # Ejemplos
663
  gr.Examples(
664
  examples=[
665
- [["examples/growth_kinetics.pdf"]],
666
- [["examples/experimental_data.csv"]],
667
- [["examples/multiple_files.zip"]]
668
  ],
669
  inputs=[files_input],
670
- label="📚 Ejemplos"
671
  )
672
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
673
  # Footer
674
  gr.Markdown("""
675
  ---
676
  ### 🔧 Características técnicas:
677
- - **Base de modelos escalable**: Fácil adición de nuevos modelos matemáticos
678
- - **Análisis con IA**: Detección automática del contexto y tipo de análisis
679
- - **Optimizado para HuggingFace**: Configuración lista para deployment
680
- - **Código modular**: Arquitectura flexible y mantenible
681
-
682
- ### 📖 Instrucciones:
683
- 1. Sube uno o varios archivos (PDF, CSV, Excel o ZIP)
684
- 2. El sistema detectará automáticamente el tipo de análisis necesario
685
- 3. Revisa los resultados y el código generado
686
- 4. Copia el código para tu implementación
687
  """)
688
 
689
  # Eventos
690
  analyze_btn.click(
691
  fn=lambda files, model: (
692
- process_files(files, model) if files else "Por favor sube archivos para analizar",
693
  generate_implementation_code("") if files else ""
694
  ),
695
  inputs=[files_input, model_selector],
 
25
  class AnalysisType(Enum):
26
  MATHEMATICAL_MODEL = "mathematical_model"
27
  DATA_FITTING = "data_fitting"
28
+ FITTING_RESULTS = "fitting_results" # Nuevo: resultados de ajuste
29
  UNKNOWN = "unknown"
30
 
31
  # Estructura modular para modelos
 
37
  application: str
38
  sources: List[str]
39
  category: str
40
+ biological_meaning: str
41
 
42
  # Sistema de registro de modelos escalable
43
  class ModelRegistry:
 
68
  parameters=["μmax (h⁻¹)", "Ks (g/L)"],
69
  application="Crecimiento limitado por sustrato único",
70
  sources=["Cambridge", "MIT", "DTU"],
71
+ category="crecimiento_biomasa",
72
+ biological_meaning="Describe cómo la velocidad de crecimiento depende de la concentración de sustrato limitante"
73
  ))
74
 
75
  self.register_model(MathematicalModel(
 
78
  parameters=["μmax (h⁻¹)", "Xmax (g/L)"],
79
  application="Sistemas cerrados batch",
80
  sources=["Cranfield", "Swansea", "HAL Theses"],
81
+ category="crecimiento_biomasa",
82
+ biological_meaning="Modela crecimiento limitado por capacidad de carga del sistema"
83
  ))
84
 
85
  self.register_model(MathematicalModel(
 
88
  parameters=["λ (h)", "μmax (h⁻¹)", "Xmax (g/L)"],
89
  application="Crecimiento con fase lag pronunciada",
90
  sources=["Lund University", "NC State"],
91
+ category="crecimiento_biomasa",
92
+ biological_meaning="Incluye fase de adaptación (lag) seguida de crecimiento exponencial y estacionario"
93
  ))
94
 
95
  # Modelos enzimáticos
 
99
  parameters=["Vmax", "Km"],
100
  application="Cinética enzimática básica",
101
  sources=["Warsaw Univ Tech", "Food Processing"],
102
+ category="consumo_sustrato",
103
+ biological_meaning="Describe saturación enzimática con afinidad por sustrato"
104
  ))
105
 
106
  # Modelos de producto
 
110
  parameters=["α (asociado)", "β (no asociado)"],
111
  application="Producción mixta asociada/no asociada",
112
  sources=["Cambridge", "E-Century"],
113
+ category="formacion_producto",
114
+ biological_meaning="Separa producción ligada al crecimiento de la producción metabólica"
115
  ))
116
 
117
  # Instancia global del registro
 
194
  def detect_analysis_type(self, content: Union[str, pd.DataFrame]) -> AnalysisType:
195
  """Detecta el tipo de análisis necesario"""
196
  if isinstance(content, pd.DataFrame):
197
+ # Analizar si son datos experimentales o resultados de ajuste
198
+ columns = [col.lower() for col in content.columns]
199
+
200
+ # Indicadores de resultados de ajuste
201
+ fitting_indicators = [
202
+ 'r2', 'r_squared', 'rmse', 'mse', 'aic', 'bic',
203
+ 'parameter', 'param', 'coefficient', 'fit',
204
+ 'model', 'equation', 'goodness', 'chi_square',
205
+ 'p_value', 'confidence', 'standard_error', 'se'
206
+ ]
207
+
208
+ # Verificar si hay indicadores de resultados de ajuste
209
+ has_fitting_results = any(indicator in ' '.join(columns) for indicator in fitting_indicators)
210
+
211
+ if has_fitting_results:
212
+ return AnalysisType.FITTING_RESULTS
213
+ else:
214
+ return AnalysisType.DATA_FITTING
215
 
216
  # Analizar texto para determinar tipo
217
  prompt = """
218
  Analiza este contenido y determina si es:
219
  1. Un artículo científico que describe modelos matemáticos biotecnológicos
220
+ 2. Datos experimentales para ajuste de parámetros
221
+ 3. Resultados de ajuste de modelos (con parámetros, R², RMSE, etc.)
222
 
223
+ Responde solo con: "MODELO", "DATOS" o "RESULTADOS"
224
  """
225
 
226
  try:
 
233
  result = response.content[0].text.strip().upper()
234
  if "MODELO" in result:
235
  return AnalysisType.MATHEMATICAL_MODEL
236
+ elif "RESULTADOS" in result:
237
+ return AnalysisType.FITTING_RESULTS
238
  elif "DATOS" in result:
239
  return AnalysisType.DATA_FITTING
240
  else:
 
306
  return {"error": str(e)}
307
 
308
  def analyze_fitting_data(self, data: pd.DataFrame, claude_model: str) -> Dict:
309
+ """Analiza datos experimentales para ajuste de parámetros"""
310
  # Preparar resumen de datos
311
  data_summary = f"""
312
  Columnas: {list(data.columns)}
 
340
  )
341
 
342
  return {
343
+ "tipo": "Datos Experimentales para Ajuste",
344
  "analisis": response.content[0].text,
345
  "resumen_datos": data_summary
346
  }
347
 
348
  except Exception as e:
349
  return {"error": str(e)}
350
+
351
+ def analyze_fitting_results(self, data: pd.DataFrame, claude_model: str) -> Dict:
352
+ """Analiza resultados de ajuste de modelos cinéticos"""
353
+ # Preparar resumen detallado de resultados
354
+ data_summary = f"""
355
+ DATOS DE RESULTADOS DE AJUSTE:
356
+
357
+ Columnas disponibles: {list(data.columns)}
358
+ Forma de los datos: {data.shape}
359
+
360
+ Datos completos:
361
+ {data.to_string()}
362
+
363
+ Estadísticas descriptivas:
364
+ {data.describe().to_string()}
365
+ """
366
+
367
+ prompt = """
368
+ Estos son RESULTADOS DE AJUSTE DE MODELOS CINÉTICOS/BIOTECNOLÓGICOS ya calculados.
369
+
370
+ Necesito que analices:
371
+
372
+ 1. **IDENTIFICACIÓN DE MODELOS**: ¿Qué modelos matemáticos se ajustaron? (Monod, Logístico, Gompertz, Michaelis-Menten, etc.)
373
+
374
+ 2. **CALIDAD DEL AJUSTE**:
375
+ - Compara R², RMSE, AIC, BIC entre modelos
376
+ - ¿Cuál ajusta mejor y por qué?
377
+ - ¿Hay sobreajuste o subajuste?
378
+
379
+ 3. **SIGNIFICADO BIOLÓGICO**:
380
+ - ¿Qué significan los parámetros estimados biológicamente?
381
+ - ¿Son valores realistas para el sistema biológico?
382
+ - ¿Qué información nos dan sobre el proceso?
383
+
384
+ 4. **INFERENCIA DEL DISEÑO EXPERIMENTAL**:
385
+ - ¿Qué tipo de experimento se realizó?
386
+ - ¿Qué variables se midieron?
387
+ - ¿Batch, continuo, fed-batch?
388
+
389
+ 5. **INTERPRETACIÓN EN LENGUAJE HUMANO**:
390
+ - Traduce los resultados técnicos a conclusiones comprensibles
391
+ - ¿Qué nos dicen sobre el comportamiento del microorganismo/proceso?
392
+ - ¿Cuáles son las implicaciones prácticas?
393
+
394
+ 6. **RECOMENDACIONES**:
395
+ - ¿Qué modelo(s) recomiendas usar?
396
+ - ¿Qué limitaciones tienen?
397
+ - ¿Qué experimentos adicionales serían útiles?
398
+
399
+ Estructura tu respuesta de forma clara y detallada.
400
+ """
401
+
402
+ try:
403
+ response = self.client.messages.create(
404
+ model=claude_model,
405
+ max_tokens=4000,
406
+ messages=[{
407
+ "role": "user",
408
+ "content": f"{prompt}\n\nRESULTADOS DE AJUSTE:\n{data_summary}"
409
+ }]
410
+ )
411
+
412
+ return {
413
+ "tipo": "Análisis de Resultados de Ajuste de Modelos Cinéticos",
414
+ "analisis_completo": response.content[0].text,
415
+ "resumen_datos": data_summary,
416
+ "n_modelos": len(data),
417
+ "metricas_disponibles": [col for col in data.columns if any(metric in col.lower()
418
+ for metric in ['r2', 'rmse', 'aic', 'bic', 'mse'])]
419
+ }
420
+
421
+ except Exception as e:
422
+ return {"error": str(e)}
423
 
424
  def process_files(files, claude_model: str) -> str:
425
  """Procesa múltiples archivos"""
 
467
  df = processor.read_excel(content)
468
 
469
  if df is not None:
470
+ analysis_type = analyzer.detect_analysis_type(df)
471
+
472
+ if analysis_type == AnalysisType.FITTING_RESULTS:
473
+ result = analyzer.analyze_fitting_results(df, claude_model)
474
+ else:
475
+ result = analyzer.analyze_fitting_data(df, claude_model)
476
+
477
  results.append(json.dumps(result, indent=2, ensure_ascii=False))
478
 
479
  results.append("\n---\n")
 
500
  df = processor.read_excel(file_content)
501
 
502
  if df is not None:
503
+ analysis_type = analyzer.detect_analysis_type(df)
504
+
505
+ if analysis_type == AnalysisType.FITTING_RESULTS:
506
+ result = analyzer.analyze_fitting_results(df, claude_model)
507
+ results.append("### 🎯 ANÁLISIS DE RESULTADOS DE AJUSTE")
508
+ else:
509
+ result = analyzer.analyze_fitting_data(df, claude_model)
510
+ results.append("### 📈 ANÁLISIS DE DATOS EXPERIMENTALES")
511
+
512
  results.append(json.dumps(result, indent=2, ensure_ascii=False))
513
 
514
  results.append("\n---\n")
 
530
  plt.style.use('seaborn-v0_8-darkgrid')
531
  sns.set_palette("husl")
532
 
533
+ class BiotechModelAnalyzer:
534
+ \"\"\"Clase para análisis de resultados de ajuste de modelos biotecnológicos\"\"\"
535
 
536
  def __init__(self):
537
  self.models = {}
538
  self.fitted_params = {}
539
  self.results = {}
540
+ self.biological_interpretations = {}
541
 
542
+ def load_fitting_results(self, data_path):
543
+ \"\"\"Carga resultados de ajuste desde CSV/Excel\"\"\"
544
+ if data_path.endswith('.csv'):
545
+ self.results_df = pd.read_csv(data_path)
546
+ else:
547
+ self.results_df = pd.read_excel(data_path)
548
+
549
+ return self.results_df
550
 
551
+ def compare_models(self, r2_col='R2', rmse_col='RMSE', model_col='Model'):
552
+ \"\"\"Compara modelos basado en métricas de ajuste\"\"\"
553
+ if not hasattr(self, 'results_df'):
554
+ raise ValueError("Primero carga los datos con load_fitting_results()")
555
+
556
+ # Ordenar por R² descendente
557
+ comparison = self.results_df.sort_values(by=r2_col, ascending=False)
558
+
559
+ print("=== COMPARACIÓN DE MODELOS ===")
560
+ print(f"{'Modelo':<20} {'R²':<10} {'RMSE':<10} {'Ranking':<10}")
561
+ print("-" * 60)
562
+
563
+ for i, (idx, row) in enumerate(comparison.iterrows()):
564
+ model_name = row[model_col] if model_col in row else f"Modelo_{idx}"
565
+ r2_val = row[r2_col] if r2_col in row else "N/A"
566
+ rmse_val = row[rmse_col] if rmse_col in row else "N/A"
 
 
 
 
 
 
 
 
 
 
567
 
568
+ print(f"{model_name:<20} {r2_val:<10.4f} {rmse_val:<10.4f} {i+1:<10}")
569
+
570
+ return comparison
 
 
 
571
 
572
+ def interpret_biological_meaning(self, model_name, parameters):
573
+ \"\"\"Interpreta el significado biológico de parámetros\"\"\"
574
+ interpretations = {
575
+ 'monod': {
576
+ 'mu_max': 'Velocidad máxima específica de crecimiento',
577
+ 'Ks': 'Constante de saturación (afinidad por sustrato)',
578
+ 'biological_meaning': 'Crecimiento limitado por sustrato único'
579
+ },
580
+ 'logistic': {
581
+ 'K': 'Capacidad de carga del sistema',
582
+ 'r': 'Tasa intrínseca de crecimiento',
583
+ 'biological_meaning': 'Crecimiento limitado por densidad poblacional'
584
+ },
585
+ 'gompertz': {
586
+ 'A': 'Asíntota superior (biomasa máxima)',
587
+ 'mu': 'Velocidad máxima de crecimiento',
588
+ 'lambda': 'Tiempo de fase lag',
589
+ 'biological_meaning': 'Crecimiento con adaptación inicial'
 
 
 
 
 
 
 
 
 
590
  }
591
+ }
 
592
 
593
+ model_key = model_name.lower()
594
+ if model_key in interpretations:
595
+ return interpretations[model_key]
596
+ else:
597
+ return {'biological_meaning': 'Modelo no reconocido en base de datos'}
598
 
599
+ def generate_human_readable_report(self):
600
+ \"\"\"Genera reporte en lenguaje humano\"\"\"
601
+ if not hasattr(self, 'results_df'):
602
+ raise ValueError("Primero carga los datos")
603
+
604
+ report = []
605
+ report.append("# 🧬 REPORTE DE ANÁLISIS BIOTECNOLÓGICO")
606
+ report.append("=" * 50)
607
+ report.append("")
608
+
609
+ # Mejor modelo
610
+ best_model = self.results_df.loc[self.results_df['R2'].idxmax()]
611
+ report.append(f"## 🏆 MEJOR MODELO IDENTIFICADO")
612
+ report.append(f"**Modelo:** {best_model.get('Model', 'No especificado')}")
613
+ report.append(f"**Calidad del ajuste:** R² = {best_model.get('R2', 'N/A'):.4f}")
614
+ report.append(f"**Error:** RMSE = {best_model.get('RMSE', 'N/A'):.4f}")
615
+ report.append("")
616
+
617
+ # Interpretación biológica
618
+ report.append("## 🔬 SIGNIFICADO BIOLÓGICO")
619
+ report.append("Los parámetros ajustados nos indican:")
620
+
621
+ # Aquí puedes expandir según los modelos específicos encontrados
622
+ report.append("- El sistema estudiado muestra un comportamiento predecible")
623
+ report.append("- Los parámetros están dentro de rangos biológicamente plausibles")
624
+ report.append("")
625
+
626
+ # Recomendaciones
627
+ report.append("## 💡 RECOMENDACIONES")
628
+ report.append("1. Validar el modelo con datos independientes")
629
+ report.append("2. Considerar factores ambientales adicionales")
630
+ report.append("3. Evaluar la robustez del modelo")
631
+
632
+ return "\\n".join(report)
633
+
634
+ def plot_model_comparison(self):
635
+ \"\"\"Visualiza comparación de modelos\"\"\"
636
+ if not hasattr(self, 'results_df'):
637
+ raise ValueError("Primero carga los datos")
638
 
639
+ fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(15, 6))
 
 
640
 
641
+ # Gráfico de R²
642
+ models = self.results_df.get('Model', range(len(self.results_df)))
643
+ r2_values = self.results_df.get('R2', [])
644
 
645
+ ax1.bar(models, r2_values, color='skyblue', edgecolor='navy', alpha=0.7)
646
+ ax1.set_title('Comparación de R² por Modelo', fontsize=14, fontweight='bold')
647
+ ax1.set_ylabel('R² (Coeficiente de Determinación)', fontsize=12)
648
+ ax1.set_ylim(0, 1)
649
+ ax1.grid(True, alpha=0.3)
650
 
651
+ # Rotar etiquetas si es necesario
652
+ plt.setp(ax1.get_xticklabels(), rotation=45, ha='right')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
653
 
654
+ # Gráfico de RMSE
655
+ rmse_values = self.results_df.get('RMSE', [])
656
+ ax2.bar(models, rmse_values, color='lightcoral', edgecolor='darkred', alpha=0.7)
657
+ ax2.set_title('Comparación de RMSE por Modelo', fontsize=14, fontweight='bold')
658
+ ax2.set_ylabel('RMSE (Error Cuadrático Medio)', fontsize=12)
659
+ ax2.grid(True, alpha=0.3)
660
 
661
+ plt.setp(ax2.get_xticklabels(), rotation=45, ha='right')
 
 
 
 
 
 
 
 
 
662
 
663
+ plt.tight_layout()
664
+ return fig
665
 
666
  # Modelos predefinidos comunes
667
  def monod_model(S, mu_max, Ks):
 
676
  def michaelis_menten(S, Vmax, Km):
677
  return Vmax * S / (Km + S)
678
 
679
+ # Ejemplo de uso para análisis de resultados
680
  if __name__ == "__main__":
681
+ # Crear instancia del analizador
682
+ analyzer = BiotechModelAnalyzer()
683
+
684
+ # Ejemplo de carga de datos
685
+ # analyzer.load_fitting_results('resultados_ajuste.csv')
686
 
687
+ # Ejemplo de comparación
688
+ # comparison = analyzer.compare_models()
 
 
689
 
690
+ # Generar reporte
691
+ # report = analyzer.generate_human_readable_report()
692
+ # print(report)
693
+
694
+ print("🔬 Sistema de análisis de resultados listo!")
695
+ print("📊 Carga tus resultados CSV y utiliza analyzer.load_fitting_results()")
696
+ print("📈 Luego usa analyzer.compare_models() para comparar")
697
  """
698
 
699
  return code
 
707
  .gradio-container {
708
  font-family: 'Arial', sans-serif;
709
  }
710
+ .highlight-results {
711
+ background-color: #f0f8ff;
712
+ padding: 15px;
713
+ border-radius: 10px;
714
+ border-left: 5px solid #4CAF50;
715
+ }
716
  """
717
  ) as demo:
718
 
719
  gr.Markdown("""
720
  # 🧬 Analizador Inteligente de Modelos Biotecnológicos
721
 
722
+ ### 🎯 Especializado en Análisis de Resultados de Ajuste:
723
+ - **Análisis automático** de resultados de ajuste de modelos cinéticos
724
+ - **Interpretación biológica** de parámetros y métricas
725
+ - **Comparación inteligente** entre modelos (R², RMSE, AIC, BIC)
726
+ - **Traducción a lenguaje humano** de resultados técnicos
727
+ - **Inferencia del diseño experimental** a partir de los datos
728
+ - **Recomendaciones** sobre qué modelos usar y por qué
729
 
730
  ### 📁 Tipos de archivo soportados:
731
+ - **CSV/Excel** con resultados de ajuste (parámetros, R², RMSE, etc.)
732
+ - **PDF** con artículos científicos o reportes
733
+ - **ZIP** con múltiples archivos
734
+
735
+ ### 🔍 ¿Qué analiza específicamente?
736
+ - Calidad del ajuste y comparación entre modelos
737
+ - Significado biológico de parámetros estimados
738
+ - Detección de sobreajuste o problemas en el ajuste
739
+ - Interpretación de resultados en contexto biotecnológico
740
  """)
741
 
742
  with gr.Row():
743
  with gr.Column(scale=1):
744
  files_input = gr.File(
745
+ label="📁 Subir resultados de ajuste (CSV/Excel)",
746
  file_count="multiple",
747
+ file_types=[".csv", ".xlsx", ".xls", ".pdf", ".zip"],
748
  type="filepath"
749
  )
750
 
 
756
  )
757
 
758
  analyze_btn = gr.Button(
759
+ "🚀 Analizar Resultados",
760
  variant="primary",
761
  size="lg"
762
  )
 
782
 
783
  with gr.Column(scale=2):
784
  analysis_output = gr.Markdown(
785
+ label="📊 Análisis de Resultados",
786
+ elem_classes=["highlight-results"]
787
  )
788
 
789
  code_output = gr.Code(
790
+ label="💻 Código de Análisis",
791
  language="python",
792
  interactive=True
793
  )
 
795
  # Ejemplos
796
  gr.Examples(
797
  examples=[
798
+ [["examples/fitting_results.csv"]],
799
+ [["examples/model_comparison.xlsx"]],
800
+ [["examples/kinetic_parameters.csv"]]
801
  ],
802
  inputs=[files_input],
803
+ label="📚 Ejemplos de resultados de ajuste"
804
  )
805
 
806
+ # Guía de uso
807
+ gr.Markdown("""
808
+ ---
809
+ ### 📋 Guía de uso para resultados de ajuste:
810
+
811
+ **Para obtener el mejor análisis, asegúrate que tu CSV/Excel contenga:**
812
+
813
+ 1. **Columna de modelos**: Nombres de los modelos ajustados (Monod, Logístico, Gompertz, etc.)
814
+ 2. **Métricas de ajuste**: R², RMSE, AIC, BIC, MSE, etc.
815
+ 3. **Parámetros**: Valores de parámetros estimados (μmax, Ks, K, etc.)
816
+ 4. **Errores estándar**: Si están disponibles
817
+
818
+ **Ejemplo de estructura ideal:**
819
+ ```
820
+ Model | R2 | RMSE | mu_max | Ks | AIC
821
+ Monod | 0.985 | 0.023 | 0.45 | 2.1 | -45.2
822
+ Logistic | 0.976 | 0.031 | 0.42 | 15.3 | -42.1
823
+ Gompertz | 0.992 | 0.018 | 0.48 | 1.8 | -48.5
824
+ ```
825
+
826
+ ### 🔬 Lo que obtendrás:
827
+ - **Ranking de modelos** basado en calidad de ajuste
828
+ - **Interpretación biológica** de cada parámetro
829
+ - **Análisis del diseño experimental** inferido
830
+ - **Recomendaciones** sobre cuál modelo usar
831
+ - **Explicación en lenguaje simple** de los resultados
832
+ """)
833
+
834
  # Footer
835
  gr.Markdown("""
836
  ---
837
  ### 🔧 Características técnicas:
838
+ - **Detección automática** de tipo de análisis (datos vs resultados)
839
+ - **Interpretación contextual** de parámetros biotecnológicos
840
+ - **Análisis comparativo** inteligente entre modelos
841
+ - **Traducción técnica** a lenguaje comprensible
842
+
843
+ ### 💡 Casos de uso:
844
+ - Análisis de resultados de ajustes de crecimiento microbiano
845
+ - Comparación de modelos de consumo de sustrato
846
+ - Evaluación de modelos de formación de producto
847
+ - Interpretación de parámetros cinéticos
848
  """)
849
 
850
  # Eventos
851
  analyze_btn.click(
852
  fn=lambda files, model: (
853
+ process_files(files, model) if files else "Por favor sube archivos con resultados de ajuste para analizar",
854
  generate_implementation_code("") if files else ""
855
  ),
856
  inputs=[files_input, model_selector],