Spaces:
Runtime error
Runtime error
File size: 21,604 Bytes
e0fbfa8 71817ec f340ee7 71817ec 519b16c da8be82 47688e3 65cf050 32ffd0d 71817ec 5c5ea4a 32ffd0d 71817ec 550ed22 65cf050 854197f c281624 854197f c281624 550ed22 854197f 23ece77 550ed22 406b47b c281624 4b95828 c281624 4b95828 c281624 4b95828 c281624 4b95828 854197f 23ece77 854197f c281624 23ece77 854197f c281624 23ece77 47688e3 23ece77 c281624 23ece77 32ffd0d 550ed22 c281624 854197f 65cf050 550ed22 c281624 629ec27 f340ee7 c281624 f340ee7 10fce6d c281624 10fce6d f340ee7 10fce6d dd3c2a8 1c36a23 fa99217 1c36a23 fa99217 1c36a23 fa99217 1c36a23 fa99217 1c36a23 519b16c c281624 e0fbfa8 65cf050 c281624 10fce6d 65cf050 c281624 65cf050 c281624 4b95828 65cf050 10fce6d c281624 10fce6d c281624 65cf050 c281624 dd3c2a8 c281624 65cf050 c281624 10fce6d 09fa6fe c281624 10fce6d c281624 629ec27 dd3c2a8 c281624 dd3c2a8 629ec27 c281624 32ffd0d 629ec27 4b95828 550ed22 32ffd0d c281624 10fce6d 32ffd0d c281624 10fce6d 32ffd0d 10fce6d 32ffd0d c281624 32ffd0d c281624 32ffd0d c281624 32ffd0d 10fce6d 32ffd0d c281624 32ffd0d c281624 10fce6d c281624 32ffd0d c281624 32ffd0d 10fce6d c281624 32ffd0d c281624 32ffd0d 10fce6d c281624 32ffd0d c281624 32ffd0d c281624 32ffd0d c281624 32ffd0d 10fce6d c281624 32ffd0d c281624 32ffd0d c281624 32ffd0d 10fce6d 32ffd0d c281624 32ffd0d 10fce6d 32ffd0d c281624 32ffd0d 10fce6d 32ffd0d 10fce6d 32ffd0d 10fce6d 32ffd0d c281624 32ffd0d 10fce6d c281624 32ffd0d 10fce6d |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 |
import numpy as np
import pandas as pd
import statsmodels.formula.api as smf
import statsmodels.api as sm
import plotly.graph_objects as go
from scipy.optimize import minimize
import plotly.express as px
from scipy.stats import t, f
import gradio as gr
import io
import zipfile
import tempfile
from datetime import datetime
import docx
from docx.shared import Inches, Pt
from docx.enum.text import WD_PARAGRAPH_ALIGNMENT
from matplotlib.colors import to_hex
import os
# --- Data definition in global scope ---
data_dict = {
'Tratamiento': ['T1', 'T2', 'T3', 'T4', 'T5', 'T6', 'T7', 'T8', 'T9', 'T10', 'T11', 'T12', 'T13', 'T14', 'T15'] * 3,
'Tiempo_fermentacion_h': [16] * 15 + [23] * 15 + [40] * 15,
'pH': [6.02, 5.39, 6.27, 4.82, 6.25, 4.87, 4.76, 4.68, 4.64, 6.35, 4.67, 6.43, 4.58, 4.60, 6.96,
5.17, 5.95, 6.90, 5.50, 5.08, 4.95, 5.41, 5.52, 4.98, 7.10, 5.36, 6.91, 5.21, 4.66, 7.10,
5.42, 5.60, 7.36, 5.36, 4.66, 4.93, 5.18, 5.26, 4.92, 7.28, 5.26, 6.84, 5.19, 4.58, 7.07],
'Abs_600nm': [1.576, 1.474, 1.293, 1.446, 1.537, 1.415, 1.481, 1.419, 1.321, 1.224, 1.459, 0.345, 1.279, 1.181, 0.662,
1.760, 1.690, 1.485, 1.658, 1.728, 1.594, 1.673, 1.607, 1.531, 1.424, 1.595, 0.344, 1.477, 1.257, 0.660,
1.932, 1.780, 1.689, 1.876, 1.885, 1.824, 1.913, 1.810, 1.852, 1.694, 1.831, 0.347, 1.752, 1.367, 0.656],
'Glucosa_g_L': [5,10,0,5,10,5,10,5,10,0,5,0,5,5,0] * 3,
'Proteina_Pescado_g_L': [1.4,1.4,3.2,3.2,3.2,3.2,3.2,5,5,5,5,0,5,5,0] * 3,
'Sulfato_Manganeso_g_L': [0.75,0.5,0.75,0.5,0.75,0.5,0.75,0.5,0.25,0.75,0.5,0.25,0.5,0.25,0.5] * 3
}
data = pd.DataFrame(data_dict)
# --- End of data definition in global scope ---
# --- Clase RSM_BoxBehnken ---
class RSM_BoxBehnken:
def __init__(self, data, x1_name, x2_name, x3_name, y_name, x1_levels, x2_levels, x3_levels):
"""
Inicializa la clase con los datos del diseño Box-Behnken.
"""
self.data = data.copy()
self.model = None
self.model_simplified = None
self.model_personalized = None # For personalized model
self.optimized_results = None
self.optimal_levels = None
self.all_figures_full = [] # Separate lists for different model plots
self.all_figures_simplified = []
self.all_figures_personalized = []
self.x1_name = x1_name
self.x2_name = x2_name
self.x3_name = x3_name
self.y_name = y_name
# Niveles originales de las variables
self.x1_levels = x1_levels
self.x2_levels = x2_levels
self.x3_levels = x3_levels
# ... (previous methods like get_levels, get_units, coded_to_natural, natural_to_coded, pareto_chart, get_simplified_equation, generate_prediction_table, calculate_contribution_percentage, calculate_detailed_anova, get_all_tables, save_figures_to_zip, save_fig_to_bytes, save_all_figures_png, save_tables_to_excel, export_tables_to_word remain mostly the same)
def fit_personalized_model(self, formula):
"""
Ajusta un modelo personalizado de segundo orden a los datos, usando la formula dada.
"""
self.model_personalized = smf.ols(formula, data=self.data).fit()
print("\nModelo Personalizado:")
print(self.model_personalized.summary())
return self.model_personalized, self.pareto_chart(self.model_personalized, "Pareto - Modelo Personalizado")
def generate_all_plots(self):
"""
Genera todas las gráficas de RSM para todos los modelos.
"""
if self.model_simplified is None:
print("Error: Ajusta el modelo simplificado primero.")
return
self.all_figures_full = [] # Reset lists for each model type
self.all_figures_simplified = []
self.all_figures_personalized = []
levels_to_plot_natural = { # Levels from data, as before
self.x1_name: sorted(list(set(self.data[self.x1_name]))),
self.x2_name: sorted(list(set(self.data[self.x2_name]))),
self.x3_name: sorted(list(set(self.data[self.x3_name])))
}
for fixed_variable in [self.x1_name, self.x2_name, self.x3_name]:
for level in levels_to_plot_natural[fixed_variable]:
fig_full = self.plot_rsm_individual(fixed_variable, level, model_type='full') # Pass model_type
if fig_full:
self.all_figures_full.append(fig_full)
fig_simplified = self.plot_rsm_individual(fixed_variable, level, model_type='simplified') # Pass model_type
if fig_simplified:
self.all_figures_simplified.append(fig_simplified)
if self.model_personalized is not None: # Generate personalized plots only if model exists
fig_personalized = self.plot_rsm_individual(fixed_variable, level, model_type='personalized') # Pass model_type
if fig_personalized:
self.all_figures_personalized.append(fig_personalized)
def plot_rsm_individual(self, fixed_variable, fixed_level, model_type='simplified'): # Added model_type parameter
"""
Genera un gráfico de superficie de respuesta (RSM) individual para una configuración específica y modelo.
"""
model_to_use = self.model_simplified # Default to simplified model
model_title_suffix = "(Modelo Simplificado)"
if model_type == 'full':
model_to_use = self.model
model_title_suffix = "(Modelo Completo)"
elif model_type == 'personalized':
if self.model_personalized is None:
print("Error: Modelo personalizado no ajustado.")
return None
model_to_use = self.model_personalized
model_title_suffix = "(Modelo Personalizado)"
if model_to_use is None: # Use model_to_use instead of self.model_simplified
print(f"Error: Ajusta el modelo {model_type} primero.") # More informative error message
return None
# ... (rest of the plot_rsm_individual method remains similar, but use model_to_use and model_title_suffix)
# ... (Make sure to update title to include model_title_suffix)
fig.update_layout(
scene=dict(
xaxis_title=f"{varying_variables[0]} ({self.get_units(varying_variables[0])})",
yaxis_title=f"{varying_variables[1]} ({self.get_units(varying_variables[1])})",
zaxis_title=self.y_name,
),
title=f"{self.y_name} vs {varying_variables[0]} y {varying_variables[1]}<br><sup>{fixed_variable} fijo en {fixed_level:.3f} ({self.get_units(fixed_variable)}) {model_title_suffix}</sup>", # Updated title
height=800,
width=1000,
showlegend=True
)
return fig
# --- Funciones para la Interfaz de Gradio ---
def load_data(data_str):
# ... (load_data function remains the same)
return data.round(3), gr.update(visible=True)
def fit_and_optimize_model():
if 'rsm' not in globals():
return [None]*11
model_completo, pareto_completo = rsm.fit_model()
model_simplificado, pareto_simplificado = rsm.fit_simplified_model()
# Personalized model fitting is now triggered separately by custom_model_button
optimization_table = rsm.optimize()
equation = rsm.get_simplified_equation()
prediction_table = rsm.generate_prediction_table()
contribution_table = rsm.calculate_contribution_percentage()
anova_table = rsm.calculate_detailed_anova()
rsm.generate_all_plots() # Generate all plots for all models after fitting
equation_formatted = equation.replace(" + ", "<br>+ ").replace(" ** ", "^").replace("*", " × ")
equation_formatted = f"### Ecuación del Modelo Simplificado:<br>{equation_formatted}"
excel_path = rsm.save_tables_to_excel()
zip_path = rsm.save_figures_to_zip()
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,
zip_path,
excel_path
)
def fit_custom_model(factor_checkboxes, interaction_checkboxes): # New function for custom model
if 'rsm' not in globals():
return [None]*3 # Adjust output number
formula_parts = [rsm.x1_name, rsm.x2_name, rsm.x3_name] if "factors" in factor_checkboxes else []
if "x1_sq" in factor_checkboxes: formula_parts.append(f'I({rsm.x1_name}**2)')
if "x2_sq" in factor_checkboxes: formula_parts.append(f'I({rsm.x2_name}**2)')
if "x3_sq" in factor_checkboxes: formula_parts.append(f'I({rsm.x3_name}**2)')
if "x1x2" in interaction_checkboxes: formula_parts.append(f'{rsm.x1_name}:{rsm.x2_name}')
if "x1x3" in interaction_checkboxes: formula_parts.append(f'{rsm.x1_name}:{rsm.x3_name}')
if "x2x3" in interaction_checkboxes: formula_parts.append(f'{rsm.x2_name}:{rsm.x3_name}')
if not formula_parts:
formula = f'{rsm.y_name} ~ 1' # Intercept-only model if nothing selected
else:
formula = f'{rsm.y_name} ~ ' + ' + '.join(formula_parts)
custom_model, pareto_custom = rsm.fit_personalized_model(formula) # Fit personalized model
rsm.generate_all_plots() # Regenerate plots to include personalized model plots
return custom_model.summary().as_html(), pareto_custom, rsm.all_figures_personalized # Return custom model summary, pareto, and personalized plots
def show_plot(current_index, all_figures, model_type): # Modified to accept model_type
figure_list = []
if model_type == 'full':
figure_list = rsm.all_figures_full
elif model_type == 'simplified':
figure_list = rsm.all_figures_simplified
elif model_type == 'personalized':
figure_list = rsm.all_figures_personalized
if not figure_list:
return None, f"No hay gráficos disponibles para el modelo {model_type}.", current_index
selected_fig = figure_list[current_index]
plot_info_text = f"Gráfico {current_index + 1} de {len(figure_list)} (Modelo {model_type.capitalize()})" # Updated plot info
return selected_fig, plot_info_text, current_index
def navigate_plot(direction, current_index, all_figures, model_type): # Modified to accept model_type
figure_list = []
if model_type == 'full':
figure_list = rsm.all_figures_full
elif model_type == 'simplified':
figure_list = rsm.all_figures_simplified
elif model_type == 'personalized':
figure_list = rsm.all_figures_personalized
if not figure_list:
return None, f"No hay gráficos disponibles para el modelo {model_type}.", current_index
if direction == 'left':
new_index = (current_index - 1) % len(figure_list)
elif direction == 'right':
new_index = (current_index + 1) % len(figure_list)
else:
new_index = current_index
selected_fig = figure_list[new_index]
plot_info_text = f"Gráfico {new_index + 1} de {len(figure_list)} (Modelo {model_type.capitalize()})" # Updated plot info
return selected_fig, plot_info_text, new_index
def download_current_plot(all_figures, current_index, model_type): # Modified to accept model_type
figure_list = []
if model_type == 'full':
figure_list = rsm.all_figures_full
elif model_type == 'simplified':
figure_list = rsm.all_figures_simplified
elif model_type == 'personalized':
figure_list = rsm.all_figures_personalized
if not figure_list:
return None
fig = figure_list[current_index]
img_bytes = rsm.save_fig_to_bytes(fig)
filename = f"Grafico_RSM_{model_type}_{current_index + 1}.png" # Added model type to filename
with tempfile.NamedTemporaryFile(delete=False, suffix=".png") as temp_file:
temp_file.write(img_bytes)
temp_path = temp_file.name
return temp_path
def download_all_plots_zip(model_type): # Modified to accept model_type
if 'rsm' not in globals():
return None
if model_type == 'full':
rsm.all_figures = rsm.all_figures_full # Set current figures to download
elif model_type == 'simplified':
rsm.all_figures = rsm.all_figures_simplified
elif model_type == 'personalized':
rsm.all_figures = rsm.all_figures_personalized
zip_path = rsm.save_figures_to_zip()
if zip_path:
filename = f"Graficos_RSM_{model_type}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.zip" # Added model type to filename
return zip_path
return None
# --- Crear la interfaz de Gradio ---
def create_gradio_interface():
with gr.Blocks() as demo:
gr.Markdown("# Optimización de la Absorbancia usando RSM")
with gr.Row():
with gr.Column():
gr.Markdown("## Configuración del Análisis")
data_input = gr.Textbox(label="Datos del Experimento (formato CSV - Ignored, Data is Hardcoded)", lines=5, interactive=False, value="Data is pre-loaded, ignore input.")
load_button = gr.Button("Cargar Datos")
data_dropdown = gr.Dropdown(["All Data"], value="All Data", label="Seleccionar Datos") # Data Selection Dropdown - currently only 'All Data'
with gr.Column():
gr.Markdown("## Datos Cargados")
data_output = gr.Dataframe(label="Tabla de Datos", interactive=False)
with gr.Row(visible=False) as analysis_row:
with gr.Column():
fit_button = gr.Button("Ajustar Modelo Simplificado y Completo") # Button label changed
gr.Markdown("**Modelo Completo**")
model_completo_output = gr.HTML()
pareto_completo_output = gr.Plot()
gr.Markdown("**Modelo Simplificado**")
model_simplificado_output = gr.HTML()
pareto_simplificado_output = gr.Plot()
gr.Markdown("## Modelo Personalizado") # Personalized Model Section
factor_checkboxes = gr.CheckboxGroup(["factors", "x1_sq", "x2_sq", "x3_sq"], label="Términos de Factores", value=["factors", "x1_sq", "x2_sq", "x3_sq"]) # Factor Checkboxes
interaction_checkboxes = gr.CheckboxGroup(["x1x2", "x1x3", "x2x3"], label="Términos de Interacción") # Interaction Checkboxes
custom_model_button = gr.Button("Ajustar Modelo Personalizado") # Fit Custom Model Button
model_personalized_output = gr.HTML() # Output for personalized model summary
pareto_personalized_output = gr.Plot() # Pareto for personalized model
gr.Markdown("**Ecuación del Modelo Simplificado**")
equation_output = gr.HTML()
optimization_table_output = gr.Dataframe(label="Tabla de Optimización", interactive=False)
prediction_table_output = gr.Dataframe(label="Tabla de Predicciones", interactive=False)
contribution_table_output = gr.Dataframe(label="Tabla de % de Contribución", interactive=False)
anova_table_output = gr.Dataframe(label="Tabla ANOVA Detallada", interactive=False)
gr.Markdown("## Descargar Todas las Tablas")
download_excel_button = gr.DownloadButton("Descargar Tablas en Excel")
download_word_button = gr.DownloadButton("Descargar Tablas en Word")
with gr.Column():
gr.Markdown("## Gráficos de Superficie de Respuesta")
model_type_radio = gr.Radio(["simplified", "full", "personalized"], value="simplified", label="Tipo de Modelo para Gráficos") # Model Type Radio
fixed_variable_input = gr.Dropdown(label="Variable Fija", choices=["Glucosa_g_L", "Proteina_Pescado_g_L", "Sulfato_Manganeso_g_L"], value="Glucosa_g_L")
fixed_level_input = gr.Slider(label="Nivel de Variable Fija (Natural Units)", minimum=min(data['Glucosa_g_L']), maximum=max(data['Glucosa_g_L']), step=0.1, value=5.0)
plot_button = gr.Button("Generar Gráficos")
with gr.Row():
left_button = gr.Button("<")
right_button = gr.Button(">")
rsm_plot_output = gr.Plot()
plot_info = gr.Textbox(label="Información del Gráfico", value="Gráfico 1 de 9", interactive=False)
with gr.Row():
download_plot_button = gr.DownloadButton("Descargar Gráfico Actual (PNG)")
download_all_plots_button = gr.DownloadButton("Descargar Todos los Gráficos (ZIP)")
current_index_state = gr.State(0)
all_figures_state = gr.State([])
current_model_type_state = gr.State('simplified') # State to track selected model type for plots
# Cargar datos
load_button.click(
load_data,
inputs=[data_input],
outputs=[data_output, analysis_row]
)
# Ajustar modelo y optimizar (Simplified and Full)
fit_button.click(
fit_and_optimize_model,
inputs=[],
outputs=[
model_completo_output,
pareto_completo_output,
model_simplificado_output,
pareto_simplificado_output,
equation_output,
optimization_table_output,
prediction_table_output,
contribution_table_output,
anova_table_output,
download_all_plots_button,
download_excel_button
]
)
# Ajustar modelo personalizado
custom_model_button.click( # New event for custom model fitting
fit_custom_model,
inputs=[factor_checkboxes, interaction_checkboxes],
outputs=[model_personalized_output, pareto_personalized_output, all_figures_state] # Output personalized plots to state
)
# Generar y mostrar los gráficos
plot_button.click(
lambda fixed_var, fixed_lvl, model_type: ( # Added model_type input
show_plot(0, [], model_type) if not hasattr(rsm, 'all_figures_full') or not rsm.all_figures_full else show_plot(0, rsm.all_figures_full if model_type == 'full' else rsm.all_figures_simplified if model_type == 'simplified' else rsm.all_figures_personalized if model_type == 'personalized' else [], model_type) , # Conditional plot selection
0,
model_type # Update model_type state
),
inputs=[fixed_variable_input, fixed_level_input, model_type_radio], # Added model_type_radio input
outputs=[rsm_plot_output, plot_info, current_index_state, current_model_type_state] # Output model_type to state
)
# Navegación de gráficos
left_button.click(
lambda current_index, all_figures, model_type: navigate_plot('left', current_index, all_figures, model_type), # Pass model_type
inputs=[current_index_state, all_figures_state, current_model_type_state], # Input model_type state
outputs=[rsm_plot_output, plot_info, current_index_state]
)
right_button.click(
lambda current_index, all_figures, model_type: navigate_plot('right', current_index, all_figures, model_type), # Pass model_type
inputs=[current_index_state, all_figures_state, current_model_type_state], # Input model_type state
outputs=[rsm_plot_output, plot_info, current_index_state]
)
# Descargar gráfico actual
download_plot_button.click(
download_current_plot,
inputs=[all_figures_state, current_index_state, current_model_type_state], # Pass model_type state
outputs=download_plot_button
)
# Descargar todos los gráficos en ZIP
download_all_plots_button.click(
lambda model_type: download_all_plots_zip(model_type), # Pass model_type
inputs=[current_model_type_state], # Input model_type state
outputs=download_all_plots_button
)
# Descargar todas las tablas en Excel y Word
download_excel_button.click(
fn=lambda: download_all_tables_excel(),
inputs=[],
outputs=download_excel_button
)
download_word_button.click(
fn=lambda: exportar_word(rsm, rsm.get_all_tables()),
inputs=[],
outputs=download_word_button
)
# Ejemplo de uso
gr.Markdown("## Instrucciones:")
gr.Markdown("""
1. Click 'Cargar Datos' para usar los datos precargados.
2. Click 'Ajustar Modelo Simplificado y Completo'.
3. Opcional: Define un Modelo Personalizado seleccionando términos y haz clic en 'Ajustar Modelo Personalizado'.
4. Selecciona el 'Tipo de Modelo para Gráficos' (Simplificado, Completo o Personalizado).
5. Select 'Variable Fija' and 'Nivel de Variable Fija'.
6. Click 'Generar Gráficos'.
7. Navega entre los gráficos usando los botones '<' y '>'.
8. Descarga el gráfico actual en PNG o descarga todos los gráficos en un ZIP.
9. Descarga todas las tablas en un archivo Excel o Word con los botones correspondientes.
""")
return demo
# --- Función Principal ---
def main():
interface = create_gradio_interface()
interface.launch(share=True)
if __name__ == "__main__":
main() |