#import os #!pip install gradio seaborn scipy scikit-learn openpyxl pydantic==1.10.0 -q from pydantic import BaseModel, ConfigDict import numpy as np import pandas as pd import matplotlib.pyplot as plt import seaborn as sns from scipy.integrate import odeint from scipy.optimize import curve_fit from sklearn.metrics import mean_squared_error import gradio as gr import io from PIL import Image import tempfile class YourModel(BaseModel): class Config: arbitrary_types_allowed = True class BioprocessModel: def __init__(self, model_type='logistic', maxfev=50000): self.params = {} self.r2 = {} self.rmse = {} self.datax = [] self.datas = [] self.datap = [] self.dataxp = [] self.datasp = [] self.datapp = [] self.datax_std = [] self.datas_std = [] self.datap_std = [] self.biomass_model = None self.biomass_diff = None self.model_type = model_type self.maxfev = maxfev @staticmethod def logistic(time, xo, xm, um): return (xo * np.exp(um * time)) / (1 - (xo / xm) * (1 - np.exp(um * time))) @staticmethod def gompertz(time, xm, um, lag): return xm * np.exp(-np.exp((um * np.e / xm) * (lag - time) + 1)) @staticmethod def moser(time, Xm, um, Ks): return Xm * (1 - np.exp(-um * (time - Ks))) @staticmethod def logistic_diff(X, t, params): xo, xm, um = params return um * X * (1 - X / xm) @staticmethod def gompertz_diff(X, t, params): xm, um, lag = params return X * (um * np.e / xm) * np.exp((um * np.e / xm) * (lag - t) + 1) @staticmethod def moser_diff(X, t, params): Xm, um, Ks = params return um * (Xm - X) def substrate(self, time, so, p, q, biomass_params): X_t = self.biomass_model(time, *biomass_params) dXdt = np.gradient(X_t, time) integral_X = np.cumsum(X_t) * np.gradient(time) return so - p * (X_t - biomass_params[0]) - q * integral_X def product(self, time, po, alpha, beta, biomass_params): X_t = self.biomass_model(time, *biomass_params) dXdt = np.gradient(X_t, time) integral_X = np.cumsum(X_t) * np.gradient(time) return po + alpha * (X_t - biomass_params[0]) + beta * integral_X def process_data(self, df): biomass_cols = [col for col in df.columns if col[1] == 'Biomasa'] substrate_cols = [col for col in df.columns if col[1] == 'Sustrato'] product_cols = [col for col in df.columns if col[1] == 'Producto'] time_col = [col for col in df.columns if col[1] == 'Tiempo'][0] time = df[time_col].values data_biomass = [df[col].values for col in biomass_cols] data_biomass = np.array(data_biomass) self.datax.append(data_biomass) self.dataxp.append(np.mean(data_biomass, axis=0)) self.datax_std.append(np.std(data_biomass, axis=0, ddof=1)) data_substrate = [df[col].values for col in substrate_cols] data_substrate = np.array(data_substrate) self.datas.append(data_substrate) self.datasp.append(np.mean(data_substrate, axis=0)) self.datas_std.append(np.std(data_substrate, axis=0, ddof=1)) data_product = [df[col].values for col in product_cols] data_product = np.array(data_product) self.datap.append(data_product) self.datapp.append(np.mean(data_product, axis=0)) self.datap_std.append(np.std(data_product, axis=0, ddof=1)) self.time = time def fit_model(self): if self.model_type == 'logistic': self.biomass_model = self.logistic self.biomass_diff = self.logistic_diff elif self.model_type == 'gompertz': self.biomass_model = self.gompertz self.biomass_diff = self.gompertz_diff elif self.model_type == 'moser': self.biomass_model = self.moser self.biomass_diff = self.moser_diff def fit_biomass(self, time, biomass): try: if self.model_type == 'logistic': p0 = [min(biomass), max(biomass)*1.5 if max(biomass)>0 else 1.0, 0.1] popt, _ = curve_fit(self.logistic, time, biomass, p0=p0, maxfev=self.maxfev) self.params['biomass'] = {'xo': popt[0], 'xm': popt[1], 'um': popt[2]} y_pred = self.logistic(time, *popt) elif self.model_type == 'gompertz': p0 = [max(biomass) if max(biomass)>0 else 1.0, 0.1, time[np.argmax(np.gradient(biomass))]] popt, _ = curve_fit(self.gompertz, time, biomass, p0=p0, maxfev=self.maxfev) self.params['biomass'] = {'xm': popt[0], 'um': popt[1], 'lag': popt[2]} y_pred = self.gompertz(time, *popt) elif self.model_type == 'moser': p0 = [max(biomass) if max(biomass)>0 else 1.0, 0.1, min(time)] popt, _ = curve_fit(self.moser, time, biomass, p0=p0, maxfev=self.maxfev) self.params['biomass'] = {'Xm': popt[0], 'um': popt[1], 'Ks': popt[2]} y_pred = self.moser(time, *popt) self.r2['biomass'] = 1 - (np.sum((biomass - y_pred) ** 2) / np.sum((biomass - np.mean(biomass)) ** 2)) self.rmse['biomass'] = np.sqrt(mean_squared_error(biomass, y_pred)) return y_pred except Exception as e: print(f"Error en fit_biomass_{self.model_type}: {e}") return None def fit_substrate(self, time, substrate, biomass_params): try: if self.model_type == 'logistic': p0 = [min(substrate), 0.01, 0.01] popt, _ = curve_fit( lambda t, so, p, q: self.substrate(t, so, p, q, [biomass_params['xo'], biomass_params['xm'], biomass_params['um']]), time, substrate, p0=p0, maxfev=self.maxfev ) self.params['substrate'] = {'so': popt[0], 'p': popt[1], 'q': popt[2]} y_pred = self.substrate(time, *popt, [biomass_params['xo'], biomass_params['xm'], biomass_params['um']]) elif self.model_type == 'gompertz': p0 = [min(substrate), 0.01, 0.01] popt, _ = curve_fit( lambda t, so, p, q: self.substrate(t, so, p, q, [biomass_params['xm'], biomass_params['um'], biomass_params['lag']]), time, substrate, p0=p0, maxfev=self.maxfev ) self.params['substrate'] = {'so': popt[0], 'p': popt[1], 'q': popt[2]} y_pred = self.substrate(time, *popt, [biomass_params['xm'], biomass_params['um'], biomass_params['lag']]) elif self.model_type == 'moser': p0 = [min(substrate), 0.01, 0.01] popt, _ = curve_fit( lambda t, so, p, q: self.substrate(t, so, p, q, [biomass_params['Xm'], biomass_params['um'], biomass_params['Ks']]), time, substrate, p0=p0, maxfev=self.maxfev ) self.params['substrate'] = {'so': popt[0], 'p': popt[1], 'q': popt[2]} y_pred = self.substrate(time, *popt, [biomass_params['Xm'], biomass_params['um'], biomass_params['Ks']]) self.r2['substrate'] = 1 - (np.sum((substrate - y_pred) ** 2) / np.sum((substrate - np.mean(substrate)) ** 2)) self.rmse['substrate'] = np.sqrt(mean_squared_error(substrate, y_pred)) return y_pred except Exception as e: print(f"Error en fit_substrate_{self.model_type}: {e}") return None def fit_product(self, time, product, biomass_params): try: if self.model_type == 'logistic': p0 = [min(product), 0.01, 0.01] popt, _ = curve_fit( lambda t, po, alpha, beta: self.product(t, po, alpha, beta, [biomass_params['xo'], biomass_params['xm'], biomass_params['um']]), time, product, p0=p0, maxfev=self.maxfev ) self.params['product'] = {'po': popt[0], 'alpha': popt[1], 'beta': popt[2]} y_pred = self.product(time, *popt, [biomass_params['xo'], biomass_params['xm'], biomass_params['um']]) elif self.model_type == 'gompertz': p0 = [min(product), 0.01, 0.01] popt, _ = curve_fit( lambda t, po, alpha, beta: self.product(t, po, alpha, beta, [biomass_params['xm'], biomass_params['um'], biomass_params['lag']]), time, product, p0=p0, maxfev=self.maxfev ) self.params['product'] = {'po': popt[0], 'alpha': popt[1], 'beta': popt[2]} y_pred = self.product(time, *popt, [biomass_params['xm'], biomass_params['um'], biomass_params['lag']]) elif self.model_type == 'moser': p0 = [min(product), 0.01, 0.01] popt, _ = curve_fit( lambda t, po, alpha, beta: self.product(t, po, alpha, beta, [biomass_params['Xm'], biomass_params['um'], biomass_params['Ks']]), time, product, p0=p0, maxfev=self.maxfev ) self.params['product'] = {'po': popt[0], 'alpha': popt[1], 'beta': popt[2]} y_pred = self.product(time, *popt, [biomass_params['Xm'], biomass_params['um'], biomass_params['Ks']]) self.r2['product'] = 1 - (np.sum((product - y_pred) ** 2) / np.sum((product - np.mean(product)) ** 2)) self.rmse['product'] = np.sqrt(mean_squared_error(product, y_pred)) return y_pred except Exception as e: print(f"Error en fit_product_{self.model_type}: {e}") return None def generate_fine_time_grid(self, time): time_fine = np.linspace(time.min(), time.max(), 500) return time_fine def system(self, y, t, biomass_params, substrate_params, product_params, model_type): X, S, P = y if model_type == 'logistic': dXdt = self.logistic_diff(X, t, biomass_params) elif model_type == 'gompertz': dXdt = self.gompertz_diff(X, t, biomass_params) elif model_type == 'moser': dXdt = self.moser_diff(X, t, biomass_params) else: dXdt = 0.0 so, p, q = substrate_params po, alpha, beta = product_params dSdt = -p * dXdt - q * X dPdt = alpha * dXdt + beta * X return [dXdt, dSdt, dPdt] def get_initial_conditions(self, time, biomass, substrate, product): if 'biomass' in self.params: if self.model_type == 'logistic': xo = self.params['biomass']['xo'] X0 = xo elif self.model_type == 'gompertz': xm = self.params['biomass']['xm'] um = self.params['biomass']['um'] lag = self.params['biomass']['lag'] X0 = xm * np.exp(-np.exp((um * np.e / xm)*(lag - 0)+1)) elif self.model_type == 'moser': Xm = self.params['biomass']['Xm'] um = self.params['biomass']['um'] Ks = self.params['biomass']['Ks'] X0 = Xm*(1 - np.exp(-um*(0 - Ks))) else: X0 = biomass[0] if 'substrate' in self.params: so = self.params['substrate']['so'] S0 = so else: S0 = substrate[0] if 'product' in self.params: po = self.params['product']['po'] P0 = po else: P0 = product[0] return [X0, S0, P0] def solve_differential_equations(self, time, biomass, substrate, product): if 'biomass' not in self.params or not self.params['biomass']: print("No hay parámetros de biomasa, no se pueden resolver las EDO.") return None, None, None, time if self.model_type == 'logistic': biomass_params = [self.params['biomass']['xo'], self.params['biomass']['xm'], self.params['biomass']['um']] elif self.model_type == 'gompertz': biomass_params = [self.params['biomass']['xm'], self.params['biomass']['um'], self.params['biomass']['lag']] elif self.model_type == 'moser': biomass_params = [self.params['biomass']['Xm'], self.params['biomass']['um'], self.params['biomass']['Ks']] else: biomass_params = [0,0,0] if 'substrate' in self.params: substrate_params = [self.params['substrate']['so'], self.params['substrate']['p'], self.params['substrate']['q']] else: substrate_params = [0,0,0] if 'product' in self.params: product_params = [self.params['product']['po'], self.params['product']['alpha'], self.params['product']['beta']] else: product_params = [0,0,0] initial_conditions = self.get_initial_conditions(time, biomass, substrate, product) time_fine = self.generate_fine_time_grid(time) sol = odeint(self.system, initial_conditions, time_fine, args=(biomass_params, substrate_params, product_params, self.model_type)) X = sol[:, 0] S = sol[:, 1] P = sol[:, 2] return X, S, P, time_fine def plot_results(self, time, biomass, substrate, product, y_pred_biomass, y_pred_substrate, y_pred_product, biomass_std=None, substrate_std=None, product_std=None, experiment_name='', legend_position='best', params_position='upper right', show_legend=True, show_params=True, style='whitegrid', line_color='#0000FF', point_color='#000000', line_style='-', marker_style='o', use_differential=False): if y_pred_biomass is None: print(f"No se pudo ajustar biomasa para {experiment_name} con {self.model_type}. Omitiendo figura.") return None sns.set_style(style) if use_differential and 'biomass' in self.params and self.params['biomass']: X, S, P, time_to_plot = self.solve_differential_equations(time, biomass, substrate, product) if X is not None: y_pred_biomass, y_pred_substrate, y_pred_product = X, S, P else: time_to_plot = time else: time_to_plot = time fig, (ax1, ax2, ax3) = plt.subplots(3, 1, figsize=(10, 15)) fig.suptitle(f'{experiment_name}', fontsize=16) plots = [ (ax1, biomass, y_pred_biomass, biomass_std, 'Biomasa', 'Modelo', self.params.get('biomass', {}), self.r2.get('biomass', np.nan), self.rmse.get('biomass', np.nan)), (ax2, substrate, y_pred_substrate, substrate_std, 'Sustrato', 'Modelo', self.params.get('substrate', {}), self.r2.get('substrate', np.nan), self.rmse.get('substrate', np.nan)), (ax3, product, y_pred_product, product_std, 'Producto', 'Modelo', self.params.get('product', {}), self.r2.get('product', np.nan), self.rmse.get('product', np.nan)) ] for idx, (ax, data, y_pred, data_std, ylabel, model_name, params, r2, rmse) in enumerate(plots): if data_std is not None: ax.errorbar(time, data, yerr=data_std, fmt=marker_style, color=point_color, label='Datos experimentales', capsize=5) else: ax.plot(time, data, marker=marker_style, linestyle='', color=point_color, label='Datos experimentales') if y_pred is not None: ax.plot(time_to_plot, y_pred, linestyle=line_style, color=line_color, label=model_name) ax.set_xlabel('Tiempo') ax.set_ylabel(ylabel) if show_legend: ax.legend(loc=legend_position) ax.set_title(f'{ylabel}') if show_params and params and all(np.isfinite(list(map(float, params.values())))): param_text = '\n'.join([f"{k} = {v:.3f}" for k, v in params.items()]) text = f"{param_text}\nR² = {r2:.3f}\nRMSE = {rmse:.3f}" if params_position == 'outside right': bbox_props = dict(boxstyle='round', facecolor='white', alpha=0.5) ax.annotate(text, xy=(1.05, 0.5), xycoords='axes fraction', verticalalignment='center', bbox=bbox_props) else: if params_position in ['upper right', 'lower right']: text_x = 0.95 ha = 'right' else: text_x = 0.05 ha = 'left' if params_position in ['upper right', 'upper left']: text_y = 0.95 va = 'top' else: text_y = 0.05 va = 'bottom' ax.text(text_x, text_y, text, transform=ax.transAxes, verticalalignment=va, horizontalalignment=ha, bbox={'boxstyle': 'round', 'facecolor':'white', 'alpha':0.5}) plt.tight_layout(rect=[0, 0.03, 1, 0.95]) buf = io.BytesIO() fig.savefig(buf, format='png') buf.seek(0) image = Image.open(buf).convert("RGB") plt.close(fig) return image def plot_combined_results(self, time, biomass, substrate, product, y_pred_biomass, y_pred_substrate, y_pred_product, biomass_std=None, substrate_std=None, product_std=None, experiment_name='', legend_position='best', params_position='upper right', show_legend=True, show_params=True, style='whitegrid', line_color='#0000FF', point_color='#000000', line_style='-', marker_style='o', use_differential=False): if y_pred_biomass is None: print(f"No se pudo ajustar biomasa para {experiment_name} con {self.model_type}. Omitiendo figura.") return None sns.set_style(style) if use_differential and 'biomass' in self.params and self.params['biomass']: X, S, P, time_to_plot = self.solve_differential_equations(time, biomass, substrate, product) if X is not None: y_pred_biomass, y_pred_substrate, y_pred_product = X, S, P else: time_to_plot = time else: time_to_plot = time fig, ax1 = plt.subplots(figsize=(10, 7)) fig.suptitle(f'{experiment_name}', fontsize=16) colors = {'Biomasa': 'blue', 'Sustrato': 'green', 'Producto': 'red'} ax1.set_xlabel('Tiempo') ax1.set_ylabel('Biomasa', color=colors['Biomasa']) if biomass_std is not None: ax1.errorbar(time, biomass, yerr=biomass_std, fmt=marker_style, color=colors['Biomasa'], label='Biomasa (Datos)', capsize=5) else: ax1.plot(time, biomass, marker=marker_style, linestyle='', color=colors['Biomasa'], label='Biomasa (Datos)') ax1.plot(time_to_plot, y_pred_biomass, linestyle=line_style, color=colors['Biomasa'], label='Biomasa (Modelo)') ax1.tick_params(axis='y', labelcolor=colors['Biomasa']) ax2 = ax1.twinx() ax2.set_ylabel('Sustrato', color=colors['Sustrato']) if substrate_std is not None: ax2.errorbar(time, substrate, yerr=substrate_std, fmt=marker_style, color=colors['Sustrato'], label='Sustrato (Datos)', capsize=5) else: ax2.plot(time, substrate, marker=marker_style, linestyle='', color=colors['Sustrato'], label='Sustrato (Datos)') if y_pred_substrate is not None: ax2.plot(time_to_plot, y_pred_substrate, linestyle=line_style, color=colors['Sustrato'], label='Sustrato (Modelo)') ax2.tick_params(axis='y', labelcolor=colors['Sustrato']) ax3 = ax1.twinx() ax3.spines["right"].set_position(("axes", 1.2)) ax3.set_frame_on(True) ax3.patch.set_visible(False) for sp in ax3.spines.values(): sp.set_visible(True) ax3.set_ylabel('Producto', color=colors['Producto']) if product_std is not None: ax3.errorbar(time, product, yerr=product_std, fmt=marker_style, color=colors['Producto'], label='Producto (Datos)', capsize=5) else: ax3.plot(time, product, marker=marker_style, linestyle='', color=colors['Producto'], label='Producto (Datos)') if y_pred_product is not None: ax3.plot(time_to_plot, y_pred_product, linestyle=line_style, color=colors['Producto'], label='Producto (Modelo)') ax3.tick_params(axis='y', labelcolor=colors['Producto']) lines_labels = [ax.get_legend_handles_labels() for ax in [ax1, ax2, ax3]] lines, labels = [sum(lol, []) for lol in zip(*lines_labels)] if show_legend: ax1.legend(lines, labels, loc=legend_position) if show_params: param_text_biomass = '' if 'biomass' in self.params: param_text_biomass = '\n'.join([f"{k} = {v:.3f}" for k, v in self.params['biomass'].items()]) text_biomass = f"Biomasa:\n{param_text_biomass}\nR² = {self.r2.get('biomass', np.nan):.3f}\nRMSE = {self.rmse.get('biomass', np.nan):.3f}" param_text_substrate = '' if 'substrate' in self.params: param_text_substrate = '\n'.join([f"{k} = {v:.3f}" for k, v in self.params['substrate'].items()]) text_substrate = f"Sustrato:\n{param_text_substrate}\nR² = {self.r2.get('substrate', np.nan):.3f}\nRMSE = {self.rmse.get('substrate', np.nan):.3f}" param_text_product = '' if 'product' in self.params: param_text_product = '\n'.join([f"{k} = {v:.3f}" for k, v in self.params['product'].items()]) text_product = f"Producto:\n{param_text_product}\nR² = {self.r2.get('product', np.nan):.3f}\nRMSE = {self.rmse.get('product', np.nan):.3f}" total_text = f"{text_biomass}\n\n{text_substrate}\n\n{text_product}" if params_position == 'outside right': bbox_props = dict(boxstyle='round', facecolor='white', alpha=0.5) ax3.annotate(total_text, xy=(1.2, 0.5), xycoords='axes fraction', verticalalignment='center', bbox=bbox_props) else: if params_position in ['upper right', 'lower right']: text_x = 0.95 ha = 'right' else: text_x = 0.05 ha = 'left' if params_position in ['upper right', 'upper left']: text_y = 0.95 va = 'top' else: text_y = 0.05 va = 'bottom' ax1.text(text_x, text_y, total_text, transform=ax1.transAxes, verticalalignment=va, horizontalalignment=ha, bbox={'boxstyle':'round', 'facecolor':'white', 'alpha':0.5}) plt.tight_layout(rect=[0, 0.03, 1, 0.95]) buf = io.BytesIO() fig.savefig(buf, format='png') buf.seek(0) image = Image.open(buf).convert("RGB") plt.close(fig) return image def process_all_data(file, legend_position, params_position, model_types, experiment_names, lower_bounds, upper_bounds, mode='independent', style='whitegrid', line_color='#0000FF', point_color='#000000', line_style='-', marker_style='o', show_legend=True, show_params=True, use_differential=False, maxfev_val=50000): try: xls = pd.ExcelFile(file.name) except Exception as e: print(f"Error al leer el archivo Excel: {e}") return [], pd.DataFrame() sheet_names = xls.sheet_names figures = [] comparison_data = [] experiment_counter = 0 for sheet_name in sheet_names: try: df = pd.read_excel(file.name, sheet_name=sheet_name, header=[0, 1]) except Exception as e: print(f"Error al leer la hoja '{sheet_name}': {e}") continue model_dummy = BioprocessModel() model_dummy.process_data(df) time = model_dummy.time if mode == 'independent': num_experiments = len(df.columns.levels[0]) for idx in range(num_experiments): col = df.columns.levels[0][idx] try: time_exp = df[(col, 'Tiempo')].dropna().values biomass = df[(col, 'Biomasa')].dropna().values substrate = df[(col, 'Sustrato')].dropna().values product = df[(col, 'Producto')].dropna().values except KeyError as e: print(f"Error al procesar el experimento '{col}': {e}") continue biomass_std = None substrate_std = None product_std = None if biomass.ndim > 1: biomass_std = np.std(biomass, axis=0, ddof=1) biomass = np.mean(biomass, axis=0) if substrate.ndim > 1: substrate_std = np.std(substrate, axis=0, ddof=1) substrate = np.mean(substrate, axis=0) if product.ndim > 1: product_std = np.std(product, axis=0, ddof=1) product = np.mean(product, axis=0) experiment_name = (experiment_names[experiment_counter] if experiment_counter < len(experiment_names) else f"Tratamiento {experiment_counter + 1}") for model_type in model_types: model = BioprocessModel(model_type=model_type, maxfev=maxfev_val) model.fit_model() y_pred_biomass = model.fit_biomass(time_exp, biomass) if y_pred_biomass is None: comparison_data.append({ 'Experimento': experiment_name, 'Modelo': model_type.capitalize(), 'R² Biomasa': np.nan, 'RMSE Biomasa': np.nan, 'R² Sustrato': np.nan, 'RMSE Sustrato': np.nan, 'R² Producto': np.nan, 'RMSE Producto': np.nan }) continue else: if 'biomass' in model.params and model.params['biomass']: y_pred_substrate = model.fit_substrate(time_exp, substrate, model.params['biomass']) y_pred_product = model.fit_product(time_exp, product, model.params['biomass']) else: y_pred_substrate = None y_pred_product = None comparison_data.append({ 'Experimento': experiment_name, 'Modelo': model_type.capitalize(), 'R² Biomasa': model.r2.get('biomass', np.nan), 'RMSE Biomasa': model.rmse.get('biomass', np.nan), 'R² Sustrato': model.r2.get('substrate', np.nan), 'RMSE Sustrato': model.rmse.get('substrate', np.nan), 'R² Producto': model.r2.get('product', np.nan), 'RMSE Producto': model.rmse.get('product', np.nan) }) if mode == 'combinado': fig = model.plot_combined_results(time_exp, biomass, substrate, product, y_pred_biomass, y_pred_substrate, y_pred_product, biomass_std, substrate_std, product_std, experiment_name, legend_position, params_position, show_legend, show_params, style, line_color, point_color, line_style, marker_style, use_differential) else: fig = model.plot_results(time_exp, biomass, substrate, product, y_pred_biomass, y_pred_substrate, y_pred_product, biomass_std, substrate_std, product_std, experiment_name, legend_position, params_position, show_legend, show_params, style, line_color, point_color, line_style, marker_style, use_differential) if fig is not None: figures.append(fig) experiment_counter += 1 elif mode in ['average', 'combinado']: try: time_exp = df[(df.columns.levels[0][0], 'Tiempo')].dropna().values biomass = model_dummy.dataxp[-1] substrate = model_dummy.datasp[-1] product = model_dummy.datapp[-1] except IndexError as e: print(f"Error al obtener los datos promedio de la hoja '{sheet_name}': {e}") continue biomass_std = model_dummy.datax_std[-1] substrate_std = model_dummy.datas_std[-1] product_std = model_dummy.datap_std[-1] experiment_name = (experiment_names[experiment_counter] if experiment_counter < len(experiment_names) else f"Tratamiento {experiment_counter + 1}") for model_type in model_types: model = BioprocessModel(model_type=model_type, maxfev=maxfev_val) model.fit_model() y_pred_biomass = model.fit_biomass(time_exp, biomass) if y_pred_biomass is None: comparison_data.append({ 'Experimento': experiment_name, 'Modelo': model_type.capitalize(), 'R² Biomasa': np.nan, 'RMSE Biomasa': np.nan, 'R² Sustrato': np.nan, 'RMSE Sustrato': np.nan, 'R² Producto': np.nan, 'RMSE Producto': np.nan }) continue else: if 'biomass' in model.params and model.params['biomass']: y_pred_substrate = model.fit_substrate(time_exp, substrate, model.params['biomass']) y_pred_product = model.fit_product(time_exp, product, model.params['biomass']) else: y_pred_substrate = None y_pred_product = None comparison_data.append({ 'Experimento': experiment_name, 'Modelo': model_type.capitalize(), 'R² Biomasa': model.r2.get('biomass', np.nan), 'RMSE Biomasa': model.rmse.get('biomass', np.nan), 'R² Sustrato': model.r2.get('substrate', np.nan), 'RMSE Sustrato': model.rmse.get('substrate', np.nan), 'R² Producto': model.r2.get('product', np.nan), 'RMSE Producto': model.rmse.get('product', np.nan) }) if mode == 'combinado': fig = model.plot_combined_results(time_exp, biomass, substrate, product, y_pred_biomass, y_pred_substrate, y_pred_product, biomass_std, substrate_std, product_std, experiment_name, legend_position, params_position, show_legend, show_params, style, line_color, point_color, line_style, marker_style, use_differential) else: fig = model.plot_results(time_exp, biomass, substrate, product, y_pred_biomass, y_pred_substrate, y_pred_product, biomass_std, substrate_std, product_std, experiment_name, legend_position, params_position, show_legend, show_params, style, line_color, point_color, line_style, marker_style, use_differential) if fig is not None: figures.append(fig) experiment_counter += 1 comparison_df = pd.DataFrame(comparison_data) if not comparison_df.empty: comparison_df_sorted = comparison_df.sort_values( by=['R² Biomasa', 'R² Sustrato', 'R² Producto', 'RMSE Biomasa', 'RMSE Sustrato', 'RMSE Producto'], ascending=[False, False, False, True, True, True] ).reset_index(drop=True) else: comparison_df_sorted = comparison_df return figures, comparison_df_sorted def create_interface(): with gr.Blocks() as demo: gr.Markdown("# Modelos de Bioproceso: Logístico, Gompertz, Moser y Luedeking-Piret") gr.Markdown(r""" ## Ecuaciones Diferenciales Utilizadas **Biomasa:** - Logístico: $$ \frac{dX}{dt} = \mu_m X\left(1 - \frac{X}{X_m}\right) $$ - Gompertz: $$ X(t) = X_m \exp\left(-\exp\left(\left(\frac{\mu_m e}{X_m}\right)(\text{lag}-t)+1\right)\right) $$ Ecuación diferencial: $$ \frac{dX}{dt} = X(t)\left(\frac{\mu_m e}{X_m}\right)\exp\left(\left(\frac{\mu_m e}{X_m}\right)(\text{lag}-t)+1\right) $$ - Moser (simplificado): $$ X(t)=X_m(1-e^{-\mu_m(t-K_s)}) $$ $$ \frac{dX}{dt}=\mu_m(X_m - X) $$ **Sustrato y Producto (Luedeking-Piret):** $$ \frac{dS}{dt} = -p \frac{dX}{dt} - q X $$ $$ \frac{dP}{dt} = \alpha \frac{dX}{dt} + \beta X $$ """) file_input = gr.File(label="Subir archivo Excel") with gr.Row(): with gr.Column(): legend_position = gr.Radio( choices=["upper left", "upper right", "lower left", "lower right", "best"], label="Posición de la leyenda", value="best" ) show_legend = gr.Checkbox(label="Mostrar Leyenda", value=True) with gr.Column(): params_positions = ["upper left", "upper right", "lower left", "lower right", "outside right"] params_position = gr.Radio( choices=params_positions, label="Posición de los parámetros", value="upper right" ) show_params = gr.Checkbox(label="Mostrar Parámetros", value=True) model_types = gr.CheckboxGroup( choices=["logistic", "gompertz", "moser"], label="Tipo(s) de Modelo", value=["logistic"] ) mode = gr.Radio(["independent", "average", "combinado"], label="Modo de Análisis", value="independent") use_differential = gr.Checkbox(label="Usar ecuaciones diferenciales para graficar", value=False) experiment_names = gr.Textbox( label="Nombres de los experimentos (uno por línea)", placeholder="Experimento 1\nExperimento 2\n...", lines=5 ) with gr.Row(): with gr.Column(): lower_bounds = gr.Textbox( label="Lower Bounds (uno por línea, formato: param1,param2,param3)", placeholder="0,0,0\n0,0,0\n...", lines=5 ) with gr.Column(): upper_bounds = gr.Textbox( label="Upper Bounds (uno por línea, formato: param1,param2,param3)", placeholder="inf,inf,inf\ninf,inf,inf\n...", lines=5 ) styles = ['white', 'dark', 'whitegrid', 'darkgrid', 'ticks'] style_dropdown = gr.Dropdown(choices=styles, label="Selecciona el estilo de gráfico", value='whitegrid') line_color_picker = gr.ColorPicker(label="Color de la línea", value='#0000FF') point_color_picker = gr.ColorPicker(label="Color de los puntos", value='#000000') line_style_options = ['-', '--', '-.', ':'] line_style_dropdown = gr.Dropdown(choices=line_style_options, label="Estilo de línea", value='-') marker_style_options = ['o', 's', '^', 'v', 'D', 'x', '+', '*'] marker_style_dropdown = gr.Dropdown(choices=marker_style_options, label="Estilo de punto", value='o') maxfev_input = gr.Number(label="maxfev (Máx. evaluaciones para el ajuste)", value=50000) simulate_btn = gr.Button("Simular") output_gallery = gr.Gallery(label="Resultados", columns=2, height='auto') output_table = gr.Dataframe( label="Tabla Comparativa de Modelos", headers=["Experimento", "Modelo", "R² Biomasa", "RMSE Biomasa", "R² Sustrato", "RMSE Sustrato", "R² Producto", "RMSE Producto"], interactive=False ) state_df = gr.State() def process_and_plot(file, legend_position, params_position, model_types, mode, experiment_names, lower_bounds, upper_bounds, style, line_color, point_color, line_style, marker_style, show_legend, show_params, use_differential, maxfev_input): experiment_names_list = experiment_names.strip().split('\n') if experiment_names.strip() else [] lower_bounds_list = [] if lower_bounds.strip(): for lb in lower_bounds.strip().split('\n'): lb_values = [] for val in lb.split(','): val = val.strip().lower() if val in ['inf', 'infty', 'infinity']: lb_values.append(-np.inf) else: try: lb_values.append(float(val)) except ValueError: lb_values.append(0.0) lower_bounds_list.append(tuple(lb_values)) upper_bounds_list = [] if upper_bounds.strip(): for ub in upper_bounds.strip().split('\n'): ub_values = [] for val in ub.split(','): val = val.strip().lower() if val in ['inf', 'infty', 'infinity']: ub_values.append(np.inf) else: try: ub_values.append(float(val)) except ValueError: ub_values.append(np.inf) upper_bounds_list.append(tuple(ub_values)) figures, comparison_df = process_all_data(file, legend_position, params_position, model_types, experiment_names_list, lower_bounds_list, upper_bounds_list, mode, style, line_color, point_color, line_style, marker_style, show_legend, show_params, use_differential, maxfev_val=int(maxfev_input)) return figures, comparison_df, comparison_df simulate_output = simulate_btn.click( fn=process_and_plot, inputs=[file_input, legend_position, params_position, model_types, mode, experiment_names, lower_bounds, upper_bounds, style_dropdown, line_color_picker, point_color_picker, line_style_dropdown, marker_style_dropdown, show_legend, show_params, use_differential, maxfev_input], outputs=[output_gallery, output_table, state_df] ) def export_excel(df): if df.empty: return None with tempfile.NamedTemporaryFile(suffix=".xlsx", delete=False) as tmp: df.to_excel(tmp.name, index=False) return tmp.name export_btn = gr.Button("Exportar Tabla a Excel") file_output = gr.File() export_btn.click( fn=export_excel, inputs=state_df, outputs=file_output ) return demo demo = create_interface() demo.launch(share=True)