Spaces:
Sleeping
Sleeping
Update app.py
Browse files
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 |
-
#
|
191 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 "
|
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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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
|
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
|
429 |
-
\"\"\"
|
430 |
-
|
431 |
-
|
432 |
-
|
433 |
-
|
|
|
|
|
434 |
|
435 |
-
def
|
436 |
-
\"\"\"
|
437 |
-
if
|
438 |
-
raise ValueError(
|
439 |
-
|
440 |
-
|
441 |
-
|
442 |
-
|
443 |
-
|
444 |
-
|
445 |
-
|
446 |
-
|
447 |
-
|
448 |
-
|
449 |
-
|
450 |
-
|
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 |
-
|
463 |
-
|
464 |
-
|
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
|
470 |
-
\"\"\"
|
471 |
-
|
472 |
-
|
473 |
-
|
474 |
-
|
475 |
-
|
476 |
-
|
477 |
-
|
478 |
-
|
479 |
-
|
480 |
-
|
481 |
-
|
482 |
-
|
483 |
-
|
484 |
-
|
485 |
-
|
486 |
-
|
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 |
-
|
|
|
|
|
|
|
|
|
501 |
|
502 |
-
def
|
503 |
-
\"\"\"
|
504 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
505 |
|
506 |
-
|
507 |
-
plt.scatter(x_data, y_data, label='Datos experimentales',
|
508 |
-
s=50, alpha=0.7, edgecolors='black')
|
509 |
|
510 |
-
#
|
511 |
-
|
512 |
-
|
513 |
|
514 |
-
|
|
|
|
|
|
|
|
|
515 |
|
516 |
-
|
517 |
-
|
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 |
-
|
535 |
-
|
536 |
-
|
537 |
-
|
538 |
-
|
|
|
539 |
|
540 |
-
|
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 |
-
|
|
|
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
|
569 |
-
|
|
|
|
|
|
|
570 |
|
571 |
-
#
|
572 |
-
|
573 |
-
fitter.add_model('Michaelis-Menten', michaelis_menten, ['Vmax', 'Km'])
|
574 |
-
fitter.add_model('Logistic', logistic_growth, ['K', 'r', 't0'])
|
575 |
|
576 |
-
|
577 |
-
|
|
|
|
|
|
|
|
|
|
|
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 |
-
### 🎯
|
598 |
-
- **
|
599 |
-
- **
|
600 |
-
- **
|
601 |
-
- **
|
602 |
-
- **
|
|
|
603 |
|
604 |
### 📁 Tipos de archivo soportados:
|
605 |
-
-
|
606 |
-
-
|
607 |
-
- ZIP
|
|
|
|
|
|
|
|
|
|
|
|
|
608 |
""")
|
609 |
|
610 |
with gr.Row():
|
611 |
with gr.Column(scale=1):
|
612 |
files_input = gr.File(
|
613 |
-
label="📁 Subir
|
614 |
file_count="multiple",
|
615 |
-
file_types=[".
|
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="📊
|
|
|
654 |
)
|
655 |
|
656 |
code_output = gr.Code(
|
657 |
-
label="💻 Código de
|
658 |
language="python",
|
659 |
interactive=True
|
660 |
)
|
@@ -662,34 +795,62 @@ def create_interface():
|
|
662 |
# Ejemplos
|
663 |
gr.Examples(
|
664 |
examples=[
|
665 |
-
[["examples/
|
666 |
-
[["examples/
|
667 |
-
[["examples/
|
668 |
],
|
669 |
inputs=[files_input],
|
670 |
-
label="📚 Ejemplos"
|
671 |
)
|
672 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
673 |
# Footer
|
674 |
gr.Markdown("""
|
675 |
---
|
676 |
### 🔧 Características técnicas:
|
677 |
-
- **
|
678 |
-
- **
|
679 |
-
- **
|
680 |
-
- **
|
681 |
-
|
682 |
-
###
|
683 |
-
|
684 |
-
|
685 |
-
|
686 |
-
|
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],
|