C2MV commited on
Commit
854197f
·
verified ·
1 Parent(s): 71817ec

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +342 -73
app.py CHANGED
@@ -10,7 +10,305 @@ from scipy.stats import t
10
  import gradio as gr
11
 
12
  class RSM_BoxBehnken:
13
- # ... (Tu código de la clase RSM_BoxBehnken se mantiene igual) ...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
 
15
  # Crear un DataFrame a partir de la tabla
16
  data = pd.DataFrame({
@@ -24,84 +322,55 @@ data = pd.DataFrame({
24
  # Crear una instancia de la clase RSM_BoxBehnken
25
  rsm = RSM_BoxBehnken(data)
26
 
27
- # --- Funciones para la interfaz de Gradio ---
 
28
 
29
- def fit_full_model():
30
- rsm.fit_model()
31
- return "Modelo completo ajustado. Revisa la consola para ver el resumen."
32
 
33
- def fit_simplified_model():
34
- rsm.fit_simplified_model()
35
- return "Modelo simplificado ajustado. Revisa la consola para ver el resumen."
36
 
37
- def optimize_model(method):
38
- rsm.optimize(method)
39
- return (f"Optimización realizada con {method}. Revisa la consola para ver los niveles óptimos.\n"
40
- f"Niveles óptimos (codificados): {rsm.optimal_levels}\n"
41
- f"Valor máximo de {rsm.y_name}: {-rsm.optimized_results.fun:.4f}")
42
 
43
- def generate_plot(fixed_variable, fixed_level_natural):
44
- fig = rsm.plot_rsm_individual(fixed_variable, fixed_level_natural)
45
- if fig is not None:
46
- return fig
47
- else:
48
- return "Ajusta el modelo simplificado primero."
 
 
 
49
 
50
- # --- Creación de la interfaz de Gradio ---
 
 
51
 
 
52
  with gr.Blocks() as demo:
53
- gr.Markdown("# Análisis de Superficie de Respuesta (RSM) - Diseño Box-Behnken")
54
-
55
- with gr.Tab("Ajuste de Modelos"):
56
- with gr.Row():
57
- full_model_button = gr.Button("Ajustar Modelo Completo")
58
- simplified_model_button = gr.Button("Ajustar Modelo Simplificado")
59
- model_output = gr.Textbox(label="Resultado del Ajuste")
60
-
61
- full_model_button.click(fn=fit_full_model, outputs=model_output)
62
- simplified_model_button.click(fn=fit_simplified_model, outputs=model_output)
63
-
64
- with gr.Tab("Optimización"):
65
- method_dropdown = gr.Dropdown(
66
- choices=['Nelder-Mead', 'Powell', 'BFGS'],
67
- value='Nelder-Mead',
68
- label="Método de Optimización"
69
- )
70
- optimize_button = gr.Button("Optimizar")
71
- optimization_output = gr.Textbox(label="Resultado de la Optimización")
72
-
73
- optimize_button.click(fn=optimize_model, inputs=method_dropdown, outputs=optimization_output)
74
-
75
- with gr.Tab("Gráficos de Superficie de Respuesta"):
76
- with gr.Row():
77
- fixed_variable_dropdown = gr.Dropdown(
78
- choices=[rsm.x1_name, rsm.x2_name, rsm.x3_name],
79
- value=rsm.x1_name,
80
- label="Variable Fija"
81
- )
82
- fixed_level_slider = gr.Slider(
83
- minimum=min(rsm.get_levels(rsm.x1_name)),
84
- maximum=max(rsm.get_levels(rsm.x1_name)),
85
- step=0.01,
86
- value=rsm.get_levels(rsm.x1_name)[1],
87
- label="Nivel de Variable Fija (Natural)"
88
- )
89
- plot_button = gr.Button("Generar Gráfico")
90
- plot_output = gr.Plot(label="Gráfico RSM")
91
-
92
- def update_slider_range(fixed_variable):
93
- levels = rsm.get_levels(fixed_variable)
94
- return gr.Slider.update(minimum=min(levels), maximum=max(levels), value=levels[1])
95
-
96
- fixed_variable_dropdown.change(
97
- fn=update_slider_range,
98
- inputs=fixed_variable_dropdown,
99
- outputs=fixed_level_slider
100
- )
101
- plot_button.click(
102
- fn=generate_plot,
103
- inputs=[fixed_variable_dropdown, fixed_level_slider],
104
- outputs=plot_output
105
- )
106
 
107
  demo.launch()
 
10
  import gradio as gr
11
 
12
  class RSM_BoxBehnken:
13
+ # ... (El código de tu clase RSM_BoxBehnken se mantiene igual) ...
14
+ def __init__(self, data):
15
+ """
16
+ Inicializa la clase con los datos del diseño Box-Behnken.
17
+
18
+ Args:
19
+ data (pd.DataFrame): DataFrame con los datos del experimento.
20
+ """
21
+ self.data = data.copy()
22
+ self.data.rename(columns={
23
+ 'Glucosa': 'Glucosa',
24
+ 'Extracto de Levadura': 'Extracto_de_Levadura',
25
+ 'Triptófano': 'Triptofano',
26
+ 'AIA (ppm)': 'AIA_ppm'
27
+ }, inplace=True)
28
+
29
+ self.model = None
30
+ self.model_simplified = None
31
+ self.optimized_results = None
32
+ self.optimal_levels = None
33
+
34
+ self.x1_name = 'Glucosa'
35
+ self.x2_name = 'Extracto_de_Levadura'
36
+ self.x3_name = 'Triptofano'
37
+ self.y_name = 'AIA_ppm'
38
+
39
+ # Niveles originales de las variables
40
+ self.x1_levels = [1, 3.5, 5.5] # Glucosa
41
+ self.x2_levels = [0.03, 0.2, 0.3] # Extracto de Levadura
42
+ self.x3_levels = [0.4, 0.65, 0.9] # Triptófano
43
+
44
+ def get_levels(self, variable_name):
45
+ """
46
+ Obtiene los niveles para una variable específica.
47
+
48
+ Args:
49
+ variable_name (str): Nombre de la variable.
50
+
51
+ Returns:
52
+ list: Niveles de la variable.
53
+ """
54
+ if variable_name == self.x1_name:
55
+ return self.x1_levels
56
+ elif variable_name == self.x2_name:
57
+ return self.x2_levels
58
+ elif variable_name == self.x3_name:
59
+ return self.x3_levels
60
+ else:
61
+ raise ValueError(f"Variable desconocida: {variable_name}")
62
+
63
+ def fit_model(self):
64
+ """
65
+ Ajusta el modelo de segundo orden completo a los datos.
66
+ """
67
+ formula = f'{self.y_name} ~ {self.x1_name} + {self.x2_name} + {self.x3_name} + ' \
68
+ f'I({self.x1_name}**2) + I({self.x2_name}**2) + I({self.x3_name}**2) + ' \
69
+ f'{self.x1_name}:{self.x2_name} + {self.x1_name}:{self.x3_name} + {self.x2_name}:{self.x3_name}'
70
+ self.model = smf.ols(formula, data=self.data).fit()
71
+ print("Modelo Completo:")
72
+ print(self.model.summary())
73
+ self.pareto_chart(self.model, "Pareto - Modelo Completo")
74
+
75
+ def fit_simplified_model(self):
76
+ """
77
+ Ajusta el modelo de segundo orden a los datos, eliminando términos no significativos.
78
+ """
79
+ formula = f'{self.y_name} ~ {self.x1_name} + {self.x2_name} + ' \
80
+ f'I({self.x1_name}**2) + I({self.x2_name}**2) + I({self.x3_name}**2)'
81
+ self.model_simplified = smf.ols(formula, data=self.data).fit()
82
+ print("\nModelo Simplificado:")
83
+ print(self.model_simplified.summary())
84
+ self.pareto_chart(self.model_simplified, "Pareto - Modelo Simplificado")
85
+
86
+ def optimize(self, method='Nelder-Mead'):
87
+ """
88
+ Encuentra los niveles óptimos de los factores para maximizar la respuesta usando el modelo simplificado.
89
+
90
+ Args:
91
+ method (str): Método de optimización a utilizar (por defecto, 'Nelder-Mead').
92
+ """
93
+ if self.model_simplified is None:
94
+ print("Error: Ajusta el modelo simplificado primero.")
95
+ return
96
+
97
+ def objective_function(x):
98
+ return -self.model_simplified.predict(pd.DataFrame({self.x1_name: [x[0]], self.x2_name: [x[1]], self.x3_name: [x[2]]}))
99
+
100
+ bounds = [(-1, 1), (-1, 1), (-1, 1)]
101
+ x0 = [0, 0, 0]
102
+
103
+ self.optimized_results = minimize(objective_function, x0, method=method, bounds=bounds)
104
+ self.optimal_levels = self.optimized_results.x
105
+
106
+ # Convertir niveles óptimos de codificados a naturales
107
+ optimal_levels_natural = [
108
+ self.coded_to_natural(self.optimal_levels[0], self.x1_name),
109
+ self.coded_to_natural(self.optimal_levels[1], self.x2_name),
110
+ self.coded_to_natural(self.optimal_levels[2], self.x3_name)
111
+ ]
112
+
113
+ print(f"\nNiveles óptimos encontrados (basado en modelo simplificado):")
114
+ print(f"{self.x1_name}: {optimal_levels_natural[0]:.4f} g/L")
115
+ print(f"{self.x2_name}: {optimal_levels_natural[1]:.4f} g/L")
116
+ print(f"{self.x3_name}: {optimal_levels_natural[2]:.4f} g/L")
117
+ print(f"Valor máximo de {self.y_name}: {-self.optimized_results.fun:.4f}")
118
+
119
+ def plot_rsm_individual(self, fixed_variable, fixed_level):
120
+ """
121
+ Genera un gráfico de superficie de respuesta (RSM) individual para una configuración específica.
122
+
123
+ Args:
124
+ fixed_variable (str): Nombre de la variable a mantener fija.
125
+ fixed_level (float): Nivel al que se fija la variable (en unidades naturales).
126
+
127
+ Returns:
128
+ go.Figure: Objeto de figura de Plotly.
129
+ """
130
+ if self.model_simplified is None:
131
+ print("Error: Ajusta el modelo simplificado primero.")
132
+ return None
133
+
134
+ # Determinar las variables que varían y sus niveles naturales
135
+ varying_variables = [var for var in [self.x1_name, self.x2_name, self.x3_name] if var != fixed_variable]
136
+
137
+ # Establecer los niveles naturales para las variables que varían
138
+ x_natural_levels = self.get_levels(varying_variables[0])
139
+ y_natural_levels = self.get_levels(varying_variables[1])
140
+
141
+ # Crear una malla de puntos para las variables que varían (en unidades naturales)
142
+ x_range_natural = np.linspace(x_natural_levels[0], x_natural_levels[-1], 100)
143
+ y_range_natural = np.linspace(y_natural_levels[0], y_natural_levels[-1], 100)
144
+ x_grid_natural, y_grid_natural = np.meshgrid(x_range_natural, y_range_natural)
145
+
146
+ # Convertir la malla de variables naturales a codificadas
147
+ x_grid_coded = self.natural_to_coded(x_grid_natural, varying_variables[0])
148
+ y_grid_coded = self.natural_to_coded(y_grid_natural, varying_variables[1])
149
+
150
+ # Crear un DataFrame para la predicción con variables codificadas
151
+ prediction_data = pd.DataFrame({
152
+ varying_variables[0]: x_grid_coded.flatten(),
153
+ varying_variables[1]: y_grid_coded.flatten(),
154
+ })
155
+ prediction_data[fixed_variable] = self.natural_to_coded(fixed_level, fixed_variable)
156
+
157
+ # Calcular los valores predichos
158
+ z_pred = self.model_simplified.predict(prediction_data).values.reshape(x_grid_coded.shape)
159
+
160
+ # 1. Identificar los dos factores que varían
161
+ varying_variables = [var for var in [self.x1_name, self.x2_name, self.x3_name] if var != fixed_variable]
162
+
163
+ # 2. Filtrar por el nivel de la variable fija (en codificado)
164
+ fixed_level_coded = self.natural_to_coded(fixed_level, fixed_variable)
165
+ subset_data = self.data[np.isclose(self.data[fixed_variable], fixed_level_coded)]
166
+
167
+ # 3. Filtrar por niveles válidos en las variables que varían
168
+ valid_levels = [-1, 0, 1]
169
+ experiments_data = subset_data[
170
+ subset_data[varying_variables[0]].isin(valid_levels) &
171
+ subset_data[varying_variables[1]].isin(valid_levels)
172
+ ]
173
+
174
+ # Convertir coordenadas de experimentos a naturales
175
+ experiments_x_natural = experiments_data[varying_variables[0]].apply(lambda x: self.coded_to_natural(x, varying_variables[0]))
176
+ experiments_y_natural = experiments_data[varying_variables[1]].apply(lambda x: self.coded_to_natural(x, varying_variables[1]))
177
+
178
+ # Crear el gráfico de superficie con variables naturales en los ejes y transparencia
179
+ fig = go.Figure(data=[go.Surface(z=z_pred, x=x_grid_natural, y=y_grid_natural, colorscale='Viridis', opacity=0.7, showscale=True)])
180
+
181
+ # --- Añadir cuadrícula a la superficie ---
182
+ # Líneas en la dirección x
183
+ for i in range(x_grid_natural.shape[0]):
184
+ fig.add_trace(go.Scatter3d(
185
+ x=x_grid_natural[i, :],
186
+ y=y_grid_natural[i, :],
187
+ z=z_pred[i, :],
188
+ mode='lines',
189
+ line=dict(color='gray', width=2),
190
+ showlegend=False,
191
+ hoverinfo='skip'
192
+ ))
193
+ # Líneas en la dirección y
194
+ for j in range(x_grid_natural.shape[1]):
195
+ fig.add_trace(go.Scatter3d(
196
+ x=x_grid_natural[:, j],
197
+ y=y_grid_natural[:, j],
198
+ z=z_pred[:, j],
199
+ mode='lines',
200
+ line=dict(color='gray', width=2),
201
+ showlegend=False,
202
+ hoverinfo='skip'
203
+ ))
204
+
205
+ # --- Fin de la adición de la cuadrícula ---
206
+
207
+ # Añadir los puntos de los experimentos en la superficie de respuesta con diferentes colores y etiquetas
208
+ # Crear una lista de colores y etiquetas para los puntos
209
+ colors = ['red', 'blue', 'green', 'purple', 'orange', 'yellow', 'cyan', 'magenta']
210
+ point_labels = []
211
+ for i, row in experiments_data.iterrows():
212
+ point_labels.append(f"{row[self.y_name]:.2f}")
213
+
214
+ fig.add_trace(go.Scatter3d(
215
+ x=experiments_x_natural,
216
+ y=experiments_y_natural,
217
+ z=experiments_data[self.y_name],
218
+ mode='markers+text',
219
+ marker=dict(size=4, color=colors[:len(experiments_x_natural)]), # Usar colores de la lista
220
+ text=point_labels, # Usar las etiquetas creadas
221
+ textposition='top center',
222
+ name='Experimentos'
223
+ ))
224
+
225
+ # Añadir etiquetas y título con variables naturales
226
+ fig.update_layout(
227
+ scene=dict(
228
+ xaxis_title=varying_variables[0] + " (g/L)",
229
+ yaxis_title=varying_variables[1] + " (g/L)",
230
+ zaxis_title=self.y_name,
231
+ # Puedes mantener la configuración de grid en los planos si lo deseas
232
+ # xaxis=dict(showgrid=True, gridwidth=1, gridcolor='lightgray'),
233
+ # yaxis=dict(showgrid=True, gridwidth=1, gridcolor='lightgray'),
234
+ # zaxis=dict(showgrid=True, gridwidth=1, gridcolor='lightgray')
235
+ ),
236
+ title=f"{self.y_name} vs {varying_variables[0]} y {varying_variables[1]}<br><sup>{fixed_variable} fijo en {fixed_level:.2f} (g/L) (Modelo Simplificado)</sup>",
237
+ height=800,
238
+ width=1000,
239
+ showlegend=True
240
+ )
241
+ return fig
242
+
243
+ def generate_all_plots(self):
244
+ """
245
+ Genera todas las gráficas de RSM, variando la variable fija y sus niveles usando el modelo simplificado.
246
+ """
247
+ if self.model_simplified is None:
248
+ print("Error: Ajusta el modelo simplificado primero.")
249
+ return
250
+
251
+ # Niveles naturales para graficar
252
+ levels_to_plot_natural = {
253
+ self.x1_name: self.x1_levels,
254
+ self.x2_name: self.x2_levels,
255
+ self.x3_name: self.x3_levels
256
+ }
257
+
258
+ # Generar y mostrar gráficos individuales
259
+ for fixed_variable in [self.x1_name, self.x2_name, self.x3_name]:
260
+ for level in levels_to_plot_natural[fixed_variable]:
261
+ fig = self.plot_rsm_individual(fixed_variable, level)
262
+ if fig is not None:
263
+ fig.show()
264
+
265
+ def coded_to_natural(self, coded_value, variable_name):
266
+ """Convierte un valor codificado a su valor natural."""
267
+ levels = self.get_levels(variable_name)
268
+ return levels[0] + (coded_value + 1) * (levels[-1] - levels[0]) / 2
269
+
270
+ def natural_to_coded(self, natural_value, variable_name):
271
+ """Convierte un valor natural a su valor codificado."""
272
+ levels = self.get_levels(variable_name)
273
+ return -1 + 2 * (natural_value - levels[0]) / (levels[-1] - levels[0])
274
+
275
+ def pareto_chart(self, model, title):
276
+ """
277
+ Genera un diagrama de Pareto para los efectos estandarizados de un modelo,
278
+ incluyendo la línea de significancia.
279
+
280
+ Args:
281
+ model: Modelo ajustado de statsmodels.
282
+ title (str): Título del gráfico.
283
+ """
284
+ # Calcular los efectos estandarizados
285
+ tvalues = model.tvalues[1:] # Excluir la Intercept
286
+ abs_tvalues = np.abs(tvalues)
287
+ sorted_idx = np.argsort(abs_tvalues)[::-1]
288
+ sorted_tvalues = abs_tvalues[sorted_idx]
289
+ sorted_names = tvalues.index[sorted_idx]
290
+
291
+ # Calcular el valor crítico de t para la línea de significancia
292
+ alpha = 0.05 # Nivel de significancia
293
+ dof = model.df_resid # Grados de libertad residuales
294
+ t_critical = t.ppf(1 - alpha / 2, dof)
295
+
296
+ # Crear el diagrama de Pareto
297
+ fig = px.bar(
298
+ x=sorted_tvalues,
299
+ y=sorted_names,
300
+ orientation='h',
301
+ labels={'x': 'Efecto Estandarizado', 'y': 'Término'},
302
+ title=title
303
+ )
304
+ fig.update_yaxes(autorange="reversed")
305
+
306
+ # Agregar la línea de significancia
307
+ fig.add_vline(x=t_critical, line_dash="dot",
308
+ annotation_text=f"t crítico = {t_critical:.2f}",
309
+ annotation_position="bottom right")
310
+
311
+ return fig
312
 
313
  # Crear un DataFrame a partir de la tabla
314
  data = pd.DataFrame({
 
322
  # Crear una instancia de la clase RSM_BoxBehnken
323
  rsm = RSM_BoxBehnken(data)
324
 
325
+ # Ajustar el modelo completo y generar el diagrama de Pareto
326
+ rsm.fit_model()
327
 
328
+ # Ajustar el modelo simplificado y generar el diagrama de Pareto
329
+ rsm.fit_simplified_model()
 
330
 
331
+ # Optimizar para encontrar los niveles óptimos (basado en el modelo simplificado)
332
+ rsm.optimize()
 
333
 
334
+ # Generar gráficos individuales de superficie de respuesta (9 en total)
335
+ #rsm.generate_all_plots()
 
 
 
336
 
337
+ # --- Interfaz de Gradio ---
338
+
339
+ def fit_and_optimize_model():
340
+ rsm.fit_model()
341
+ rsm.fit_simplified_model()
342
+ rsm.optimize()
343
+ model_summary = rsm.model_simplified.summary().as_html()
344
+ pareto_fig = rsm.pareto_chart(rsm.model_simplified, "Pareto - Modelo Simplificado")
345
+ return model_summary, pareto_fig, f"{rsm.x1_name}: {rsm.optimal_levels[0]:.4f} g/L, {rsm.x2_name}: {rsm.optimal_levels[1]:.4f} g/L, {rsm.x3_name}: {rsm.optimal_levels[2]:.4f} g/L, Valor máximo de {rsm.y_name}: {-rsm.optimized_results.fun:.4f}"
346
 
347
+ def generate_rsm_plot(fixed_variable, fixed_level):
348
+ fig = rsm.plot_rsm_individual(fixed_variable, fixed_level)
349
+ return fig
350
 
351
+ # Crear la interfaz de Gradio
352
  with gr.Blocks() as demo:
353
+ gr.Markdown("# Optimización de la producción de AIA usando RSM Box-Behnken")
354
+ with gr.Row():
355
+ with gr.Column():
356
+ fit_button = gr.Button("Ajustar Modelo y Optimizar")
357
+ model_summary_output = gr.HTML()
358
+ pareto_chart_output = gr.Plot()
359
+ optimization_results_output = gr.Textbox(label="Resultados de la Optimización")
360
+ with gr.Column():
361
+ gr.Markdown("## Generar Gráficos de Superficie de Respuesta")
362
+ fixed_variable_input = gr.Dropdown(label="Variable Fija", choices=[rsm.x1_name, rsm.x2_name, rsm.x3_name], value=rsm.x1_name)
363
+ fixed_level_input = gr.Slider(label="Nivel de Variable Fija", minimum=rsm.x1_levels[0], maximum=rsm.x1_levels[-1], step=0.01, value=rsm.x1_levels[1])
364
+ plot_button = gr.Button("Generar Gráfico")
365
+ rsm_plot_output = gr.Plot()
366
+
367
+ fit_button.click(fit_and_optimize_model, inputs=[], outputs=[model_summary_output, pareto_chart_output, optimization_results_output])
368
+ plot_button.click(generate_rsm_plot, inputs=[fixed_variable_input, fixed_level_input], outputs=rsm_plot_output)
369
+
370
+ # Ejemplo de uso
371
+ gr.Markdown("## Ejemplo de uso")
372
+ gr.Markdown("1. Haz clic en 'Ajustar Modelo y Optimizar' para ajustar el modelo y encontrar los niveles óptimos de los factores.")
373
+ gr.Markdown("2. Selecciona una variable fija y su nivel en los controles deslizantes.")
374
+ gr.Markdown("3. Haz clic en 'Generar Gráfico' para generar un gráfico de superficie de respuesta.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
375
 
376
  demo.launch()