C2MV commited on
Commit
9b58814
·
verified ·
1 Parent(s): 3e2f067

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +699 -747
app.py CHANGED
@@ -8,12 +8,24 @@ import os
8
  import json
9
  import zipfile
10
  import tempfile
11
- from typing import Dict, List, Tuple, Union
12
  import re
13
  from pathlib import Path
14
  import openpyxl
15
  from dataclasses import dataclass
16
  from enum import Enum
 
 
 
 
 
 
 
 
 
 
 
 
17
 
18
  # Configuración para HuggingFace
19
  os.environ['GRADIO_ANALYTICS_ENABLED'] = 'False'
@@ -21,6 +33,181 @@ os.environ['GRADIO_ANALYTICS_ENABLED'] = 'False'
21
  # Inicializar cliente Anthropic
22
  client = anthropic.Anthropic()
23
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
  # Enum para tipos de análisis
25
  class AnalysisType(Enum):
26
  MATHEMATICAL_MODEL = "mathematical_model"
@@ -91,28 +278,6 @@ class ModelRegistry:
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
96
- self.register_model(MathematicalModel(
97
- name="Michaelis-Menten",
98
- equation="v = Vmax × S / (Km + S)",
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
107
- self.register_model(MathematicalModel(
108
- name="Luedeking-Piret",
109
- equation="dP/dt = α × (dX/dt) + β × X",
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
118
  model_registry = ModelRegistry()
@@ -121,21 +286,21 @@ model_registry = ModelRegistry()
121
  CLAUDE_MODELS = {
122
  "claude-3-5-sonnet-20241022": {
123
  "name": "Claude 3.5 Sonnet",
124
- "description": "Modelo rápido y eficiente",
125
  "max_tokens": 4000,
126
- "best_for": "Análisis general"
127
  },
128
  "claude-3-opus-20240229": {
129
  "name": "Claude 3 Opus",
130
- "description": "Modelo más potente",
131
  "max_tokens": 4000,
132
- "best_for": "Análisis complejos"
133
  },
134
  "claude-3-haiku-20240307": {
135
  "name": "Claude 3 Haiku",
136
- "description": "Modelo más rápido",
137
  "max_tokens": 4000,
138
- "best_for": "Análisis rápidos"
139
  }
140
  }
141
 
@@ -152,7 +317,7 @@ class FileProcessor:
152
  text += page.extract_text() + "\n"
153
  return text
154
  except Exception as e:
155
- return f"Error al leer PDF: {str(e)}"
156
 
157
  @staticmethod
158
  def read_csv(csv_file) -> pd.DataFrame:
@@ -181,9 +346,162 @@ class FileProcessor:
181
  file_data = zip_ref.read(file_name)
182
  files.append((file_name, file_data))
183
  except Exception as e:
184
- print(f"Error procesando ZIP: {e}")
185
  return files
186
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
187
  class AIAnalyzer:
188
  """Clase para análisis con IA"""
189
 
@@ -211,12 +529,12 @@ class AIAnalyzer:
211
  return AnalysisType.DATA_FITTING
212
 
213
  prompt = """
214
- Analiza este contenido y determina si es:
215
- 1. Un artículo científico que describe modelos matemáticos biotecnológicos
216
- 2. Datos experimentales para ajuste de parámetros
217
- 3. Resultados de ajuste de modelos (con parámetros, R², RMSE, etc.)
218
 
219
- Responde solo con: "MODELO", "DATOS" o "RESULTADOS"
220
  """
221
 
222
  try:
@@ -227,11 +545,11 @@ class AIAnalyzer:
227
  )
228
 
229
  result = response.content[0].text.strip().upper()
230
- if "MODELO" in result:
231
  return AnalysisType.MATHEMATICAL_MODEL
232
- elif "RESULTADOS" in result:
233
  return AnalysisType.FITTING_RESULTS
234
- elif "DATOS" in result:
235
  return AnalysisType.DATA_FITTING
236
  else:
237
  return AnalysisType.UNKNOWN
@@ -239,206 +557,120 @@ class AIAnalyzer:
239
  except:
240
  return AnalysisType.UNKNOWN
241
 
242
- def analyze_mathematical_article(self, text: str, claude_model: str) -> Dict:
243
- """Analiza artículo con modelos matemáticos"""
244
- prompts = {
245
- "identificar_modelos": """
246
- Analiza este texto científico e identifica:
247
- 1. Modelos matemáticos biotecnológicos descritos
248
- 2. Ecuaciones específicas
249
- 3. Parámetros mencionados
250
- 4. Aplicaciones biotecnológicas
251
- 5. Microorganismos y procesos
252
-
253
- Formato JSON con estructura:
254
- {
255
- "modelos": ["nombre1", "nombre2"],
256
- "ecuaciones": ["eq1", "eq2"],
257
- "parametros": ["param1", "param2"],
258
- "aplicaciones": ["app1", "app2"],
259
- "microorganismos": ["org1", "org2"]
260
- }
261
- """,
262
-
263
- "recomendar_implementacion": """
264
- Basado en los modelos identificados, proporciona:
265
- 1. Estrategia de implementación
266
- 2. Consideraciones experimentales
267
- 3. Métodos de validación
268
- 4. Posibles limitaciones
269
- """
270
  }
271
-
272
- try:
273
- response = self.client.messages.create(
274
- model=claude_model,
275
- max_tokens=2000,
276
- messages=[{
277
- "role": "user",
278
- "content": f"{prompts['identificar_modelos']}\n\nTEXTO:\n{text[:3000]}"
279
- }]
280
- )
281
-
282
- models_info = response.content[0].text
283
-
284
- response2 = self.client.messages.create(
285
- model=claude_model,
286
- max_tokens=2000,
287
- messages=[{
288
- "role": "user",
289
- "content": f"{prompts['recomendar_implementacion']}\n\nMODELOS:\n{models_info}"
290
- }]
291
- )
292
-
293
- return {
294
- "tipo": "Artículo de Modelos Matemáticos",
295
- "modelos": models_info,
296
- "recomendaciones": response2.content[0].text
297
- }
298
-
299
- except Exception as e:
300
- return {"error": str(e)}
301
 
302
- def analyze_fitting_data(self, data: pd.DataFrame, claude_model: str) -> Dict:
303
- """Analiza datos experimentales para ajuste de parámetros"""
304
- data_summary = f"""
305
- Columnas: {list(data.columns)}
306
- Forma: {data.shape}
307
- Primeras filas:
308
- {data.head().to_string()}
309
-
310
- Estadísticas:
311
- {data.describe().to_string()}
312
- """
313
-
314
- prompt = """
315
- Analiza estos datos experimentales y determina:
316
- 1. Variables independientes y dependientes
317
- 2. Posibles modelos matemáticos aplicables
318
- 3. Método de ajuste recomendado
319
- 4. Parámetros a estimar
320
- 5. Calidad esperada del ajuste
321
-
322
- Proporciona código Python para el ajuste.
323
- """
324
-
325
- try:
326
- response = self.client.messages.create(
327
- model=claude_model,
328
- max_tokens=3000,
329
- messages=[{
330
- "role": "user",
331
- "content": f"{prompt}\n\nDATOS:\n{data_summary}"
332
- }]
333
- )
334
-
335
- return {
336
- "tipo": "Datos Experimentales para Ajuste",
337
- "analisis": response.content[0].text,
338
- "resumen_datos": data_summary
339
- }
340
-
341
- except Exception as e:
342
- return {"error": str(e)}
343
-
344
- def analyze_fitting_results(self, data: pd.DataFrame, claude_model: str, detail_level: str = "detallado") -> Dict:
345
- """Analiza resultados de ajuste de modelos con enfoque mejorado"""
346
 
347
  # Preparar resumen completo de los datos
348
  data_summary = f"""
349
- RESULTADOS DE AJUSTE DE MODELOS MATEMÁTICOS:
350
 
351
- Estructura de datos:
352
- - Columnas: {list(data.columns)}
353
- - Número de modelos evaluados: {len(data)}
354
 
355
- Datos completos:
356
  {data.to_string()}
357
 
358
- Estadísticas descriptivas:
359
  {data.describe().to_string()}
360
  """
361
 
362
- # Prompt mejorado y especializado
 
 
 
363
  prompt = f"""
364
- Eres un experto en biotecnología y modelado matemático. Analiza estos resultados de ajuste de modelos cinéticos/biotecnológicos.
365
-
366
- NIVEL DE DETALLE SOLICITADO: {detail_level}
367
-
368
- REALIZA UN ANÁLISIS COMPARATIVO EXHAUSTIVO:
369
-
370
- 1. **IDENTIFICACIÓN Y CLASIFICACIÓN DE MODELOS**
371
- - Identifica TODOS los modelos matemáticos ajustados
372
- - Clasifícalos por tipo: biomasa, sustrato, producto
373
- - Indica la ecuación matemática de cada modelo si es posible inferirla
374
-
375
- 2. **ANÁLISIS COMPARATIVO DE CALIDAD DE AJUSTE**
376
- - Compara TODOS los indicadores disponibles: R², RMSE, AIC, BIC, etc.
377
- - Crea un ranking ordenado de mejor a peor modelo
378
- - Identifica diferencias significativas entre modelos
379
- - Detecta posible sobreajuste o subajuste
380
-
381
- 3. **DETERMINACIÓN DEL MEJOR MODELO**
382
- - Selecciona el MEJOR modelo basándote en MÚLTIPLES criterios:
383
- * Mayor (más cercano a 1)
384
- * Menor RMSE/MSE
385
- * Menor AIC/BIC (si están disponibles)
386
- * Parsimonia (menos parámetros si el ajuste es similar)
387
- - Justifica NUMÉRICAMENTE por qué es el mejor
388
- - Si hay empate técnico, explica las ventajas de cada uno
389
-
390
- 4. **ANÁLISIS ESPECÍFICO POR TIPO DE VARIABLE**
391
- a) **BIOMASA (si aplica)**:
392
- - Parámetros de crecimiento (μmax, Xmax, etc.)
393
- - Tiempo de duplicación
394
- - Productividad de biomasa
395
- - Comparación numérica entre modelos
 
 
396
 
397
- b) **SUSTRATO (si aplica)**:
398
- - Constantes de afinidad (Ks, Km)
399
- - Velocidades de consumo
400
- - Rendimiento Yx/s
401
- - Eficiencia de utilización
402
 
403
- c) **PRODUCTO (si aplica)**:
404
- - Parámetros de producción (α, β)
405
- - Productividad específica
406
- - Rendimiento Yp/x
407
- - Tipo de producción (asociada/no asociada)
408
-
409
- 5. **INTERPRETACIÓN BIOLÓGICA DE PARÁMETROS**
410
- - Explica qué significa CADA parámetro biológicamente
411
- - Compara valores entre modelos
412
- - Evalúa si son realistas para el sistema
413
- - Identifica parámetros críticos del proceso
414
-
415
- 6. **CONCLUSIONES CON CONTENIDO NUMÉRICO**
416
- - Resume los hallazgos clave con NÚMEROS específicos
417
- - Proporciona rangos de confianza si están disponibles
418
- - Indica condiciones óptimas de operación
419
- - Sugiere valores de diseño para escalamiento
420
-
421
- 7. **RECOMENDACIONES PRÁCTICAS**
422
- - Qué modelo(s) usar para predicción
423
- - Limitaciones del modelo seleccionado
424
- - Experimentos adicionales recomendados
425
- - Consideraciones para implementación industrial
426
-
427
- 8. **TABLA COMPARATIVA FINAL**
428
- Crea una tabla resumen con:
429
- - Modelo | R² | RMSE | AIC/BIC | Parámetros clave | Ranking
430
-
431
- FORMATO DE RESPUESTA:
432
- - Si el nivel es "detallado": incluye TODOS los puntos con explicaciones completas
433
- - Si el nivel es "resumido": enfócate en puntos 3, 6 y 8 con valores numéricos clave
434
-
435
- Usa formato Markdown con:
436
- - Títulos y subtítulos claros
437
- - **Negritas** para valores importantes
438
- - Tablas cuando sea apropiado
439
- - Listas numeradas y con viñetas
440
-
441
- IMPORTANTE: Basa TODAS las conclusiones en los NÚMEROS específicos de los datos proporcionados.
442
  """
443
 
444
  try:
@@ -451,24 +683,26 @@ class AIAnalyzer:
451
  }]
452
  )
453
 
454
- # Análisis adicional para generar código si es necesario
455
- code_prompt = """
456
- Basándote en el análisis anterior, genera código Python para:
457
 
458
- 1. Cargar y visualizar estos resultados de ajuste
459
- 2. Crear gráficos comparativos de modelos (barras para R², RMSE)
460
- 3. Implementar el mejor modelo identificado
461
- 4. Generar predicciones con el modelo seleccionado
462
- 5. Análisis de sensibilidad de parámetros
463
 
464
- Incluye:
465
- - Imports necesarios
466
- - Funciones bien documentadas
467
- - Visualizaciones profesionales
468
- - Manejo de errores
469
- - Ejemplo de uso
470
 
471
- El código debe ser ejecutable y modular.
 
 
 
 
 
 
 
472
  """
473
 
474
  code_response = self.client.messages.create(
@@ -476,12 +710,12 @@ class AIAnalyzer:
476
  max_tokens=3000,
477
  messages=[{
478
  "role": "user",
479
- "content": f"{code_prompt}\n\nBasado en estos modelos:\n{response.content[0].text[:1000]}"
480
  }]
481
  )
482
 
483
  return {
484
- "tipo": "Análisis Comparativo de Modelos Matemáticos",
485
  "analisis_completo": response.content[0].text,
486
  "codigo_implementacion": code_response.content[0].text,
487
  "resumen_datos": {
@@ -497,8 +731,8 @@ class AIAnalyzer:
497
  except Exception as e:
498
  return {"error": str(e)}
499
 
500
- def process_files(files, claude_model: str, detail_level: str = "detallado") -> Tuple[str, str]:
501
- """Procesa múltiples archivos"""
502
  processor = FileProcessor()
503
  analyzer = AIAnalyzer(client, model_registry)
504
  results = []
@@ -514,61 +748,11 @@ def process_files(files, claude_model: str, detail_level: str = "detallado") ->
514
  with open(file.name, 'rb') as f:
515
  file_content = f.read()
516
 
517
- if file_ext == '.zip':
518
- extracted_files = processor.extract_from_zip(file_content)
519
- results.append(f"## 📦 Archivo ZIP: {file_name}")
520
- results.append(f"Contiene {len(extracted_files)} archivos\n")
521
-
522
- for name, content in extracted_files:
523
- sub_ext = Path(name).suffix.lower()
524
- results.append(f"### 📄 {name}")
525
-
526
- if sub_ext == '.pdf':
527
- text = processor.extract_text_from_pdf(content)
528
- analysis_type = analyzer.detect_analysis_type(text)
529
-
530
- if analysis_type == AnalysisType.MATHEMATICAL_MODEL:
531
- result = analyzer.analyze_mathematical_article(text, claude_model)
532
- else:
533
- result = {"tipo": "PDF no reconocido", "contenido": text[:500]}
534
-
535
- results.append(result.get("analisis_completo", json.dumps(result, indent=2, ensure_ascii=False)))
536
-
537
- elif sub_ext in ['.csv', '.xlsx', '.xls']:
538
- if sub_ext == '.csv':
539
- df = processor.read_csv(content)
540
- else:
541
- df = processor.read_excel(content)
542
-
543
- if df is not None:
544
- analysis_type = analyzer.detect_analysis_type(df)
545
-
546
- if analysis_type == AnalysisType.FITTING_RESULTS:
547
- result = analyzer.analyze_fitting_results(df, claude_model, detail_level)
548
- results.append(result.get("analisis_completo", ""))
549
- if "codigo_implementacion" in result:
550
- all_code.append(result["codigo_implementacion"])
551
- else:
552
- result = analyzer.analyze_fitting_data(df, claude_model)
553
- results.append(result.get("analisis", ""))
554
-
555
- results.append("\n---\n")
556
-
557
- elif file_ext == '.pdf':
558
- text = processor.extract_text_from_pdf(file_content)
559
- analysis_type = analyzer.detect_analysis_type(text)
560
-
561
- results.append(f"## 📄 PDF: {file_name}")
562
-
563
- if analysis_type == AnalysisType.MATHEMATICAL_MODEL:
564
- result = analyzer.analyze_mathematical_article(text, claude_model)
565
- results.append(result.get("modelos", "") + "\n" + result.get("recomendaciones", ""))
566
  else:
567
- result = {"tipo": "PDF - Contenido no identificado", "texto": text[:1000]}
568
- results.append(json.dumps(result, indent=2, ensure_ascii=False))
569
-
570
- elif file_ext in ['.csv', '.xlsx', '.xls']:
571
- results.append(f"## 📊 Análisis de Resultados: {file_name}")
572
 
573
  if file_ext == '.csv':
574
  df = processor.read_csv(file_content)
@@ -579,15 +763,16 @@ def process_files(files, claude_model: str, detail_level: str = "detallado") ->
579
  analysis_type = analyzer.detect_analysis_type(df)
580
 
581
  if analysis_type == AnalysisType.FITTING_RESULTS:
582
- result = analyzer.analyze_fitting_results(df, claude_model, detail_level)
583
- results.append("### 🎯 ANÁLISIS COMPARATIVO DE MODELOS MATEMÁTICOS")
 
 
 
 
 
584
  results.append(result.get("analisis_completo", ""))
585
  if "codigo_implementacion" in result:
586
  all_code.append(result["codigo_implementacion"])
587
- else:
588
- result = analyzer.analyze_fitting_data(df, claude_model)
589
- results.append("### 📈 ANÁLISIS DE DATOS EXPERIMENTALES")
590
- results.append(result.get("analisis", ""))
591
 
592
  results.append("\n---\n")
593
 
@@ -597,7 +782,7 @@ def process_files(files, claude_model: str, detail_level: str = "detallado") ->
597
  return analysis_text, code_text
598
 
599
  def generate_implementation_code(analysis_results: str) -> str:
600
- """Genera código de implementación basado en el análisis"""
601
  code = """
602
  import numpy as np
603
  import pandas as pd
@@ -608,14 +793,14 @@ from sklearn.metrics import r2_score, mean_squared_error
608
  import seaborn as sns
609
  from typing import Dict, List, Tuple, Optional
610
 
611
- # Configuración de visualización
612
  plt.style.use('seaborn-v0_8-darkgrid')
613
  sns.set_palette("husl")
614
 
615
  class ComparativeModelAnalyzer:
616
  \"\"\"
617
- Clase para análisis comparativo de resultados de ajuste de modelos biotecnológicos.
618
- Especializada en comparar modelos de biomasa, sustrato y producto.
619
  \"\"\"
620
 
621
  def __init__(self):
@@ -624,14 +809,14 @@ class ComparativeModelAnalyzer:
624
  self.model_rankings = {}
625
 
626
  def load_results(self, file_path: str) -> pd.DataFrame:
627
- \"\"\"Carga resultados de ajuste desde archivo CSV o Excel\"\"\"
628
  if file_path.endswith('.csv'):
629
  self.results_df = pd.read_csv(file_path)
630
  else:
631
  self.results_df = pd.read_excel(file_path)
632
 
633
- print(f"✅ Datos cargados: {len(self.results_df)} modelos")
634
- print(f"📊 Columnas disponibles: {list(self.results_df.columns)}")
635
 
636
  return self.results_df
637
 
@@ -642,40 +827,40 @@ class ComparativeModelAnalyzer:
642
  bic_col: Optional[str] = 'BIC',
643
  model_col: str = 'Model') -> pd.DataFrame:
644
  \"\"\"
645
- Analiza y compara la calidad de ajuste de todos los modelos.
646
- Crea un ranking basado en múltiples métricas.
647
  \"\"\"
648
  if self.results_df is None:
649
- raise ValueError("Primero carga los datos con load_results()")
650
 
651
- # Crear DataFrame de comparación
652
  comparison = self.results_df.copy()
653
 
654
- # Calcular puntuación compuesta
655
  scores = pd.DataFrame(index=comparison.index)
656
 
657
- # Normalizar métricas (0-1)
658
  if r2_col in comparison.columns:
659
- scores['r2_score'] = comparison[r2_col] # Ya está entre 0-1
660
 
661
  if rmse_col in comparison.columns:
662
- # Invertir y normalizar RMSE (menor es mejor)
663
  max_rmse = comparison[rmse_col].max()
664
  scores['rmse_score'] = 1 - (comparison[rmse_col] / max_rmse)
665
 
666
  if aic_col and aic_col in comparison.columns:
667
- # Invertir y normalizar AIC (menor es mejor)
668
  min_aic = comparison[aic_col].min()
669
  max_aic = comparison[aic_col].max()
670
  scores['aic_score'] = 1 - ((comparison[aic_col] - min_aic) / (max_aic - min_aic))
671
 
672
  if bic_col and bic_col in comparison.columns:
673
- # Invertir y normalizar BIC (menor es mejor)
674
  min_bic = comparison[bic_col].min()
675
  max_bic = comparison[bic_col].max()
676
  scores['bic_score'] = 1 - ((comparison[bic_col] - min_bic) / (max_bic - min_bic))
677
 
678
- # Calcular puntuación total (promedio ponderado)
679
  weights = {
680
  'r2_score': 0.4,
681
  'rmse_score': 0.3,
@@ -688,23 +873,23 @@ class ComparativeModelAnalyzer:
688
  if metric in scores.columns:
689
  scores['total_score'] += scores[metric] * weight
690
 
691
- # Añadir puntuación al DataFrame de comparación
692
  comparison['Score'] = scores['total_score']
693
  comparison['Ranking'] = comparison['Score'].rank(ascending=False).astype(int)
694
 
695
- # Ordenar por ranking
696
  comparison = comparison.sort_values('Ranking')
697
 
698
- # Identificar mejor modelo
699
  best_idx = comparison['Score'].idxmax()
700
  self.best_models['overall'] = comparison.loc[best_idx]
701
 
702
- # Imprimir tabla de comparación
703
  print("\\n" + "="*80)
704
- print("📊 TABLA COMPARATIVA DE MODELOS")
705
  print("="*80)
706
 
707
- print(f"\\n{'Rank':<6} {'Modelo':<20} {'R²':<8} {'RMSE':<10} {'AIC':<10} {'BIC':<10} {'Score':<8}")
708
  print("-"*80)
709
 
710
  for idx, row in comparison.iterrows():
@@ -727,369 +912,134 @@ class ComparativeModelAnalyzer:
727
  print(f"{'N/A':<10} ", end="")
728
  print(f"{score:<8.4f}")
729
 
730
- print("\\n🏆 MEJOR MODELO: " + comparison.iloc[0].get(model_col, 'No especificado'))
731
  print(f" - R² = {comparison.iloc[0].get(r2_col, 0):.4f}")
732
  print(f" - RMSE = {comparison.iloc[0].get(rmse_col, 0):.4f}")
733
 
734
  self.model_rankings = comparison
735
  return comparison
736
-
737
- def analyze_by_category(self, category_col: Optional[str] = None) -> Dict:
738
- \"\"\"
739
- Analiza modelos por categoría (biomasa, sustrato, producto).
740
- Si no hay columna de categoría, intenta inferir del nombre del modelo.
741
- \"\"\"
742
- if self.results_df is None:
743
- raise ValueError("Primero carga los datos")
744
-
745
- categories = {}
746
-
747
- if category_col and category_col in self.results_df.columns:
748
- # Usar columna de categoría existente
749
- for cat in self.results_df[category_col].unique():
750
- cat_data = self.results_df[self.results_df[category_col] == cat]
751
- categories[cat] = cat_data
752
- else:
753
- # Inferir categorías del nombre del modelo
754
- biomass_keywords = ['monod', 'logistic', 'gompertz', 'baranyi', 'growth']
755
- substrate_keywords = ['michaelis', 'menten', 'substrate', 'consumption']
756
- product_keywords = ['luedeking', 'piret', 'product', 'formation']
757
-
758
- for idx, row in self.results_df.iterrows():
759
- model_name = str(row.get('Model', '')).lower()
760
-
761
- if any(kw in model_name for kw in biomass_keywords):
762
- if 'biomasa' not in categories:
763
- categories['biomasa'] = []
764
- categories['biomasa'].append(row)
765
- elif any(kw in model_name for kw in substrate_keywords):
766
- if 'sustrato' not in categories:
767
- categories['sustrato'] = []
768
- categories['sustrato'].append(row)
769
- elif any(kw in model_name for kw in product_keywords):
770
- if 'producto' not in categories:
771
- categories['producto'] = []
772
- categories['producto'].append(row)
773
- else:
774
- if 'otros' not in categories:
775
- categories['otros'] = []
776
- categories['otros'].append(row)
777
-
778
- # Convertir listas a DataFrames
779
- for cat in categories:
780
- if isinstance(categories[cat], list):
781
- categories[cat] = pd.DataFrame(categories[cat])
782
-
783
- # Analizar cada categoría
784
- print("\\n" + "="*80)
785
- print("📈 ANÁLISIS POR CATEGORÍA")
786
- print("="*80)
787
-
788
- for cat, data in categories.items():
789
- if len(data) > 0:
790
- print(f"\\n### {cat.upper()}")
791
- print(f"Modelos analizados: {len(data)}")
792
-
793
- if 'R2' in data.columns:
794
- best_idx = data['R2'].idxmax()
795
- best_model = data.loc[best_idx]
796
-
797
- print(f"Mejor modelo: {best_model.get('Model', 'N/A')}")
798
- print(f" - R² = {best_model.get('R2', 0):.4f}")
799
- print(f" - RMSE = {best_model.get('RMSE', 0):.4f}")
800
-
801
- self.best_models[cat] = best_model
802
-
803
- return categories
804
-
805
- def plot_comparison(self, save_path: Optional[str] = None) -> plt.Figure:
806
- \"\"\"
807
- Genera visualizaciones comparativas de los modelos.
808
- \"\"\"
809
- if self.model_rankings is None:
810
- raise ValueError("Primero ejecuta analyze_model_quality()")
811
-
812
- fig = plt.figure(figsize=(16, 10))
813
-
814
- # Configurar grid de subplots
815
- gs = fig.add_gridspec(3, 2, height_ratios=[1, 1, 1], hspace=0.3, wspace=0.3)
816
-
817
- # 1. Gráfico de barras R²
818
- ax1 = fig.add_subplot(gs[0, 0])
819
- models = self.model_rankings.get('Model', range(len(self.model_rankings)))
820
- r2_values = self.model_rankings.get('R2', [])
821
-
822
- bars1 = ax1.bar(range(len(models)), r2_values, color='skyblue', edgecolor='navy', alpha=0.7)
823
- ax1.set_title('Comparación de R² por Modelo', fontsize=14, fontweight='bold')
824
- ax1.set_ylabel('R² (Coeficiente de Determinación)', fontsize=12)
825
- ax1.set_ylim(0, 1.05)
826
- ax1.set_xticks(range(len(models)))
827
- ax1.set_xticklabels(models, rotation=45, ha='right')
828
- ax1.grid(True, alpha=0.3)
829
-
830
- # Añadir valores en las barras
831
- for bar, val in zip(bars1, r2_values):
832
- height = bar.get_height()
833
- ax1.text(bar.get_x() + bar.get_width()/2., height + 0.01,
834
- f'{val:.3f}', ha='center', va='bottom', fontsize=9)
835
-
836
- # 2. Gráfico de barras RMSE
837
- ax2 = fig.add_subplot(gs[0, 1])
838
- rmse_values = self.model_rankings.get('RMSE', [])
839
-
840
- bars2 = ax2.bar(range(len(models)), rmse_values, color='lightcoral', edgecolor='darkred', alpha=0.7)
841
- ax2.set_title('Comparación de RMSE por Modelo', fontsize=14, fontweight='bold')
842
- ax2.set_ylabel('RMSE (Error Cuadrático Medio)', fontsize=12)
843
- ax2.set_xticks(range(len(models)))
844
- ax2.set_xticklabels(models, rotation=45, ha='right')
845
- ax2.grid(True, alpha=0.3)
846
-
847
- # 3. Gráfico de puntuación total
848
- ax3 = fig.add_subplot(gs[1, :])
849
- scores = self.model_rankings.get('Score', [])
850
- rankings = self.model_rankings.get('Ranking', [])
851
-
852
- # Crear gradiente de colores basado en ranking
853
- colors = plt.cm.RdYlGn(1 - (rankings - 1) / (len(rankings) - 1))
854
-
855
- bars3 = ax3.bar(range(len(models)), scores, color=colors, edgecolor='black', alpha=0.8)
856
- ax3.set_title('Puntuación Total Compuesta (Mayor es Mejor)', fontsize=16, fontweight='bold')
857
- ax3.set_ylabel('Puntuación Total', fontsize=12)
858
- ax3.set_xticks(range(len(models)))
859
- ax3.set_xticklabels(models, rotation=45, ha='right')
860
- ax3.grid(True, alpha=0.3)
861
-
862
- # Marcar el mejor modelo
863
- best_idx = scores.argmax()
864
- bars3[best_idx].set_linewidth(3)
865
- bars3[best_idx].set_edgecolor('gold')
866
-
867
- # 4. Tabla de métricas
868
- ax4 = fig.add_subplot(gs[2, :])
869
- ax4.axis('tight')
870
- ax4.axis('off')
871
-
872
- # Preparar datos para la tabla
873
- table_data = []
874
- for idx, row in self.model_rankings.head(5).iterrows():
875
- table_data.append([
876
- row.get('Ranking', ''),
877
- row.get('Model', '')[:20],
878
- f"{row.get('R2', 0):.4f}",
879
- f"{row.get('RMSE', 0):.4f}",
880
- f"{row.get('AIC', 'N/A'):.2f}" if isinstance(row.get('AIC'), (int, float)) else 'N/A',
881
- f"{row.get('BIC', 'N/A'):.2f}" if isinstance(row.get('BIC'), (int, float)) else 'N/A',
882
- f"{row.get('Score', 0):.4f}"
883
- ])
884
-
885
- table = ax4.table(cellText=table_data,
886
- colLabels=['Rank', 'Modelo', 'R²', 'RMSE', 'AIC', 'BIC', 'Score'],
887
- cellLoc='center',
888
- loc='center',
889
- colWidths=[0.08, 0.25, 0.12, 0.12, 0.12, 0.12, 0.12])
890
-
891
- table.auto_set_font_size(False)
892
- table.set_fontsize(10)
893
- table.scale(1.2, 1.5)
894
-
895
- # Colorear la primera fila (mejor modelo)
896
- for j in range(len(table_data[0])):
897
- table[(1, j)].set_facecolor('#90EE90')
898
-
899
- ax4.set_title('Top 5 Modelos - Tabla Resumen', fontsize=14, fontweight='bold', pad=20)
900
-
901
- plt.suptitle('Análisis Comparativo de Modelos Biotecnológicos', fontsize=18, fontweight='bold')
902
-
903
- if save_path:
904
- plt.savefig(save_path, dpi=300, bbox_inches='tight')
905
- print(f"\\n💾 Gráfico guardado en: {save_path}")
906
-
907
- return fig
908
-
909
- def generate_report(self, output_file: str = 'informe_comparativo.txt'):
910
- \"\"\"
911
- Genera un informe detallado con todas las conclusiones numéricas.
912
- \"\"\"
913
- if self.model_rankings is None:
914
- raise ValueError("Primero ejecuta analyze_model_quality()")
915
-
916
- report = []
917
- report.append("="*80)
918
- report.append("INFORME DE ANÁLISIS COMPARATIVO DE MODELOS MATEMÁTICOS")
919
- report.append("="*80)
920
- report.append(f"\\nFecha: {pd.Timestamp.now().strftime('%Y-%m-%d %H:%M:%S')}")
921
- report.append(f"Número de modelos analizados: {len(self.results_df)}")
922
-
923
- # Resumen ejecutivo
924
- report.append("\\n" + "-"*40)
925
- report.append("RESUMEN EJECUTIVO")
926
- report.append("-"*40)
927
-
928
- best_model = self.model_rankings.iloc[0]
929
- report.append(f"\\nMEJOR MODELO GLOBAL: {best_model.get('Model', 'N/A')}")
930
- report.append(f" - R² = {best_model.get('R2', 0):.4f} (explica {best_model.get('R2', 0)*100:.1f}% de la variabilidad)")
931
- report.append(f" - RMSE = {best_model.get('RMSE', 0):.4f}")
932
-
933
- if 'AIC' in best_model:
934
- report.append(f" - AIC = {best_model.get('AIC'):.2f}")
935
- if 'BIC' in best_model:
936
- report.append(f" - BIC = {best_model.get('BIC'):.2f}")
937
-
938
- # Análisis detallado
939
- report.append("\\n" + "-"*40)
940
- report.append("ANÁLISIS DETALLADO POR MODELO")
941
- report.append("-"*40)
942
-
943
- for idx, row in self.model_rankings.iterrows():
944
- report.append(f"\\n{row.get('Ranking')}. {row.get('Model', 'N/A')}")
945
- report.append(f" Métricas de ajuste:")
946
- report.append(f" - R² = {row.get('R2', 0):.4f}")
947
- report.append(f" - RMSE = {row.get('RMSE', 0):.4f}")
948
-
949
- # Interpretación
950
- r2_val = row.get('R2', 0)
951
- if r2_val > 0.95:
952
- quality = "EXCELENTE"
953
- elif r2_val > 0.90:
954
- quality = "MUY BUENO"
955
- elif r2_val > 0.80:
956
- quality = "BUENO"
957
- elif r2_val > 0.70:
958
- quality = "ACEPTABLE"
959
- else:
960
- quality = "POBRE"
961
-
962
- report.append(f" - Calidad del ajuste: {quality}")
963
-
964
- # Análisis por categorías si está disponible
965
- if hasattr(self, 'best_models') and len(self.best_models) > 1:
966
- report.append("\\n" + "-"*40)
967
- report.append("MEJORES MODELOS POR CATEGORÍA")
968
- report.append("-"*40)
969
-
970
- for cat, model in self.best_models.items():
971
- if cat != 'overall':
972
- report.append(f"\\n{cat.upper()}:")
973
- report.append(f" Mejor modelo: {model.get('Model', 'N/A')}")
974
- report.append(f" - R² = {model.get('R2', 0):.4f}")
975
- report.append(f" - RMSE = {model.get('RMSE', 0):.4f}")
976
-
977
- # Recomendaciones
978
- report.append("\\n" + "-"*40)
979
- report.append("RECOMENDACIONES")
980
- report.append("-"*40)
981
-
982
- report.append(f"\\n1. Para predicciones generales, usar: {best_model.get('Model', 'N/A')}")
983
- report.append("2. Validar con conjunto de datos independiente")
984
- report.append("3. Considerar análisis de residuos")
985
- report.append("4. Evaluar estabilidad de parámetros")
986
-
987
- # Guardar informe
988
- with open(output_file, 'w', encoding='utf-8') as f:
989
- f.write("\\n".join(report))
990
-
991
- print(f"\\n📄 Informe guardado en: {output_file}")
992
-
993
- return "\\n".join(report)
994
-
995
- # Implementación de modelos específicos
996
- class BiotechModels:
997
- \"\"\"Biblioteca de modelos biotecnológicos comunes\"\"\"
998
-
999
- @staticmethod
1000
- def monod(S, mu_max, Ks):
1001
- \"\"\"Modelo de Monod para crecimiento\"\"\"
1002
- return mu_max * S / (Ks + S)
1003
-
1004
- @staticmethod
1005
- def logistic(t, K, r, t0):
1006
- \"\"\"Modelo logístico\"\"\"
1007
- return K / (1 + np.exp(-r * (t - t0)))
1008
-
1009
- @staticmethod
1010
- def gompertz(t, A, mu, lambda_param):
1011
- \"\"\"Modelo de Gompertz\"\"\"
1012
- return A * np.exp(-np.exp(mu * np.e / A * (lambda_param - t) + 1))
1013
-
1014
- @staticmethod
1015
- def michaelis_menten(S, Vmax, Km):
1016
- \"\"\"Modelo de Michaelis-Menten\"\"\"
1017
- return Vmax * S / (Km + S)
1018
-
1019
- @staticmethod
1020
- def luedeking_piret_integrated(t, X0, mu_max, alpha, beta):
1021
- \"\"\"Modelo de Luedeking-Piret integrado\"\"\"
1022
- X = X0 * np.exp(mu_max * t)
1023
- P = alpha * (X - X0) + beta * X0 * (np.exp(mu_max * t) - 1) / mu_max
1024
- return P
1025
 
1026
- # Ejemplo de uso
1027
  if __name__ == "__main__":
1028
- print("🧬 Sistema de Análisis Comparativo de Modelos Biotecnológicos")
1029
  print("="*60)
1030
 
1031
- # Crear analizador
1032
  analyzer = ComparativeModelAnalyzer()
1033
 
1034
- # Instrucciones
1035
- print("\\n📋 INSTRUCCIONES DE USO:")
1036
- print("1. analyzer.load_results('tu_archivo.csv')")
1037
  print("2. analyzer.analyze_model_quality()")
1038
- print("3. analyzer.analyze_by_category()")
1039
- print("4. analyzer.plot_comparison()")
1040
- print("5. analyzer.generate_report()")
1041
 
1042
- print("\\n✨ ¡Sistema listo para análisis!")
1043
  """
1044
 
1045
  return code
1046
 
1047
- # Interfaz Gradio optimizada
1048
- def create_interface():
1049
- with gr.Blocks(
1050
- title="Analizador Comparativo de Modelos Biotecnológicos",
1051
- theme=gr.themes.Soft(),
1052
- css="""
1053
- .gradio-container {
1054
- font-family: 'Arial', sans-serif;
1055
- }
1056
- .highlight-results {
1057
- background-color: #f0f8ff;
1058
- padding: 15px;
1059
- border-radius: 10px;
1060
- border-left: 5px solid #4CAF50;
 
 
 
 
1061
  }
1062
- .comparison-table {
1063
- background-color: #f9f9f9;
1064
- padding: 10px;
1065
- border-radius: 8px;
1066
- font-family: monospace;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1067
  }
1068
- """
1069
- ) as demo:
1070
-
1071
- gr.Markdown("""
1072
- # 🧬 Analizador Comparativo de Modelos Biotecnológicos
1073
-
1074
- ### 🎯 Especializado en:
1075
- - **Análisis comparativo** de resultados de ajuste de modelos matemáticos
1076
- - **Determinación del mejor modelo** con justificación numérica
1077
- - **Análisis específico** para biomasa, sustrato y producto
1078
- - **Conclusiones numéricas** detalladas y ordenadas
1079
- - **Generación de código** para implementación y análisis
1080
-
1081
- ### 📊 Métricas analizadas:
1082
- - (Coeficiente de determinación)
1083
- - RMSE (Error cuadrático medio)
1084
- - AIC/BIC (Criterios de información)
1085
- - Parámetros específicos del modelo
1086
- - Intervalos de confianza
1087
- """)
 
 
 
 
 
 
 
 
 
 
 
 
 
1088
 
1089
  with gr.Row():
1090
  with gr.Column(scale=1):
1091
  files_input = gr.File(
1092
- label="���� Subir resultados de ajuste (CSV/Excel)",
1093
  file_count="multiple",
1094
  file_types=[".csv", ".xlsx", ".xls", ".pdf", ".zip"],
1095
  type="filepath"
@@ -1098,138 +1048,140 @@ def create_interface():
1098
  model_selector = gr.Dropdown(
1099
  choices=list(CLAUDE_MODELS.keys()),
1100
  value="claude-3-5-sonnet-20241022",
1101
- label="🤖 Modelo Claude",
1102
- info="Selecciona el modelo de IA"
1103
  )
1104
 
1105
  detail_level = gr.Radio(
1106
- choices=["detallado", "resumido"],
1107
- value="detallado",
1108
- label="📋 Nivel de detalle del análisis",
1109
- info="Detallado: análisis completo | Resumido: puntos clave"
 
 
1110
  )
1111
 
1112
  analyze_btn = gr.Button(
1113
- "🚀 Analizar y Comparar Modelos",
1114
  variant="primary",
1115
  size="lg"
1116
  )
1117
 
1118
- # Información del modelo
1119
- model_info = gr.Markdown()
1120
 
1121
- def update_model_info(model):
1122
- info = CLAUDE_MODELS[model]
1123
- return f"""
1124
- **{info['name']}**
1125
-
1126
- {info['description']}
1127
-
1128
- *Mejor para: {info['best_for']}*
1129
- """
1130
 
1131
- model_selector.change(
1132
- update_model_info,
1133
- inputs=[model_selector],
1134
- outputs=[model_info]
 
 
 
 
 
 
 
 
 
 
1135
  )
1136
 
1137
  with gr.Column(scale=2):
1138
  analysis_output = gr.Markdown(
1139
- label="📊 Análisis Comparativo",
1140
- elem_classes=["highlight-results"]
1141
  )
1142
 
1143
  code_output = gr.Code(
1144
- label="💻 Código de Implementación",
1145
  language="python",
1146
  interactive=True,
1147
  lines=20
1148
  )
1149
 
1150
- # Guía de formato de datos
1151
- with gr.Accordion("📋 Formato de datos esperado", open=False):
 
 
 
 
1152
  gr.Markdown("""
1153
- ### Estructura ideal del CSV/Excel:
1154
 
1155
  | Model | R2 | RMSE | AIC | BIC | mu_max | Ks | Parameters |
1156
  |-------|-----|------|-----|-----|--------|-------|------------|
1157
  | Monod | 0.985 | 0.023 | -45.2 | -42.1 | 0.45 | 2.1 | {...} |
1158
  | Logistic | 0.976 | 0.031 | -42.1 | -39.5 | 0.42 | - | {...} |
1159
  | Gompertz | 0.992 | 0.018 | -48.5 | -45.2 | 0.48 | - | {...} |
1160
-
1161
- **Columnas mínimas requeridas:**
1162
- - `Model` o `Modelo`: Nombre del modelo
1163
- - `R2` o `R_squared`: Coeficiente de determinación
1164
- - `RMSE` o `MSE`: Error de ajuste
1165
-
1166
- **Columnas opcionales pero recomendadas:**
1167
- - `AIC`, `BIC`: Criterios de información
1168
- - Parámetros específicos del modelo
1169
- - `SE` o `Standard_Error`: Errores estándar
1170
- - `CI_lower`, `CI_upper`: Intervalos de confianza
1171
  """)
1172
 
1173
- # Ejemplos
1174
- gr.Examples(
1175
  examples=[
1176
- [["examples/biomass_models_comparison.csv"], "claude-3-5-sonnet-20241022", "detallado"],
1177
- [["examples/substrate_kinetics_results.xlsx"], "claude-3-5-sonnet-20241022", "resumido"],
1178
- [["examples/product_formation_fits.csv"], "claude-3-opus-20240229", "detallado"]
1179
  ],
1180
  inputs=[files_input, model_selector, detail_level],
1181
- label="📚 Ejemplos de análisis"
1182
  )
1183
 
1184
  # Eventos
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1185
  analyze_btn.click(
1186
- fn=lambda files, model, detail: process_files(files, model, detail) if files else (
1187
- "Por favor sube archivos con resultados de ajuste para analizar",
1188
- ""
1189
- ),
1190
- inputs=[files_input, model_selector, detail_level],
1191
  outputs=[analysis_output, code_output]
1192
  )
1193
 
1194
- # Footer con información adicional
1195
- gr.Markdown("""
1196
- ---
1197
- ### 🔍 Qué analiza específicamente:
1198
-
1199
- 1. **Comparación de modelos**: Ranking basado en múltiples métricas
1200
- 2. **Mejor modelo**: Identificación y justificación numérica
1201
- 3. **Análisis por tipo**:
1202
- - **Biomasa**: μmax, Xmax, tiempo de duplicación
1203
- - **Sustrato**: Ks, Km, velocidades de consumo, Yx/s
1204
- - **Producto**: α, β, productividad, Yp/x
1205
- 4. **Significado biológico**: Interpretación de parámetros
1206
- 5. **Conclusiones numéricas**: Valores óptimos, rangos de operación
1207
- 6. **Código Python**: Implementación lista para usar
1208
-
1209
- ### 💡 Tips para mejores resultados:
1210
- - Incluye todas las métricas de ajuste disponibles
1211
- - Usa nombres descriptivos para los modelos
1212
- - Incluye errores estándar si están disponibles
1213
- - Añade información de condiciones experimentales si es relevante
1214
- """)
1215
-
1216
- # Cargar info inicial del modelo
1217
- demo.load(
1218
- fn=lambda: update_model_info("claude-3-5-sonnet-20241022"),
1219
- outputs=[model_info]
1220
  )
1221
 
1222
  return demo
1223
 
1224
- # Función principal para HuggingFace Spaces
1225
  def main():
1226
  if not os.getenv("ANTHROPIC_API_KEY"):
1227
- print("⚠️ Configura ANTHROPIC_API_KEY en los secretos de HuggingFace Space")
1228
  return gr.Interface(
1229
- fn=lambda x: "Por favor configura ANTHROPIC_API_KEY en los secretos del Space",
1230
  inputs=gr.Textbox(),
1231
  outputs=gr.Textbox(),
1232
- title="Error de Configuración"
1233
  )
1234
 
1235
  return create_interface()
 
8
  import json
9
  import zipfile
10
  import tempfile
11
+ from typing import Dict, List, Tuple, Union, Optional
12
  import re
13
  from pathlib import Path
14
  import openpyxl
15
  from dataclasses import dataclass
16
  from enum import Enum
17
+ from docx import Document
18
+ from docx.shared import Inches, Pt, RGBColor
19
+ from docx.enum.text import WD_ALIGN_PARAGRAPH
20
+ from reportlab.lib import colors
21
+ from reportlab.lib.pagesizes import letter, A4
22
+ from reportlab.platypus import SimpleDocTemplate, Table, TableStyle, Paragraph, Spacer, PageBreak
23
+ from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
24
+ from reportlab.lib.units import inch
25
+ from reportlab.pdfbase import pdfmetrics
26
+ from reportlab.pdfbase.ttfonts import TTFont
27
+ import matplotlib.pyplot as plt
28
+ from datetime import datetime
29
 
30
  # Configuración para HuggingFace
31
  os.environ['GRADIO_ANALYTICS_ENABLED'] = 'False'
 
33
  # Inicializar cliente Anthropic
34
  client = anthropic.Anthropic()
35
 
36
+ # Sistema de traducción
37
+ TRANSLATIONS = {
38
+ 'en': {
39
+ 'title': '🧬 Comparative Analyzer of Biotechnological Models',
40
+ 'subtitle': 'Specialized in comparative analysis of mathematical model fitting results',
41
+ 'upload_files': '📁 Upload fitting results (CSV/Excel)',
42
+ 'select_model': '🤖 Claude Model',
43
+ 'select_language': '🌐 Language',
44
+ 'select_theme': '🎨 Theme',
45
+ 'detail_level': '📋 Analysis detail level',
46
+ 'detailed': 'Detailed',
47
+ 'summarized': 'Summarized',
48
+ 'analyze_button': '🚀 Analyze and Compare Models',
49
+ 'export_format': '📄 Export format',
50
+ 'export_button': '💾 Export Report',
51
+ 'comparative_analysis': '📊 Comparative Analysis',
52
+ 'implementation_code': '💻 Implementation Code',
53
+ 'data_format': '📋 Expected data format',
54
+ 'examples': '📚 Analysis examples',
55
+ 'light': 'Light',
56
+ 'dark': 'Dark',
57
+ 'best_for': 'Best for',
58
+ 'loading': 'Loading...',
59
+ 'error_no_api': 'Please configure ANTHROPIC_API_KEY in HuggingFace Space secrets',
60
+ 'error_no_files': 'Please upload fitting result files to analyze',
61
+ 'report_exported': 'Report exported successfully as',
62
+ 'specialized_in': '🎯 Specialized in:',
63
+ 'metrics_analyzed': '📊 Analyzed metrics:',
64
+ 'what_analyzes': '🔍 What it specifically analyzes:',
65
+ 'tips': '💡 Tips for better results:'
66
+ },
67
+ 'es': {
68
+ 'title': '🧬 Analizador Comparativo de Modelos Biotecnológicos',
69
+ 'subtitle': 'Especializado en análisis comparativo de resultados de ajuste de modelos matemáticos',
70
+ 'upload_files': '📁 Subir resultados de ajuste (CSV/Excel)',
71
+ 'select_model': '🤖 Modelo Claude',
72
+ 'select_language': '🌐 Idioma',
73
+ 'select_theme': '🎨 Tema',
74
+ 'detail_level': '📋 Nivel de detalle del análisis',
75
+ 'detailed': 'Detallado',
76
+ 'summarized': 'Resumido',
77
+ 'analyze_button': '🚀 Analizar y Comparar Modelos',
78
+ 'export_format': '📄 Formato de exportación',
79
+ 'export_button': '💾 Exportar Reporte',
80
+ 'comparative_analysis': '📊 Análisis Comparativo',
81
+ 'implementation_code': '💻 Código de Implementación',
82
+ 'data_format': '📋 Formato de datos esperado',
83
+ 'examples': '📚 Ejemplos de análisis',
84
+ 'light': 'Claro',
85
+ 'dark': 'Oscuro',
86
+ 'best_for': 'Mejor para',
87
+ 'loading': 'Cargando...',
88
+ 'error_no_api': 'Por favor configura ANTHROPIC_API_KEY en los secretos del Space',
89
+ 'error_no_files': 'Por favor sube archivos con resultados de ajuste para analizar',
90
+ 'report_exported': 'Reporte exportado exitosamente como',
91
+ 'specialized_in': '🎯 Especializado en:',
92
+ 'metrics_analyzed': '📊 Métricas analizadas:',
93
+ 'what_analyzes': '🔍 Qué analiza específicamente:',
94
+ 'tips': '💡 Tips para mejores resultados:'
95
+ },
96
+ 'fr': {
97
+ 'title': '🧬 Analyseur Comparatif de Modèles Biotechnologiques',
98
+ 'subtitle': 'Spécialisé dans l\'analyse comparative des résultats d\'ajustement',
99
+ 'upload_files': '📁 Télécharger les résultats (CSV/Excel)',
100
+ 'select_model': '🤖 Modèle Claude',
101
+ 'select_language': '🌐 Langue',
102
+ 'select_theme': '🎨 Thème',
103
+ 'detail_level': '📋 Niveau de détail',
104
+ 'detailed': 'Détaillé',
105
+ 'summarized': 'Résumé',
106
+ 'analyze_button': '🚀 Analyser et Comparer',
107
+ 'export_format': '📄 Format d\'export',
108
+ 'export_button': '💾 Exporter le Rapport',
109
+ 'comparative_analysis': '📊 Analyse Comparative',
110
+ 'implementation_code': '💻 Code d\'Implémentation',
111
+ 'data_format': '📋 Format de données attendu',
112
+ 'examples': '📚 Exemples d\'analyse',
113
+ 'light': 'Clair',
114
+ 'dark': 'Sombre',
115
+ 'best_for': 'Meilleur pour',
116
+ 'loading': 'Chargement...',
117
+ 'error_no_api': 'Veuillez configurer ANTHROPIC_API_KEY',
118
+ 'error_no_files': 'Veuillez télécharger des fichiers à analyser',
119
+ 'report_exported': 'Rapport exporté avec succès comme',
120
+ 'specialized_in': '🎯 Spécialisé dans:',
121
+ 'metrics_analyzed': '📊 Métriques analysées:',
122
+ 'what_analyzes': '🔍 Ce qu\'il analyse spécifiquement:',
123
+ 'tips': '💡 Conseils pour de meilleurs résultats:'
124
+ },
125
+ 'de': {
126
+ 'title': '🧬 Vergleichender Analysator für Biotechnologische Modelle',
127
+ 'subtitle': 'Spezialisiert auf vergleichende Analyse von Modellanpassungsergebnissen',
128
+ 'upload_files': '📁 Ergebnisse hochladen (CSV/Excel)',
129
+ 'select_model': '🤖 Claude Modell',
130
+ 'select_language': '🌐 Sprache',
131
+ 'select_theme': '🎨 Thema',
132
+ 'detail_level': '📋 Detailgrad der Analyse',
133
+ 'detailed': 'Detailliert',
134
+ 'summarized': 'Zusammengefasst',
135
+ 'analyze_button': '🚀 Analysieren und Vergleichen',
136
+ 'export_format': '📄 Exportformat',
137
+ 'export_button': '💾 Bericht Exportieren',
138
+ 'comparative_analysis': '📊 Vergleichende Analyse',
139
+ 'implementation_code': '💻 Implementierungscode',
140
+ 'data_format': '📋 Erwartetes Datenformat',
141
+ 'examples': '📚 Analysebeispiele',
142
+ 'light': 'Hell',
143
+ 'dark': 'Dunkel',
144
+ 'best_for': 'Am besten für',
145
+ 'loading': 'Laden...',
146
+ 'error_no_api': 'Bitte konfigurieren Sie ANTHROPIC_API_KEY',
147
+ 'error_no_files': 'Bitte laden Sie Dateien zur Analyse hoch',
148
+ 'report_exported': 'Bericht erfolgreich exportiert als',
149
+ 'specialized_in': '🎯 Spezialisiert auf:',
150
+ 'metrics_analyzed': '📊 Analysierte Metriken:',
151
+ 'what_analyzes': '🔍 Was spezifisch analysiert wird:',
152
+ 'tips': '💡 Tipps für bessere Ergebnisse:'
153
+ },
154
+ 'pt': {
155
+ 'title': '🧬 Analisador Comparativo de Modelos Biotecnológicos',
156
+ 'subtitle': 'Especializado em análise comparativa de resultados de ajuste',
157
+ 'upload_files': '📁 Carregar resultados (CSV/Excel)',
158
+ 'select_model': '🤖 Modelo Claude',
159
+ 'select_language': '🌐 Idioma',
160
+ 'select_theme': '🎨 Tema',
161
+ 'detail_level': '📋 Nível de detalhe',
162
+ 'detailed': 'Detalhado',
163
+ 'summarized': 'Resumido',
164
+ 'analyze_button': '🚀 Analisar e Comparar',
165
+ 'export_format': '📄 Formato de exportação',
166
+ 'export_button': '💾 Exportar Relatório',
167
+ 'comparative_analysis': '📊 Análise Comparativa',
168
+ 'implementation_code': '💻 Código de Implementação',
169
+ 'data_format': '📋 Formato de dados esperado',
170
+ 'examples': '📚 Exemplos de análise',
171
+ 'light': 'Claro',
172
+ 'dark': 'Escuro',
173
+ 'best_for': 'Melhor para',
174
+ 'loading': 'Carregando...',
175
+ 'error_no_api': 'Por favor configure ANTHROPIC_API_KEY',
176
+ 'error_no_files': 'Por favor carregue arquivos para analisar',
177
+ 'report_exported': 'Relatório exportado com sucesso como',
178
+ 'specialized_in': '🎯 Especializado em:',
179
+ 'metrics_analyzed': '📊 Métricas analisadas:',
180
+ 'what_analyzes': '🔍 O que analisa especificamente:',
181
+ 'tips': '💡 Dicas para melhores resultados:'
182
+ }
183
+ }
184
+
185
+ # Temas disponibles
186
+ THEMES = {
187
+ 'light': gr.themes.Soft(),
188
+ 'dark': gr.themes.Base(
189
+ primary_hue="blue",
190
+ secondary_hue="gray",
191
+ neutral_hue="gray",
192
+ font=["Arial", "sans-serif"]
193
+ ).set(
194
+ body_background_fill="dark",
195
+ body_background_fill_dark="*neutral_950",
196
+ button_primary_background_fill="*primary_600",
197
+ button_primary_background_fill_hover="*primary_500",
198
+ button_primary_text_color="white",
199
+ block_background_fill="*neutral_800",
200
+ block_border_color="*neutral_700",
201
+ block_label_text_color="*neutral_200",
202
+ block_title_text_color="*neutral_100",
203
+ checkbox_background_color="*neutral_700",
204
+ checkbox_background_color_selected="*primary_600",
205
+ input_background_fill="*neutral_700",
206
+ input_border_color="*neutral_600",
207
+ input_placeholder_color="*neutral_400"
208
+ )
209
+ }
210
+
211
  # Enum para tipos de análisis
212
  class AnalysisType(Enum):
213
  MATHEMATICAL_MODEL = "mathematical_model"
 
278
  category="crecimiento_biomasa",
279
  biological_meaning="Incluye fase de adaptación (lag) seguida de crecimiento exponencial y estacionario"
280
  ))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
281
 
282
  # Instancia global del registro
283
  model_registry = ModelRegistry()
 
286
  CLAUDE_MODELS = {
287
  "claude-3-5-sonnet-20241022": {
288
  "name": "Claude 3.5 Sonnet",
289
+ "description": "Fast and efficient model",
290
  "max_tokens": 4000,
291
+ "best_for": "General analysis"
292
  },
293
  "claude-3-opus-20240229": {
294
  "name": "Claude 3 Opus",
295
+ "description": "Most powerful model",
296
  "max_tokens": 4000,
297
+ "best_for": "Complex analysis"
298
  },
299
  "claude-3-haiku-20240307": {
300
  "name": "Claude 3 Haiku",
301
+ "description": "Fastest model",
302
  "max_tokens": 4000,
303
+ "best_for": "Quick analysis"
304
  }
305
  }
306
 
 
317
  text += page.extract_text() + "\n"
318
  return text
319
  except Exception as e:
320
+ return f"Error reading PDF: {str(e)}"
321
 
322
  @staticmethod
323
  def read_csv(csv_file) -> pd.DataFrame:
 
346
  file_data = zip_ref.read(file_name)
347
  files.append((file_name, file_data))
348
  except Exception as e:
349
+ print(f"Error processing ZIP: {e}")
350
  return files
351
 
352
+ class ReportExporter:
353
+ """Clase para exportar reportes a diferentes formatos"""
354
+
355
+ @staticmethod
356
+ def export_to_docx(content: str, filename: str, language: str = 'en') -> str:
357
+ """Exporta el contenido a un archivo DOCX"""
358
+ doc = Document()
359
+
360
+ # Configurar estilos
361
+ title_style = doc.styles['Title']
362
+ title_style.font.size = Pt(24)
363
+ title_style.font.bold = True
364
+
365
+ heading_style = doc.styles['Heading 1']
366
+ heading_style.font.size = Pt(18)
367
+ heading_style.font.bold = True
368
+
369
+ # Título
370
+ title_text = {
371
+ 'en': 'Comparative Analysis Report - Biotechnological Models',
372
+ 'es': 'Informe de Análisis Comparativo - Modelos Biotecnológicos',
373
+ 'fr': 'Rapport d\'Analyse Comparative - Modèles Biotechnologiques',
374
+ 'de': 'Vergleichsanalysebericht - Biotechnologische Modelle',
375
+ 'pt': 'Relatório de Análise Comparativa - Modelos Biotecnológicos'
376
+ }
377
+
378
+ doc.add_heading(title_text.get(language, title_text['en']), 0)
379
+
380
+ # Fecha
381
+ date_text = {
382
+ 'en': 'Generated on',
383
+ 'es': 'Generado el',
384
+ 'fr': 'Généré le',
385
+ 'de': 'Erstellt am',
386
+ 'pt': 'Gerado em'
387
+ }
388
+ doc.add_paragraph(f"{date_text.get(language, date_text['en'])}: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
389
+ doc.add_paragraph()
390
+
391
+ # Procesar contenido
392
+ lines = content.split('\n')
393
+ current_paragraph = None
394
+
395
+ for line in lines:
396
+ line = line.strip()
397
+
398
+ if line.startswith('###'):
399
+ doc.add_heading(line.replace('###', '').strip(), level=2)
400
+ elif line.startswith('##'):
401
+ doc.add_heading(line.replace('##', '').strip(), level=1)
402
+ elif line.startswith('#'):
403
+ doc.add_heading(line.replace('#', '').strip(), level=0)
404
+ elif line.startswith('**') and line.endswith('**'):
405
+ # Texto en negrita
406
+ p = doc.add_paragraph()
407
+ run = p.add_run(line.replace('**', ''))
408
+ run.bold = True
409
+ elif line.startswith('- ') or line.startswith('* '):
410
+ # Lista
411
+ doc.add_paragraph(line[2:], style='List Bullet')
412
+ elif line.startswith(tuple('0123456789')):
413
+ # Lista numerada
414
+ doc.add_paragraph(line, style='List Number')
415
+ elif line == '---' or line.startswith('==='):
416
+ # Separador
417
+ doc.add_paragraph('_' * 50)
418
+ elif line:
419
+ # Párrafo normal
420
+ doc.add_paragraph(line)
421
+
422
+ # Guardar documento
423
+ doc.save(filename)
424
+ return filename
425
+
426
+ @staticmethod
427
+ def export_to_pdf(content: str, filename: str, language: str = 'en') -> str:
428
+ """Exporta el contenido a un archivo PDF"""
429
+ # Crear documento PDF
430
+ doc = SimpleDocTemplate(filename, pagesize=letter)
431
+ story = []
432
+ styles = getSampleStyleSheet()
433
+
434
+ # Estilos personalizados
435
+ title_style = ParagraphStyle(
436
+ 'CustomTitle',
437
+ parent=styles['Title'],
438
+ fontSize=24,
439
+ textColor=colors.HexColor('#1f4788'),
440
+ spaceAfter=30
441
+ )
442
+
443
+ heading_style = ParagraphStyle(
444
+ 'CustomHeading',
445
+ parent=styles['Heading1'],
446
+ fontSize=16,
447
+ textColor=colors.HexColor('#2e5090'),
448
+ spaceAfter=12
449
+ )
450
+
451
+ # Título
452
+ title_text = {
453
+ 'en': 'Comparative Analysis Report - Biotechnological Models',
454
+ 'es': 'Informe de Análisis Comparativo - Modelos Biotecnológicos',
455
+ 'fr': 'Rapport d\'Analyse Comparative - Modèles Biotechnologiques',
456
+ 'de': 'Vergleichsanalysebericht - Biotechnologische Modelle',
457
+ 'pt': 'Relatório de Análise Comparativa - Modelos Biotecnológicos'
458
+ }
459
+
460
+ story.append(Paragraph(title_text.get(language, title_text['en']), title_style))
461
+
462
+ # Fecha
463
+ date_text = {
464
+ 'en': 'Generated on',
465
+ 'es': 'Generado el',
466
+ 'fr': 'Généré le',
467
+ 'de': 'Erstellt am',
468
+ 'pt': 'Gerado em'
469
+ }
470
+ story.append(Paragraph(f"{date_text.get(language, date_text['en'])}: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}", styles['Normal']))
471
+ story.append(Spacer(1, 0.5*inch))
472
+
473
+ # Procesar contenido
474
+ lines = content.split('\n')
475
+
476
+ for line in lines:
477
+ line = line.strip()
478
+
479
+ if not line:
480
+ story.append(Spacer(1, 0.2*inch))
481
+ elif line.startswith('###'):
482
+ story.append(Paragraph(line.replace('###', '').strip(), styles['Heading3']))
483
+ elif line.startswith('##'):
484
+ story.append(Paragraph(line.replace('##', '').strip(), styles['Heading2']))
485
+ elif line.startswith('#'):
486
+ story.append(Paragraph(line.replace('#', '').strip(), heading_style))
487
+ elif line.startswith('**') and line.endswith('**'):
488
+ text = line.replace('**', '')
489
+ story.append(Paragraph(f"<b>{text}</b>", styles['Normal']))
490
+ elif line.startswith('- ') or line.startswith('* '):
491
+ story.append(Paragraph(f"• {line[2:]}", styles['Normal']))
492
+ elif line == '---' or line.startswith('==='):
493
+ story.append(Spacer(1, 0.3*inch))
494
+ story.append(Paragraph("_" * 70, styles['Normal']))
495
+ story.append(Spacer(1, 0.3*inch))
496
+ else:
497
+ # Limpiar caracteres especiales para PDF
498
+ clean_line = line.replace('📊', '[GRAPH]').replace('🎯', '[TARGET]').replace('🔍', '[SEARCH]').replace('💡', '[TIP]')
499
+ story.append(Paragraph(clean_line, styles['Normal']))
500
+
501
+ # Construir PDF
502
+ doc.build(story)
503
+ return filename
504
+
505
  class AIAnalyzer:
506
  """Clase para análisis con IA"""
507
 
 
529
  return AnalysisType.DATA_FITTING
530
 
531
  prompt = """
532
+ Analyze this content and determine if it is:
533
+ 1. A scientific article describing biotechnological mathematical models
534
+ 2. Experimental data for parameter fitting
535
+ 3. Model fitting results (with parameters, R², RMSE, etc.)
536
 
537
+ Reply only with: "MODEL", "DATA" or "RESULTS"
538
  """
539
 
540
  try:
 
545
  )
546
 
547
  result = response.content[0].text.strip().upper()
548
+ if "MODEL" in result:
549
  return AnalysisType.MATHEMATICAL_MODEL
550
+ elif "RESULTS" in result:
551
  return AnalysisType.FITTING_RESULTS
552
+ elif "DATA" in result:
553
  return AnalysisType.DATA_FITTING
554
  else:
555
  return AnalysisType.UNKNOWN
 
557
  except:
558
  return AnalysisType.UNKNOWN
559
 
560
+ def get_language_prompt_prefix(self, language: str) -> str:
561
+ """Obtiene el prefijo del prompt según el idioma"""
562
+ prefixes = {
563
+ 'en': "Please respond in English. ",
564
+ 'es': "Por favor responde en español. ",
565
+ 'fr': "Veuillez répondre en français. ",
566
+ 'de': "Bitte antworten Sie auf Deutsch. ",
567
+ 'pt': "Por favor responda em português. "
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
568
  }
569
+ return prefixes.get(language, prefixes['en'])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
570
 
571
+ def analyze_fitting_results(self, data: pd.DataFrame, claude_model: str, detail_level: str = "detailed", language: str = "en") -> Dict:
572
+ """Analiza resultados de ajuste de modelos con soporte multiidioma"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
573
 
574
  # Preparar resumen completo de los datos
575
  data_summary = f"""
576
+ FITTING RESULTS DATA:
577
 
578
+ Data structure:
579
+ - Columns: {list(data.columns)}
580
+ - Number of models evaluated: {len(data)}
581
 
582
+ Complete data:
583
  {data.to_string()}
584
 
585
+ Descriptive statistics:
586
  {data.describe().to_string()}
587
  """
588
 
589
+ # Obtener prefijo de idioma
590
+ lang_prefix = self.get_language_prompt_prefix(language)
591
+
592
+ # Prompt mejorado con soporte de idioma
593
  prompt = f"""
594
+ {lang_prefix}
595
+
596
+ You are an expert in biotechnology and mathematical modeling. Analyze these kinetic/biotechnological model fitting results.
597
+
598
+ REQUESTED DETAIL LEVEL: {detail_level}
599
+
600
+ PERFORM A COMPREHENSIVE COMPARATIVE ANALYSIS:
601
+
602
+ 1. **MODEL IDENTIFICATION AND CLASSIFICATION**
603
+ - Identify ALL fitted mathematical models
604
+ - Classify them by type: biomass, substrate, product
605
+ - Indicate the mathematical equation of each model if possible
606
+
607
+ 2. **COMPARATIVE ANALYSIS OF FIT QUALITY**
608
+ - Compare ALL available indicators: R², RMSE, AIC, BIC, etc.
609
+ - Create a ranking from best to worst model
610
+ - Identify significant differences between models
611
+ - Detect possible overfitting or underfitting
612
+
613
+ 3. **DETERMINATION OF THE BEST MODEL**
614
+ - Select the BEST model based on MULTIPLE criteria:
615
+ * Highest (closest to 1)
616
+ * Lowest RMSE/MSE
617
+ * Lowest AIC/BIC (if available)
618
+ * Parsimony (fewer parameters if fit is similar)
619
+ - Justify NUMERICALLY why it is the best
620
+ - If there's a technical tie, explain the advantages of each
621
+
622
+ 4. **SPECIFIC ANALYSIS BY VARIABLE TYPE**
623
+ a) **BIOMASS (if applicable)**:
624
+ - Growth parameters (μmax, Xmax, etc.)
625
+ - Doubling time
626
+ - Biomass productivity
627
+ - Numerical comparison between models
628
 
629
+ b) **SUBSTRATE (if applicable)**:
630
+ - Affinity constants (Ks, Km)
631
+ - Consumption rates
632
+ - Yield Yx/s
633
+ - Utilization efficiency
634
 
635
+ c) **PRODUCT (if applicable)**:
636
+ - Production parameters (α, β)
637
+ - Specific productivity
638
+ - Yield Yp/x
639
+ - Production type (associated/non-associated)
640
+
641
+ 5. **BIOLOGICAL INTERPRETATION OF PARAMETERS**
642
+ - Explain what EACH parameter means biologically
643
+ - Compare values between models
644
+ - Evaluate if they are realistic for the system
645
+ - Identify critical process parameters
646
+
647
+ 6. **CONCLUSIONS WITH NUMERICAL CONTENT**
648
+ - Summarize key findings with SPECIFIC NUMBERS
649
+ - Provide confidence intervals if available
650
+ - Indicate optimal operating conditions
651
+ - Suggest design values for scale-up
652
+
653
+ 7. **PRACTICAL RECOMMENDATIONS**
654
+ - Which model(s) to use for prediction
655
+ - Limitations of the selected model
656
+ - Recommended additional experiments
657
+ - Considerations for industrial implementation
658
+
659
+ 8. **FINAL COMPARATIVE TABLE**
660
+ Create a summary table with:
661
+ - Model | R² | RMSE | AIC/BIC | Key Parameters | Ranking
662
+
663
+ RESPONSE FORMAT:
664
+ - If level is "detailed": include ALL points with complete explanations
665
+ - If level is "summarized": focus on points 3, 6 and 8 with key numerical values
666
+
667
+ Use Markdown format with:
668
+ - Clear titles and subtitles
669
+ - **Bold** for important values
670
+ - Tables when appropriate
671
+ - Numbered and bulleted lists
672
+
673
+ IMPORTANT: Base ALL conclusions on the SPECIFIC NUMBERS from the provided data.
674
  """
675
 
676
  try:
 
683
  }]
684
  )
685
 
686
+ # Análisis adicional para generar código
687
+ code_prompt = f"""
688
+ {lang_prefix}
689
 
690
+ Based on the previous analysis, generate Python code for:
 
 
 
 
691
 
692
+ 1. Load and visualize these fitting results
693
+ 2. Create comparative model graphs (bars for R², RMSE)
694
+ 3. Implement the best identified model
695
+ 4. Generate predictions with the selected model
696
+ 5. Parameter sensitivity analysis
 
697
 
698
+ Include:
699
+ - Necessary imports
700
+ - Well-documented functions
701
+ - Professional visualizations
702
+ - Error handling
703
+ - Usage example
704
+
705
+ The code should be executable and modular.
706
  """
707
 
708
  code_response = self.client.messages.create(
 
710
  max_tokens=3000,
711
  messages=[{
712
  "role": "user",
713
+ "content": f"{code_prompt}\n\nBased on these models:\n{response.content[0].text[:1000]}"
714
  }]
715
  )
716
 
717
  return {
718
+ "tipo": "Comparative Analysis of Mathematical Models",
719
  "analisis_completo": response.content[0].text,
720
  "codigo_implementacion": code_response.content[0].text,
721
  "resumen_datos": {
 
731
  except Exception as e:
732
  return {"error": str(e)}
733
 
734
+ def process_files(files, claude_model: str, detail_level: str = "detailed", language: str = "en") -> Tuple[str, str]:
735
+ """Procesa múltiples archivos con soporte de idioma"""
736
  processor = FileProcessor()
737
  analyzer = AIAnalyzer(client, model_registry)
738
  results = []
 
748
  with open(file.name, 'rb') as f:
749
  file_content = f.read()
750
 
751
+ if file_ext in ['.csv', '.xlsx', '.xls']:
752
+ if language == 'es':
753
+ results.append(f"## 📊 Análisis de Resultados: {file_name}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
754
  else:
755
+ results.append(f"## 📊 Results Analysis: {file_name}")
 
 
 
 
756
 
757
  if file_ext == '.csv':
758
  df = processor.read_csv(file_content)
 
763
  analysis_type = analyzer.detect_analysis_type(df)
764
 
765
  if analysis_type == AnalysisType.FITTING_RESULTS:
766
+ result = analyzer.analyze_fitting_results(df, claude_model, detail_level, language)
767
+
768
+ if language == 'es':
769
+ results.append("### 🎯 ANÁLISIS COMPARATIVO DE MODELOS MATEMÁTICOS")
770
+ else:
771
+ results.append("### 🎯 COMPARATIVE ANALYSIS OF MATHEMATICAL MODELS")
772
+
773
  results.append(result.get("analisis_completo", ""))
774
  if "codigo_implementacion" in result:
775
  all_code.append(result["codigo_implementacion"])
 
 
 
 
776
 
777
  results.append("\n---\n")
778
 
 
782
  return analysis_text, code_text
783
 
784
  def generate_implementation_code(analysis_results: str) -> str:
785
+ """Genera código de implementación"""
786
  code = """
787
  import numpy as np
788
  import pandas as pd
 
793
  import seaborn as sns
794
  from typing import Dict, List, Tuple, Optional
795
 
796
+ # Visualization configuration
797
  plt.style.use('seaborn-v0_8-darkgrid')
798
  sns.set_palette("husl")
799
 
800
  class ComparativeModelAnalyzer:
801
  \"\"\"
802
+ Class for comparative analysis of biotechnological model fitting results.
803
+ Specialized in comparing biomass, substrate and product models.
804
  \"\"\"
805
 
806
  def __init__(self):
 
809
  self.model_rankings = {}
810
 
811
  def load_results(self, file_path: str) -> pd.DataFrame:
812
+ \"\"\"Load fitting results from CSV or Excel file\"\"\"
813
  if file_path.endswith('.csv'):
814
  self.results_df = pd.read_csv(file_path)
815
  else:
816
  self.results_df = pd.read_excel(file_path)
817
 
818
+ print(f"✅ Data loaded: {len(self.results_df)} models")
819
+ print(f"📊 Available columns: {list(self.results_df.columns)}")
820
 
821
  return self.results_df
822
 
 
827
  bic_col: Optional[str] = 'BIC',
828
  model_col: str = 'Model') -> pd.DataFrame:
829
  \"\"\"
830
+ Analyze and compare the fit quality of all models.
831
+ Create a ranking based on multiple metrics.
832
  \"\"\"
833
  if self.results_df is None:
834
+ raise ValueError("First load data with load_results()")
835
 
836
+ # Create comparison DataFrame
837
  comparison = self.results_df.copy()
838
 
839
+ # Calculate composite score
840
  scores = pd.DataFrame(index=comparison.index)
841
 
842
+ # Normalize metrics (0-1)
843
  if r2_col in comparison.columns:
844
+ scores['r2_score'] = comparison[r2_col] # Already between 0-1
845
 
846
  if rmse_col in comparison.columns:
847
+ # Invert and normalize RMSE (lower is better)
848
  max_rmse = comparison[rmse_col].max()
849
  scores['rmse_score'] = 1 - (comparison[rmse_col] / max_rmse)
850
 
851
  if aic_col and aic_col in comparison.columns:
852
+ # Invert and normalize AIC (lower is better)
853
  min_aic = comparison[aic_col].min()
854
  max_aic = comparison[aic_col].max()
855
  scores['aic_score'] = 1 - ((comparison[aic_col] - min_aic) / (max_aic - min_aic))
856
 
857
  if bic_col and bic_col in comparison.columns:
858
+ # Invert and normalize BIC (lower is better)
859
  min_bic = comparison[bic_col].min()
860
  max_bic = comparison[bic_col].max()
861
  scores['bic_score'] = 1 - ((comparison[bic_col] - min_bic) / (max_bic - min_bic))
862
 
863
+ # Calculate total score (weighted average)
864
  weights = {
865
  'r2_score': 0.4,
866
  'rmse_score': 0.3,
 
873
  if metric in scores.columns:
874
  scores['total_score'] += scores[metric] * weight
875
 
876
+ # Add score to comparison DataFrame
877
  comparison['Score'] = scores['total_score']
878
  comparison['Ranking'] = comparison['Score'].rank(ascending=False).astype(int)
879
 
880
+ # Sort by ranking
881
  comparison = comparison.sort_values('Ranking')
882
 
883
+ # Identify best model
884
  best_idx = comparison['Score'].idxmax()
885
  self.best_models['overall'] = comparison.loc[best_idx]
886
 
887
+ # Print comparison table
888
  print("\\n" + "="*80)
889
+ print("📊 MODEL COMPARISON TABLE")
890
  print("="*80)
891
 
892
+ print(f"\\n{'Rank':<6} {'Model':<20} {'R²':<8} {'RMSE':<10} {'AIC':<10} {'BIC':<10} {'Score':<8}")
893
  print("-"*80)
894
 
895
  for idx, row in comparison.iterrows():
 
912
  print(f"{'N/A':<10} ", end="")
913
  print(f"{score:<8.4f}")
914
 
915
+ print("\\n🏆 BEST MODEL: " + comparison.iloc[0].get(model_col, 'Not specified'))
916
  print(f" - R² = {comparison.iloc[0].get(r2_col, 0):.4f}")
917
  print(f" - RMSE = {comparison.iloc[0].get(rmse_col, 0):.4f}")
918
 
919
  self.model_rankings = comparison
920
  return comparison
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
921
 
922
+ # Example usage
923
  if __name__ == "__main__":
924
+ print("🧬 Biotechnological Model Comparative Analysis System")
925
  print("="*60)
926
 
927
+ # Create analyzer
928
  analyzer = ComparativeModelAnalyzer()
929
 
930
+ # Instructions
931
+ print("\\n📋 USAGE INSTRUCTIONS:")
932
+ print("1. analyzer.load_results('your_file.csv')")
933
  print("2. analyzer.analyze_model_quality()")
934
+ print("3. analyzer.plot_comparison()")
935
+ print("4. analyzer.generate_report()")
 
936
 
937
+ print("\\n✨ System ready for analysis!")
938
  """
939
 
940
  return code
941
 
942
+ # Estado global para almacenar resultados
943
+ class AppState:
944
+ def __init__(self):
945
+ self.current_analysis = ""
946
+ self.current_code = ""
947
+ self.current_language = "en"
948
+
949
+ app_state = AppState()
950
+
951
+ def export_report(export_format: str, language: str) -> Tuple[str, str]:
952
+ """Exporta el reporte al formato seleccionado"""
953
+ if not app_state.current_analysis:
954
+ error_msg = {
955
+ 'en': "No analysis available to export",
956
+ 'es': "No hay análisis disponible para exportar",
957
+ 'fr': "Aucune analyse disponible pour exporter",
958
+ 'de': "Keine Analyse zum Exportieren verfügbar",
959
+ 'pt': "Nenhuma análise disponível para exportar"
960
  }
961
+ return error_msg.get(language, error_msg['en']), ""
962
+
963
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
964
+
965
+ try:
966
+ if export_format == "DOCX":
967
+ filename = f"biotech_analysis_report_{timestamp}.docx"
968
+ ReportExporter.export_to_docx(app_state.current_analysis, filename, language)
969
+ else: # PDF
970
+ filename = f"biotech_analysis_report_{timestamp}.pdf"
971
+ ReportExporter.export_to_pdf(app_state.current_analysis, filename, language)
972
+
973
+ success_msg = TRANSLATIONS[language]['report_exported']
974
+ return f"{success_msg} {filename}", filename
975
+ except Exception as e:
976
+ return f"Error: {str(e)}", ""
977
+
978
+ # Interfaz Gradio con soporte multiidioma y temas
979
+ def create_interface():
980
+ # Estado inicial
981
+ current_theme = "light"
982
+ current_language = "en"
983
+
984
+ def update_interface_language(language):
985
+ """Actualiza el idioma de la interfaz"""
986
+ app_state.current_language = language
987
+ t = TRANSLATIONS[language]
988
+
989
+ return {
990
+ title_text: gr.update(value=f"# {t['title']}"),
991
+ subtitle_text: gr.update(value=t['subtitle']),
992
+ files_input: gr.update(label=t['upload_files']),
993
+ model_selector: gr.update(label=t['select_model']),
994
+ language_selector: gr.update(label=t['select_language']),
995
+ theme_selector: gr.update(label=t['select_theme']),
996
+ detail_level: gr.update(label=t['detail_level']),
997
+ analyze_btn: gr.update(value=t['analyze_button']),
998
+ export_format: gr.update(label=t['export_format']),
999
+ export_btn: gr.update(value=t['export_button']),
1000
+ analysis_output: gr.update(label=t['comparative_analysis']),
1001
+ code_output: gr.update(label=t['implementation_code']),
1002
+ examples_label: gr.update(label=t['examples']),
1003
+ data_format_accordion: gr.update(label=t['data_format'])
1004
  }
1005
+
1006
+ def process_and_store(files, model, detail, language):
1007
+ """Procesa archivos y almacena resultados"""
1008
+ if not files:
1009
+ error_msg = TRANSLATIONS[language]['error_no_files']
1010
+ return error_msg, ""
1011
+
1012
+ analysis, code = process_files(files, model, detail, language)
1013
+ app_state.current_analysis = analysis
1014
+ app_state.current_code = code
1015
+ return analysis, code
1016
+
1017
+ with gr.Blocks(theme=THEMES[current_theme]) as demo:
1018
+ # Componentes de UI
1019
+ with gr.Row():
1020
+ with gr.Column(scale=3):
1021
+ title_text = gr.Markdown(f"# {TRANSLATIONS[current_language]['title']}")
1022
+ subtitle_text = gr.Markdown(TRANSLATIONS[current_language]['subtitle'])
1023
+ with gr.Column(scale=1):
1024
+ with gr.Row():
1025
+ language_selector = gr.Dropdown(
1026
+ choices=[("English", "en"), ("Español", "es"), ("Français", "fr"),
1027
+ ("Deutsch", "de"), ("Português", "pt")],
1028
+ value="en",
1029
+ label=TRANSLATIONS[current_language]['select_language'],
1030
+ interactive=True
1031
+ )
1032
+ theme_selector = gr.Dropdown(
1033
+ choices=[("Light", "light"), ("Dark", "dark")],
1034
+ value="light",
1035
+ label=TRANSLATIONS[current_language]['select_theme'],
1036
+ interactive=True
1037
+ )
1038
 
1039
  with gr.Row():
1040
  with gr.Column(scale=1):
1041
  files_input = gr.File(
1042
+ label=TRANSLATIONS[current_language]['upload_files'],
1043
  file_count="multiple",
1044
  file_types=[".csv", ".xlsx", ".xls", ".pdf", ".zip"],
1045
  type="filepath"
 
1048
  model_selector = gr.Dropdown(
1049
  choices=list(CLAUDE_MODELS.keys()),
1050
  value="claude-3-5-sonnet-20241022",
1051
+ label=TRANSLATIONS[current_language]['select_model'],
1052
+ info=f"{TRANSLATIONS[current_language]['best_for']}: {CLAUDE_MODELS['claude-3-5-sonnet-20241022']['best_for']}"
1053
  )
1054
 
1055
  detail_level = gr.Radio(
1056
+ choices=[
1057
+ (TRANSLATIONS[current_language]['detailed'], "detailed"),
1058
+ (TRANSLATIONS[current_language]['summarized'], "summarized")
1059
+ ],
1060
+ value="detailed",
1061
+ label=TRANSLATIONS[current_language]['detail_level']
1062
  )
1063
 
1064
  analyze_btn = gr.Button(
1065
+ TRANSLATIONS[current_language]['analyze_button'],
1066
  variant="primary",
1067
  size="lg"
1068
  )
1069
 
1070
+ gr.Markdown("---")
 
1071
 
1072
+ export_format = gr.Radio(
1073
+ choices=["DOCX", "PDF"],
1074
+ value="PDF",
1075
+ label=TRANSLATIONS[current_language]['export_format']
1076
+ )
 
 
 
 
1077
 
1078
+ export_btn = gr.Button(
1079
+ TRANSLATIONS[current_language]['export_button'],
1080
+ variant="secondary"
1081
+ )
1082
+
1083
+ export_status = gr.Textbox(
1084
+ label="Export Status",
1085
+ interactive=False,
1086
+ visible=False
1087
+ )
1088
+
1089
+ export_file = gr.File(
1090
+ label="Download Report",
1091
+ visible=False
1092
  )
1093
 
1094
  with gr.Column(scale=2):
1095
  analysis_output = gr.Markdown(
1096
+ label=TRANSLATIONS[current_language]['comparative_analysis']
 
1097
  )
1098
 
1099
  code_output = gr.Code(
1100
+ label=TRANSLATIONS[current_language]['implementation_code'],
1101
  language="python",
1102
  interactive=True,
1103
  lines=20
1104
  )
1105
 
1106
+ data_format_accordion = gr.Accordion(
1107
+ label=TRANSLATIONS[current_language]['data_format'],
1108
+ open=False
1109
+ )
1110
+
1111
+ with data_format_accordion:
1112
  gr.Markdown("""
1113
+ ### Expected CSV/Excel structure:
1114
 
1115
  | Model | R2 | RMSE | AIC | BIC | mu_max | Ks | Parameters |
1116
  |-------|-----|------|-----|-----|--------|-------|------------|
1117
  | Monod | 0.985 | 0.023 | -45.2 | -42.1 | 0.45 | 2.1 | {...} |
1118
  | Logistic | 0.976 | 0.031 | -42.1 | -39.5 | 0.42 | - | {...} |
1119
  | Gompertz | 0.992 | 0.018 | -48.5 | -45.2 | 0.48 | - | {...} |
 
 
 
 
 
 
 
 
 
 
 
1120
  """)
1121
 
1122
+ examples_label = gr.Examples(
 
1123
  examples=[
1124
+ [["examples/biomass_models_comparison.csv"], "claude-3-5-sonnet-20241022", "detailed"],
1125
+ [["examples/substrate_kinetics_results.xlsx"], "claude-3-5-sonnet-20241022", "summarized"]
 
1126
  ],
1127
  inputs=[files_input, model_selector, detail_level],
1128
+ label=TRANSLATIONS[current_language]['examples']
1129
  )
1130
 
1131
  # Eventos
1132
+ language_selector.change(
1133
+ update_interface_language,
1134
+ inputs=[language_selector],
1135
+ outputs=[
1136
+ title_text, subtitle_text, files_input, model_selector,
1137
+ language_selector, theme_selector, detail_level, analyze_btn,
1138
+ export_format, export_btn, analysis_output, code_output,
1139
+ examples_label, data_format_accordion
1140
+ ]
1141
+ )
1142
+
1143
+ def change_theme(theme_name):
1144
+ """Cambia el tema de la interfaz"""
1145
+ # Nota: En Gradio actual, cambiar el tema dinámicamente requiere recargar
1146
+ # Esta es una limitación conocida
1147
+ return gr.Info("Theme will be applied on next page load")
1148
+
1149
+ theme_selector.change(
1150
+ change_theme,
1151
+ inputs=[theme_selector],
1152
+ outputs=[]
1153
+ )
1154
+
1155
  analyze_btn.click(
1156
+ fn=process_and_store,
1157
+ inputs=[files_input, model_selector, detail_level, language_selector],
 
 
 
1158
  outputs=[analysis_output, code_output]
1159
  )
1160
 
1161
+ def handle_export(format, language):
1162
+ status, file = export_report(format, language)
1163
+ if file:
1164
+ return gr.update(value=status, visible=True), gr.update(value=file, visible=True)
1165
+ else:
1166
+ return gr.update(value=status, visible=True), gr.update(visible=False)
1167
+
1168
+ export_btn.click(
1169
+ fn=handle_export,
1170
+ inputs=[export_format, language_selector],
1171
+ outputs=[export_status, export_file]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1172
  )
1173
 
1174
  return demo
1175
 
1176
+ # Función principal
1177
  def main():
1178
  if not os.getenv("ANTHROPIC_API_KEY"):
1179
+ print("⚠️ Configure ANTHROPIC_API_KEY in HuggingFace Space secrets")
1180
  return gr.Interface(
1181
+ fn=lambda x: TRANSLATIONS['en']['error_no_api'],
1182
  inputs=gr.Textbox(),
1183
  outputs=gr.Textbox(),
1184
+ title="Configuration Error"
1185
  )
1186
 
1187
  return create_interface()