C2MV commited on
Commit
c95ab01
·
verified ·
1 Parent(s): ee2f88b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +64 -64
app.py CHANGED
@@ -21,8 +21,8 @@ import os
21
  class RSM_BoxBehnken:
22
  def __init__(self, data, x1_name, x2_name, x3_name, y_name, x1_levels, x2_levels, x3_levels):
23
  """
24
- Inicializa la clase con los datos del diseño Box-Behnken.
25
- """
26
  self.data = data.copy()
27
  self.model = None
28
  self.model_simplified = None
@@ -41,8 +41,8 @@ class RSM_BoxBehnken:
41
 
42
  def get_levels(self, variable_name):
43
  """
44
- Obtiene los niveles para una variable específica.
45
- """
46
  if variable_name == self.x1_name:
47
  return self.x1_levels
48
  elif variable_name == self.x2_name:
@@ -54,8 +54,8 @@ class RSM_BoxBehnken:
54
 
55
  def fit_model(self):
56
  """
57
- Ajusta el modelo de segundo orden completo a los datos.
58
- """
59
  formula = f'{self.y_name} ~ {self.x1_name} + {self.x2_name} + {self.x3_name} + ' \
60
  f'I({self.x1_name}**2) + I({self.x2_name}**2) + I({self.x3_name}**2) + ' \
61
  f'{self.x1_name}:{self.x2_name} + {self.x1_name}:{self.x3_name} + {self.x2_name}:{self.x3_name}'
@@ -66,19 +66,19 @@ class RSM_BoxBehnken:
66
 
67
  def fit_simplified_model(self):
68
  """
69
- Ajusta el modelo de segundo orden a los datos, eliminando términos no significativos.
70
- """
71
  formula = f'{self.y_name} ~ {self.x1_name} + {self.x2_name} + ' \
72
  f'I({self.x1_name}**2) + I({self.x2_name}**2) + I({self.x3_name}**2)'
73
  self.model_simplified = smf.ols(formula, data=self.data).fit()
74
  print("\nModelo Simplificado:")
75
  print(self.model_simplified.summary())
76
  return self.model_simplified, self.pareto_chart_f(self.model_simplified, "Pareto - Modelo Simplificado (F)"), self.pareto_chart_t(self.model_simplified, "Pareto - Modelo Simplificado (t)")
77
-
78
  def optimize(self, method='Nelder-Mead'):
79
  """
80
- Encuentra los niveles óptimos de los factores para maximizar la respuesta usando el modelo simplificado.
81
- """
82
  if self.model_simplified is None:
83
  print("Error: Ajusta el modelo simplificado primero.")
84
  return
@@ -113,15 +113,15 @@ class RSM_BoxBehnken:
113
 
114
  def plot_rsm_individual(self, fixed_variable, fixed_level):
115
  """
116
- Genera un gráfico de superficie de respuesta (RSM) individual para una configuración específica.
117
- """
118
  if self.model_simplified is None:
119
  print("Error: Ajusta el modelo simplificado primero.")
120
  return None
121
 
122
  # Determinar las variables que varían y sus niveles naturales
123
  varying_variables = [var for var in [self.x1_name, self.x2_name, self.x3_name] if var != fixed_variable]
124
-
125
  # Establecer los niveles naturales para las variables que varían
126
  x_natural_levels = self.get_levels(varying_variables[0])
127
  y_natural_levels = self.get_levels(varying_variables[1])
@@ -220,9 +220,9 @@ class RSM_BoxBehnken:
220
 
221
  def get_units(self, variable_name):
222
  """
223
- Define las unidades de las variables para etiquetas.
224
- Puedes personalizar este método según tus necesidades.
225
- """
226
  units = {
227
  'Glucosa': 'g/L',
228
  'Extracto_de_Levadura': 'g/L',
@@ -233,9 +233,9 @@ class RSM_BoxBehnken:
233
 
234
  def generate_all_plots(self):
235
  """
236
- Genera todas las gráficas de RSM, variando la variable fija y sus niveles usando el modelo simplificado.
237
- Almacena las figuras en self.all_figures.
238
- """
239
  if self.model_simplified is None:
240
  print("Error: Ajusta el modelo simplificado primero.")
241
  return
@@ -268,25 +268,25 @@ class RSM_BoxBehnken:
268
 
269
  def pareto_chart_f(self, model, title):
270
  """
271
- Genera un diagrama de Pareto para los efectos usando estadísticos F,
272
- incluyendo la línea de significancia.
273
- """
274
  # Calcular los estadísticos F para cada término manualmente
275
  # Necesitamos los coeficientes, los errores estándar y los grados de libertad del error
276
  coef = model.params[1:] # Excluir la Intercept
277
- stderr = model.bse[1:] # Excluir la Intercept
278
  df_resid = model.df_resid
279
-
280
  # Calcular F = (coef / stderr)^2
281
  fvalues = (coef / stderr) ** 2
282
-
283
  abs_fvalues = np.abs(fvalues)
284
  sorted_idx = np.argsort(abs_fvalues)[::-1]
285
  sorted_fvalues = abs_fvalues[sorted_idx]
286
  sorted_names = fvalues.index[sorted_idx]
287
-
288
  # Cambiar I() por "" en los nombres de los terminos
289
- sorted_names = sorted_names.str.replace('I(','').str.replace('**2)','^2').str.replace(' * ', '·')
290
 
291
  # Calcular el valor crítico de F para la línea de significancia
292
  alpha = 0.05 # Nivel de significancia
@@ -306,16 +306,16 @@ class RSM_BoxBehnken:
306
 
307
  # Agregar la línea de significancia
308
  fig.add_vline(x=f_critical, line_dash="dot",
309
- annotation_text=f"F crítico = {f_critical:.3f}",
310
- annotation_position="bottom right")
311
 
312
  return fig
313
-
314
  def pareto_chart_t(self, model, title):
315
  """
316
- Genera un diagrama de Pareto para los efectos usando estadísticos t,
317
- incluyendo la línea de significancia.
318
- """
319
  # Calcular los estadísticos t para cada término
320
  tvalues = model.tvalues[1:] # Excluir la Intercept
321
  abs_tvalues = np.abs(tvalues)
@@ -324,7 +324,7 @@ class RSM_BoxBehnken:
324
  sorted_names = tvalues.index[sorted_idx]
325
 
326
  # Cambiar I() por "" en los nombres de los terminos
327
- sorted_names = sorted_names.str.replace('I(','').str.replace('**2)','^2').str.replace(' * ', '·')
328
 
329
  # Calcular el valor crítico de t para la línea de significancia
330
  alpha = 0.05 # Nivel de significancia
@@ -350,8 +350,8 @@ class RSM_BoxBehnken:
350
 
351
  def get_simplified_equation(self):
352
  """
353
- Retorna la ecuación del modelo simplificado como una cadena de texto.
354
- """
355
  if self.model_simplified is None:
356
  print("Error: Ajusta el modelo simplificado primero.")
357
  return None
@@ -375,11 +375,11 @@ class RSM_BoxBehnken:
375
  equation += f" + {coef:.3f}*{self.x3_name}^2"
376
 
377
  return equation
378
-
379
  def generate_prediction_table(self):
380
  """
381
- Genera una tabla con los valores actuales, predichos y residuales.
382
- """
383
  if self.model_simplified is None:
384
  print("Error: Ajusta el modelo simplificado primero.")
385
  return None
@@ -391,8 +391,8 @@ class RSM_BoxBehnken:
391
 
392
  def calculate_anova_table(self):
393
  """
394
- Calcula la tabla ANOVA detallada, incluyendo la contribución porcentual.
395
- """
396
  if self.model_simplified is None:
397
  print("Error: Ajusta el modelo simplificado primero.")
398
  return None
@@ -444,7 +444,7 @@ class RSM_BoxBehnken:
444
  # 11. Estadísticos F y valores p
445
  f_regression = ms_regression / ms_residual
446
  p_regression = 1 - f.cdf(f_regression, df_regression, df_residual)
447
-
448
  f_lack_of_fit = ms_lack_of_fit / ms_pure_error if not np.isnan(ms_lack_of_fit) else np.nan
449
  p_lack_of_fit = 1 - f.cdf(f_lack_of_fit, df_lack_of_fit, df_pure_error) if not np.isnan(f_lack_of_fit) else np.nan
450
 
@@ -457,11 +457,11 @@ class RSM_BoxBehnken:
457
  'F': [f_regression, np.nan, f_lack_of_fit, np.nan, np.nan],
458
  'Valor p': [p_regression, np.nan, p_lack_of_fit, np.nan, np.nan]
459
  })
460
-
461
  # Calcular la suma de cuadrados y estadísticos F para la curvatura
462
  ss_curvature = anova_reduced['sum_sq'][f'I({self.x1_name} ** 2)'] + \
463
- anova_reduced['sum_sq'][f'I({self.x2_name} ** 2)'] + \
464
- anova_reduced['sum_sq'][f'I({self.x3_name} ** 2)']
465
  df_curvature = 3
466
  ms_curvature = ss_curvature / df_curvature
467
  f_curvature = ms_curvature / ms_residual
@@ -469,14 +469,14 @@ class RSM_BoxBehnken:
469
 
470
  # Añadir la fila de curvatura a la tabla ANOVA
471
  detailed_anova_table.loc[len(detailed_anova_table)] = [
472
- 'Curvatura',
473
- ss_curvature,
474
- df_curvature,
475
  ms_curvature,
476
  f_curvature,
477
  p_curvature
478
  ]
479
-
480
  # --- Agregar % de Contribución ---
481
  detailed_anova_table['% Contribución'] = (detailed_anova_table['Suma de Cuadrados'] / ss_total) * 100
482
 
@@ -487,8 +487,8 @@ class RSM_BoxBehnken:
487
 
488
  def get_all_tables(self):
489
  """
490
- Obtiene todas las tablas generadas para ser exportadas a Excel.
491
- """
492
  prediction_table = self.generate_prediction_table()
493
  anova_table = self.calculate_anova_table()
494
 
@@ -499,8 +499,8 @@ class RSM_BoxBehnken:
499
 
500
  def save_figures_to_zip(self):
501
  """
502
- Guarda todas las figuras almacenadas en self.all_figures a un archivo ZIP en memoria.
503
- """
504
  if not self.all_figures:
505
  return None
506
 
@@ -520,14 +520,14 @@ class RSM_BoxBehnken:
520
 
521
  def save_fig_to_bytes(self, fig):
522
  """
523
- Convierte una figura Plotly a bytes en formato PNG.
524
- """
525
  return fig.to_image(format="png")
526
 
527
  def save_all_figures_png(self):
528
  """
529
- Guarda todas las figuras en archivos PNG temporales y retorna las rutas.
530
- """
531
  png_paths = []
532
  for idx, fig in enumerate(self.all_figures, start=1):
533
  img_bytes = fig.to_image(format="png")
@@ -539,8 +539,8 @@ class RSM_BoxBehnken:
539
 
540
  def save_tables_to_excel(self):
541
  """
542
- Guarda todas las tablas en un archivo Excel con múltiples hojas y retorna la ruta del archivo.
543
- """
544
  tables = self.get_all_tables()
545
  excel_buffer = io.BytesIO()
546
  with pd.ExcelWriter(excel_buffer, engine='xlsxwriter') as writer:
@@ -558,8 +558,8 @@ class RSM_BoxBehnken:
558
 
559
  def export_tables_to_word(self, tables_dict):
560
  """
561
- Exporta las tablas proporcionadas a un documento de Word.
562
- """
563
  if not tables_dict:
564
  return None
565
 
@@ -615,8 +615,8 @@ class RSM_BoxBehnken:
615
 
616
  def load_data(x1_name, x2_name, x3_name, y_name, x1_levels_str, x2_levels_str, x3_levels_str, data_str):
617
  """
618
- Carga los datos del diseño Box-Behnken desde cajas de texto y crea la instancia de RSM_BoxBehnken.
619
- """
620
  try:
621
  # Convertir los niveles a listas de números
622
  x1_levels = [float(x.strip()) for x in x1_levels_str.split(',')]
 
21
  class RSM_BoxBehnken:
22
  def __init__(self, data, x1_name, x2_name, x3_name, y_name, x1_levels, x2_levels, x3_levels):
23
  """
24
+ Inicializa la clase con los datos del diseño Box-Behnken.
25
+ """
26
  self.data = data.copy()
27
  self.model = None
28
  self.model_simplified = None
 
41
 
42
  def get_levels(self, variable_name):
43
  """
44
+ Obtiene los niveles para una variable específica.
45
+ """
46
  if variable_name == self.x1_name:
47
  return self.x1_levels
48
  elif variable_name == self.x2_name:
 
54
 
55
  def fit_model(self):
56
  """
57
+ Ajusta el modelo de segundo orden completo a los datos.
58
+ """
59
  formula = f'{self.y_name} ~ {self.x1_name} + {self.x2_name} + {self.x3_name} + ' \
60
  f'I({self.x1_name}**2) + I({self.x2_name}**2) + I({self.x3_name}**2) + ' \
61
  f'{self.x1_name}:{self.x2_name} + {self.x1_name}:{self.x3_name} + {self.x2_name}:{self.x3_name}'
 
66
 
67
  def fit_simplified_model(self):
68
  """
69
+ Ajusta el modelo de segundo orden a los datos, eliminando términos no significativos.
70
+ """
71
  formula = f'{self.y_name} ~ {self.x1_name} + {self.x2_name} + ' \
72
  f'I({self.x1_name}**2) + I({self.x2_name}**2) + I({self.x3_name}**2)'
73
  self.model_simplified = smf.ols(formula, data=self.data).fit()
74
  print("\nModelo Simplificado:")
75
  print(self.model_simplified.summary())
76
  return self.model_simplified, self.pareto_chart_f(self.model_simplified, "Pareto - Modelo Simplificado (F)"), self.pareto_chart_t(self.model_simplified, "Pareto - Modelo Simplificado (t)")
77
+
78
  def optimize(self, method='Nelder-Mead'):
79
  """
80
+ Encuentra los niveles óptimos de los factores para maximizar la respuesta usando el modelo simplificado.
81
+ """
82
  if self.model_simplified is None:
83
  print("Error: Ajusta el modelo simplificado primero.")
84
  return
 
113
 
114
  def plot_rsm_individual(self, fixed_variable, fixed_level):
115
  """
116
+ Genera un gráfico de superficie de respuesta (RSM) individual para una configuración específica.
117
+ """
118
  if self.model_simplified is None:
119
  print("Error: Ajusta el modelo simplificado primero.")
120
  return None
121
 
122
  # Determinar las variables que varían y sus niveles naturales
123
  varying_variables = [var for var in [self.x1_name, self.x2_name, self.x3_name] if var != fixed_variable]
124
+
125
  # Establecer los niveles naturales para las variables que varían
126
  x_natural_levels = self.get_levels(varying_variables[0])
127
  y_natural_levels = self.get_levels(varying_variables[1])
 
220
 
221
  def get_units(self, variable_name):
222
  """
223
+ Define las unidades de las variables para etiquetas.
224
+ Puedes personalizar este método según tus necesidades.
225
+ """
226
  units = {
227
  'Glucosa': 'g/L',
228
  'Extracto_de_Levadura': 'g/L',
 
233
 
234
  def generate_all_plots(self):
235
  """
236
+ Genera todas las gráficas de RSM, variando la variable fija y sus niveles usando el modelo simplificado.
237
+ Almacena las figuras en self.all_figures.
238
+ """
239
  if self.model_simplified is None:
240
  print("Error: Ajusta el modelo simplificado primero.")
241
  return
 
268
 
269
  def pareto_chart_f(self, model, title):
270
  """
271
+ Genera un diagrama de Pareto para los efectos usando estadísticos F,
272
+ incluyendo la línea de significancia.
273
+ """
274
  # Calcular los estadísticos F para cada término manualmente
275
  # Necesitamos los coeficientes, los errores estándar y los grados de libertad del error
276
  coef = model.params[1:] # Excluir la Intercept
277
+ stderr = model.bse[1:] # Excluir la Intercept
278
  df_resid = model.df_resid
279
+
280
  # Calcular F = (coef / stderr)^2
281
  fvalues = (coef / stderr) ** 2
282
+
283
  abs_fvalues = np.abs(fvalues)
284
  sorted_idx = np.argsort(abs_fvalues)[::-1]
285
  sorted_fvalues = abs_fvalues[sorted_idx]
286
  sorted_names = fvalues.index[sorted_idx]
287
+
288
  # Cambiar I() por "" en los nombres de los terminos
289
+ sorted_names = sorted_names.str.replace(r'I\(', '', regex=True).str.replace(r'\*\*2\)', '^2', regex=True).str.replace(r' \* ', '·', regex=True)
290
 
291
  # Calcular el valor crítico de F para la línea de significancia
292
  alpha = 0.05 # Nivel de significancia
 
306
 
307
  # Agregar la línea de significancia
308
  fig.add_vline(x=f_critical, line_dash="dot",
309
+ annotation_text=f"F crítico = {f_critical:.3f}",
310
+ annotation_position="bottom right")
311
 
312
  return fig
313
+
314
  def pareto_chart_t(self, model, title):
315
  """
316
+ Genera un diagrama de Pareto para los efectos usando estadísticos t,
317
+ incluyendo la línea de significancia.
318
+ """
319
  # Calcular los estadísticos t para cada término
320
  tvalues = model.tvalues[1:] # Excluir la Intercept
321
  abs_tvalues = np.abs(tvalues)
 
324
  sorted_names = tvalues.index[sorted_idx]
325
 
326
  # Cambiar I() por "" en los nombres de los terminos
327
+ sorted_names = sorted_names.str.replace(r'I\(', '', regex=True).str.replace(r'\*\*2\)', '^2', regex=True).str.replace(r' \* ', '·', regex=True)
328
 
329
  # Calcular el valor crítico de t para la línea de significancia
330
  alpha = 0.05 # Nivel de significancia
 
350
 
351
  def get_simplified_equation(self):
352
  """
353
+ Retorna la ecuación del modelo simplificado como una cadena de texto.
354
+ """
355
  if self.model_simplified is None:
356
  print("Error: Ajusta el modelo simplificado primero.")
357
  return None
 
375
  equation += f" + {coef:.3f}*{self.x3_name}^2"
376
 
377
  return equation
378
+
379
  def generate_prediction_table(self):
380
  """
381
+ Genera una tabla con los valores actuales, predichos y residuales.
382
+ """
383
  if self.model_simplified is None:
384
  print("Error: Ajusta el modelo simplificado primero.")
385
  return None
 
391
 
392
  def calculate_anova_table(self):
393
  """
394
+ Calcula la tabla ANOVA detallada, incluyendo la contribución porcentual.
395
+ """
396
  if self.model_simplified is None:
397
  print("Error: Ajusta el modelo simplificado primero.")
398
  return None
 
444
  # 11. Estadísticos F y valores p
445
  f_regression = ms_regression / ms_residual
446
  p_regression = 1 - f.cdf(f_regression, df_regression, df_residual)
447
+
448
  f_lack_of_fit = ms_lack_of_fit / ms_pure_error if not np.isnan(ms_lack_of_fit) else np.nan
449
  p_lack_of_fit = 1 - f.cdf(f_lack_of_fit, df_lack_of_fit, df_pure_error) if not np.isnan(f_lack_of_fit) else np.nan
450
 
 
457
  'F': [f_regression, np.nan, f_lack_of_fit, np.nan, np.nan],
458
  'Valor p': [p_regression, np.nan, p_lack_of_fit, np.nan, np.nan]
459
  })
460
+
461
  # Calcular la suma de cuadrados y estadísticos F para la curvatura
462
  ss_curvature = anova_reduced['sum_sq'][f'I({self.x1_name} ** 2)'] + \
463
+ anova_reduced['sum_sq'][f'I({self.x2_name} ** 2)'] + \
464
+ anova_reduced['sum_sq'][f'I({self.x3_name} ** 2)']
465
  df_curvature = 3
466
  ms_curvature = ss_curvature / df_curvature
467
  f_curvature = ms_curvature / ms_residual
 
469
 
470
  # Añadir la fila de curvatura a la tabla ANOVA
471
  detailed_anova_table.loc[len(detailed_anova_table)] = [
472
+ 'Curvatura',
473
+ ss_curvature,
474
+ df_curvature,
475
  ms_curvature,
476
  f_curvature,
477
  p_curvature
478
  ]
479
+
480
  # --- Agregar % de Contribución ---
481
  detailed_anova_table['% Contribución'] = (detailed_anova_table['Suma de Cuadrados'] / ss_total) * 100
482
 
 
487
 
488
  def get_all_tables(self):
489
  """
490
+ Obtiene todas las tablas generadas para ser exportadas a Excel.
491
+ """
492
  prediction_table = self.generate_prediction_table()
493
  anova_table = self.calculate_anova_table()
494
 
 
499
 
500
  def save_figures_to_zip(self):
501
  """
502
+ Guarda todas las figuras almacenadas en self.all_figures a un archivo ZIP en memoria.
503
+ """
504
  if not self.all_figures:
505
  return None
506
 
 
520
 
521
  def save_fig_to_bytes(self, fig):
522
  """
523
+ Convierte una figura Plotly a bytes en formato PNG.
524
+ """
525
  return fig.to_image(format="png")
526
 
527
  def save_all_figures_png(self):
528
  """
529
+ Guarda todas las figuras en archivos PNG temporales y retorna las rutas.
530
+ """
531
  png_paths = []
532
  for idx, fig in enumerate(self.all_figures, start=1):
533
  img_bytes = fig.to_image(format="png")
 
539
 
540
  def save_tables_to_excel(self):
541
  """
542
+ Guarda todas las tablas en un archivo Excel con múltiples hojas y retorna la ruta del archivo.
543
+ """
544
  tables = self.get_all_tables()
545
  excel_buffer = io.BytesIO()
546
  with pd.ExcelWriter(excel_buffer, engine='xlsxwriter') as writer:
 
558
 
559
  def export_tables_to_word(self, tables_dict):
560
  """
561
+ Exporta las tablas proporcionadas a un documento de Word.
562
+ """
563
  if not tables_dict:
564
  return None
565
 
 
615
 
616
  def load_data(x1_name, x2_name, x3_name, y_name, x1_levels_str, x2_levels_str, x3_levels_str, data_str):
617
  """
618
+ Carga los datos del diseño Box-Behnken desde cajas de texto y crea la instancia de RSM_BoxBehnken.
619
+ """
620
  try:
621
  # Convertir los niveles a listas de números
622
  x1_levels = [float(x.strip()) for x in x1_levels_str.split(',')]