C2MV commited on
Commit
f340ee7
verified
1 Parent(s): 406b47b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +197 -21
app.py CHANGED
@@ -6,7 +6,7 @@ import plotly.graph_objects as go
6
  from plotly.subplots import make_subplots
7
  from scipy.optimize import minimize
8
  import plotly.express as px
9
- from scipy.stats import t
10
  import gradio as gr
11
 
12
  class RSM_BoxBehnken:
@@ -25,8 +25,6 @@ class RSM_BoxBehnken:
25
  x3_levels (list): Niveles de la tercera variable independiente.
26
  """
27
  self.data = data.copy()
28
- # Ya no es necesario renombrar las columnas aqu铆, se har谩 al cargar los datos
29
-
30
  self.model = None
31
  self.model_simplified = None
32
  self.optimized_results = None
@@ -71,7 +69,7 @@ class RSM_BoxBehnken:
71
  self.model = smf.ols(formula, data=self.data).fit()
72
  print("Modelo Completo:")
73
  print(self.model.summary())
74
- return self.pareto_chart(self.model, "Pareto - Modelo Completo")
75
 
76
  def fit_simplified_model(self):
77
  """
@@ -82,7 +80,7 @@ class RSM_BoxBehnken:
82
  self.model_simplified = smf.ols(formula, data=self.data).fit()
83
  print("\nModelo Simplificado:")
84
  print(self.model_simplified.summary())
85
- return self.pareto_chart(self.model_simplified, "Pareto - Modelo Simplificado")
86
 
87
  def optimize(self, method='Nelder-Mead'):
88
  """
@@ -110,12 +108,14 @@ class RSM_BoxBehnken:
110
  self.coded_to_natural(self.optimal_levels[1], self.x2_name),
111
  self.coded_to_natural(self.optimal_levels[2], self.x3_name)
112
  ]
 
 
 
 
 
 
113
 
114
- print(f"\nNiveles 贸ptimos encontrados (basado en modelo simplificado):")
115
- print(f"{self.x1_name}: {optimal_levels_natural[0]:.4f} g/L")
116
- print(f"{self.x2_name}: {optimal_levels_natural[1]:.4f} g/L")
117
- print(f"{self.x3_name}: {optimal_levels_natural[2]:.4f} g/L")
118
- print(f"Valor m谩ximo de {self.y_name}: {-self.optimized_results.fun:.4f}")
119
 
120
  def plot_rsm_individual(self, fixed_variable, fixed_level):
121
  """
@@ -311,6 +311,167 @@ class RSM_BoxBehnken:
311
 
312
  return fig
313
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
314
  # --- Funciones para la interfaz de Gradio ---
315
 
316
  def load_data(x1_name, x2_name, x3_name, y_name, x1_levels_str, x2_levels_str, x3_levels_str, data_str):
@@ -357,14 +518,22 @@ def load_data(x1_name, x2_name, x3_name, y_name, x1_levels_str, x2_levels_str, x
357
 
358
  def fit_and_optimize_model():
359
  if 'rsm' not in globals():
360
- return None, None, "Error: Carga los datos primero."
 
 
 
 
 
 
 
 
361
 
362
- pareto_completo = rsm.fit_model()
363
- pareto_simplificado = rsm.fit_simplified_model()
364
- rsm.optimize()
365
- model_summary = rsm.model_simplified.summary().as_html()
366
 
367
- return model_summary, pareto_completo, pareto_simplificado, f"{rsm.x1_name}: {rsm.optimal_levels[0]:.4f}, {rsm.x2_name}: {rsm.optimal_levels[1]:.4f}, {rsm.x3_name}: {rsm.optimal_levels[2]:.4f}, Valor m谩ximo de {rsm.y_name}: {-rsm.optimized_results.fun:.4f}"
368
 
369
  def generate_rsm_plot(fixed_variable, fixed_level):
370
  if 'rsm' not in globals():
@@ -413,13 +582,20 @@ with gr.Blocks() as demo:
413
  with gr.Row(visible=False) as analysis_row:
414
  with gr.Column():
415
  fit_button = gr.Button("Ajustar Modelo y Optimizar")
416
- model_summary_output = gr.HTML()
417
- pareto_chart_completo = gr.Plot()
418
- pareto_chart_simplificado = gr.Plot()
419
- optimization_results_output = gr.Textbox(label="Resultados de la Optimizaci贸n")
 
 
 
 
 
 
 
420
  with gr.Column():
421
  gr.Markdown("## Generar Gr谩ficos de Superficie de Respuesta")
422
- fixed_variable_input = gr.Dropdown(label="Variable Fija", choices=["Glucosa", "Extracto_de_Levadura", "Tript贸fano"], value="Glucosa")
423
  fixed_level_input = gr.Slider(label="Nivel de Variable Fija", minimum=0, maximum=1, step=0.01, value=0.5)
424
  plot_button = gr.Button("Generar Gr谩fico")
425
  rsm_plot_output = gr.Plot()
 
6
  from plotly.subplots import make_subplots
7
  from scipy.optimize import minimize
8
  import plotly.express as px
9
+ from scipy.stats import t, f
10
  import gradio as gr
11
 
12
  class RSM_BoxBehnken:
 
25
  x3_levels (list): Niveles de la tercera variable independiente.
26
  """
27
  self.data = data.copy()
 
 
28
  self.model = None
29
  self.model_simplified = None
30
  self.optimized_results = None
 
69
  self.model = smf.ols(formula, data=self.data).fit()
70
  print("Modelo Completo:")
71
  print(self.model.summary())
72
+ return self.model, self.pareto_chart(self.model, "Pareto - Modelo Completo")
73
 
74
  def fit_simplified_model(self):
75
  """
 
80
  self.model_simplified = smf.ols(formula, data=self.data).fit()
81
  print("\nModelo Simplificado:")
82
  print(self.model_simplified.summary())
83
+ return self.model_simplified, self.pareto_chart(self.model_simplified, "Pareto - Modelo Simplificado")
84
 
85
  def optimize(self, method='Nelder-Mead'):
86
  """
 
108
  self.coded_to_natural(self.optimal_levels[1], self.x2_name),
109
  self.coded_to_natural(self.optimal_levels[2], self.x3_name)
110
  ]
111
+ # Crear la tabla de optimizaci贸n
112
+ optimization_table = pd.DataFrame({
113
+ 'Variable': [self.x1_name, self.x2_name, self.x3_name],
114
+ 'Nivel 脫ptimo (Natural)': optimal_levels_natural,
115
+ 'Nivel 脫ptimo (Codificado)': self.optimal_levels
116
+ })
117
 
118
+ return optimization_table
 
 
 
 
119
 
120
  def plot_rsm_individual(self, fixed_variable, fixed_level):
121
  """
 
311
 
312
  return fig
313
 
314
+ def get_simplified_equation(self):
315
+ """
316
+ Imprime la ecuaci贸n del modelo simplificado.
317
+ """
318
+ if self.model_simplified is None:
319
+ print("Error: Ajusta el modelo simplificado primero.")
320
+ return None
321
+
322
+ coefficients = self.model_simplified.params
323
+ equation = f"{self.y_name} = {coefficients['Intercept']:.4f}"
324
+
325
+ for term, coef in coefficients.items():
326
+ if term != 'Intercept':
327
+ if term == f'{self.x1_name}':
328
+ equation += f" + {coef:.4f}*{self.x1_name}"
329
+ elif term == f'{self.x2_name}':
330
+ equation += f" + {coef:.4f}*{self.x2_name}"
331
+ elif term == f'{self.x3_name}':
332
+ equation += f" + {coef:.4f}*{self.x3_name}"
333
+ elif term == f'I({self.x1_name} ** 2)':
334
+ equation += f" + {coef:.4f}*{self.x1_name}^2"
335
+ elif term == f'I({self.x2_name} ** 2)':
336
+ equation += f" + {coef:.4f}*{self.x2_name}^2"
337
+ elif term == f'I({self.x3_name} ** 2)':
338
+ equation += f" + {coef:.4f}*{self.x3_name}^2"
339
+
340
+ return equation
341
+
342
+ def generate_prediction_table(self):
343
+ """
344
+ Genera una tabla con los valores actuales, predichos y residuales.
345
+ """
346
+ if self.model_simplified is None:
347
+ print("Error: Ajusta el modelo simplificado primero.")
348
+ return None
349
+
350
+ self.data['Predicho'] = self.model_simplified.predict(self.data)
351
+ self.data['Residual'] = self.data[self.y_name] - self.data['Predicho']
352
+
353
+ return self.data[[self.y_name, 'Predicho', 'Residual']]
354
+
355
+ def calculate_contribution_percentage(self):
356
+ """
357
+ Calcula el porcentaje de contribuci贸n de cada factor a la variabilidad de la respuesta (AIA).
358
+ """
359
+ if self.model_simplified is None:
360
+ print("Error: Ajusta el modelo simplificado primero.")
361
+ return None
362
+
363
+ # ANOVA del modelo simplificado
364
+ anova_table = sm.stats.anova_lm(self.model_simplified, typ=2)
365
+
366
+ # Suma de cuadrados total
367
+ ss_total = anova_table['sum_sq'].sum()
368
+
369
+ # Crear tabla de contribuci贸n
370
+ contribution_table = pd.DataFrame({
371
+ 'Factor': [],
372
+ 'Suma de Cuadrados': [],
373
+ '% Contribuci贸n': []
374
+ })
375
+
376
+ # Calcular porcentaje de contribuci贸n para cada factor
377
+ for index, row in anova_table.iterrows():
378
+ if index != 'Residual':
379
+ factor_name = index
380
+ if factor_name == f'I({self.x1_name} ** 2)':
381
+ factor_name = f'{self.x1_name}^2'
382
+ elif factor_name == f'I({self.x2_name} ** 2)':
383
+ factor_name = f'{self.x2_name}^2'
384
+ elif factor_name == f'I({self.x3_name} ** 2)':
385
+ factor_name = f'{self.x3_name}^2'
386
+
387
+ ss_factor = row['sum_sq']
388
+ contribution_percentage = (ss_factor / ss_total) * 100
389
+
390
+ contribution_table = pd.concat([contribution_table, pd.DataFrame({
391
+ 'Factor': [factor_name],
392
+ 'Suma de Cuadrados': [ss_factor],
393
+ '% Contribuci贸n': [contribution_percentage]
394
+ })], ignore_index=True)
395
+
396
+ return contribution_table
397
+
398
+ def calculate_detailed_anova(self):
399
+ """
400
+ Calcula la tabla ANOVA detallada con la descomposici贸n del error residual.
401
+ """
402
+ if self.model_simplified is None:
403
+ print("Error: Ajusta el modelo simplificado primero.")
404
+ return None
405
+
406
+ # --- ANOVA detallada ---
407
+ # 1. Ajustar un modelo solo con los t茅rminos de primer orden y cuadr谩ticos
408
+ formula_reduced = f'{self.y_name} ~ {self.x1_name} + {self.x2_name} + {self.x3_name} + ' \
409
+ f'I({self.x1_name}**2) + I({self.x2_name}**2) + I({self.x3_name}**2)'
410
+ model_reduced = smf.ols(formula_reduced, data=self.data).fit()
411
+
412
+ # 2. ANOVA del modelo reducido (para obtener la suma de cuadrados de la regresi贸n)
413
+ anova_reduced = sm.stats.anova_lm(model_reduced, typ=2)
414
+
415
+ # 3. Suma de cuadrados total
416
+ ss_total = np.sum((self.data[self.y_name] - self.data[self.y_name].mean())**2)
417
+
418
+ # 4. Grados de libertad totales
419
+ df_total = len(self.data) - 1
420
+
421
+ # 5. Suma de cuadrados de la regresi贸n
422
+ ss_regression = anova_reduced['sum_sq'][:-1].sum() # Sumar todo excepto 'Residual'
423
+
424
+ # 6. Grados de libertad de la regresi贸n
425
+ df_regression = len(anova_reduced) - 1
426
+
427
+ # 7. Suma de cuadrados del error residual
428
+ ss_residual = self.model_simplified.ssr
429
+ df_residual = self.model_simplified.df_resid
430
+
431
+ # 8. Suma de cuadrados del error puro (se calcula a partir de las r茅plicas)
432
+ replicas = self.data[self.data.duplicated(subset=[self.x1_name, self.x2_name, self.x3_name], keep=False)]
433
+ ss_pure_error = replicas.groupby([self.x1_name, self.x2_name, self.x3_name])[self.y_name].var().sum()
434
+ df_pure_error = len(replicas) - len(replicas.groupby([self.x1_name, self.x2_name, self.x3_name]))
435
+
436
+ # 9. Suma de cuadrados de la falta de ajuste
437
+ ss_lack_of_fit = ss_residual - ss_pure_error
438
+ df_lack_of_fit = df_residual - df_pure_error
439
+
440
+ # 10. Cuadrados medios
441
+ ms_regression = ss_regression / df_regression
442
+ ms_residual = ss_residual / df_residual
443
+ ms_lack_of_fit = ss_lack_of_fit / df_lack_of_fit
444
+ ms_pure_error = ss_pure_error / df_pure_error
445
+
446
+ # 11. Estad铆stico F y valor p para la falta de ajuste
447
+ f_lack_of_fit = ms_lack_of_fit / ms_pure_error
448
+ p_lack_of_fit = 1 - f.cdf(f_lack_of_fit, df_lack_of_fit, df_pure_error) # Usar f.cdf de scipy.stats
449
+
450
+ # 12. Crear la tabla ANOVA detallada
451
+ detailed_anova_table = pd.DataFrame({
452
+ 'Fuente de Variaci贸n': ['Regresi贸n', 'Residual', 'Falta de Ajuste', 'Error Puro', 'Total'],
453
+ 'Suma de Cuadrados': [ss_regression, ss_residual, ss_lack_of_fit, ss_pure_error, ss_total],
454
+ 'Grados de Libertad': [df_regression, df_residual, df_lack_of_fit, df_pure_error, df_total],
455
+ 'Cuadrado Medio': [ms_regression, ms_residual, ms_lack_of_fit, ms_pure_error, np.nan],
456
+ 'F': [np.nan, np.nan, f_lack_of_fit, np.nan, np.nan],
457
+ 'Valor p': [np.nan, np.nan, p_lack_of_fit, np.nan, np.nan]
458
+ })
459
+
460
+ # Calcular la suma de cuadrados y grados de libertad para la curvatura
461
+ ss_curvature = anova_reduced['sum_sq'][f'I({self.x1_name} ** 2)'] + anova_reduced['sum_sq'][f'I({self.x2_name} ** 2)'] + anova_reduced['sum_sq'][f'I({self.x3_name} ** 2)']
462
+ df_curvature = 3
463
+
464
+ # A帽adir la fila de curvatura a la tabla ANOVA
465
+ detailed_anova_table.loc[len(detailed_anova_table)] = ['Curvatura', ss_curvature, df_curvature, ss_curvature / df_curvature, np.nan, np.nan]
466
+
467
+ # Reorganizar las filas para que la curvatura aparezca despu茅s de la regresi贸n
468
+ detailed_anova_table = detailed_anova_table.reindex([0, 5, 1, 2, 3, 4])
469
+
470
+ # Resetear el 铆ndice para que sea consecutivo
471
+ detailed_anova_table = detailed_anova_table.reset_index(drop=True)
472
+
473
+ return detailed_anova_table
474
+
475
  # --- Funciones para la interfaz de Gradio ---
476
 
477
  def load_data(x1_name, x2_name, x3_name, y_name, x1_levels_str, x2_levels_str, x3_levels_str, data_str):
 
518
 
519
  def fit_and_optimize_model():
520
  if 'rsm' not in globals():
521
+ return None, None, None, None, None, None, "Error: Carga los datos primero."
522
+
523
+ model_completo, pareto_completo = rsm.fit_model()
524
+ model_simplificado, pareto_simplificado = rsm.fit_simplified_model()
525
+ optimization_table = rsm.optimize()
526
+ equation = rsm.get_simplified_equation()
527
+ prediction_table = rsm.generate_prediction_table()
528
+ contribution_table = rsm.calculate_contribution_percentage()
529
+ anova_table = rsm.calculate_detailed_anova()
530
 
531
+ # Formatear la ecuaci贸n para que se vea mejor en Markdown
532
+ equation_formatted = equation.replace(" + ", "<br>+ ").replace(" ** ", "^").replace("*", " 脳 ")
533
+ equation_formatted = f"### Ecuaci贸n del Modelo Simplificado:<br>{equation_formatted}"
534
+
535
 
536
+ return model_completo.summary().as_html(), pareto_completo, model_simplificado.summary().as_html(), pareto_simplificado, equation_formatted, optimization_table, prediction_table, contribution_table, anova_table
537
 
538
  def generate_rsm_plot(fixed_variable, fixed_level):
539
  if 'rsm' not in globals():
 
582
  with gr.Row(visible=False) as analysis_row:
583
  with gr.Column():
584
  fit_button = gr.Button("Ajustar Modelo y Optimizar")
585
+ gr.Markdown("**Modelo Completo**")
586
+ model_completo_output = gr.HTML()
587
+ pareto_completo_output = gr.Plot()
588
+ gr.Markdown("**Modelo Simplificado**")
589
+ model_simplificado_output = gr.HTML()
590
+ pareto_simplificado_output = gr.Plot()
591
+ equation_output = gr.HTML()
592
+ optimization_table_output = gr.Dataframe(label="Tabla de Optimizaci贸n")
593
+ prediction_table_output = gr.Dataframe(label="Tabla de Predicciones")
594
+ contribution_table_output = gr.Dataframe(label="Tabla de % de Contribuci贸n")
595
+ anova_table_output = gr.Dataframe(label="Tabla ANOVA Detallada")
596
  with gr.Column():
597
  gr.Markdown("## Generar Gr谩ficos de Superficie de Respuesta")
598
+ fixed_variable_input = gr.Dropdown(label="Variable Fija", choices=["Glucosa", "Extracto_de_Levadura", "Triptofano"], value="Glucosa")
599
  fixed_level_input = gr.Slider(label="Nivel de Variable Fija", minimum=0, maximum=1, step=0.01, value=0.5)
600
  plot_button = gr.Button("Generar Gr谩fico")
601
  rsm_plot_output = gr.Plot()