Spaces:
Runtime error
Runtime error
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() |