Spaces:
Sleeping
Sleeping
# -*- coding: utf-8 -*- | |
"""PrediLectia - Gradio Final v2 with Multiple Y-Axes in Combined Plot.ipynb""" | |
# Instalación de librerías necesarias | |
#!pip install gradio seaborn scipy -q | |
import os | |
os.system('pip install gradio seaborn scipy scikit-learn openpyxl pydantic==1.10.0') | |
from pydantic import BaseModel, ConfigDict | |
class YourModel(BaseModel): | |
class Config: | |
arbitrary_types_allowed = True | |
import numpy as np | |
import pandas as pd | |
import matplotlib.pyplot as plt | |
import seaborn as sns | |
from scipy.optimize import curve_fit | |
from sklearn.metrics import mean_squared_error | |
import gradio as gr | |
import io | |
from PIL import Image | |
# Definición de la clase CalibrationModel | |
class CalibrationModel: | |
def __init__(self): | |
self.params = {} | |
self.r2 = {} | |
self.rmse = {} | |
self.datax = [] | |
self.datay = [] | |
self.dataxp = [] | |
self.datayp = [] | |
self.datax_std = [] | |
self.datay_std = [] | |
# Función de ajuste lineal | |
def linear(x, m, b): | |
return m * x + b | |
# Métodos de procesamiento y ajuste de datos | |
def process_data(self, df): | |
# Obtener todas las columnas que contengan "Concentración" y "Absorbancia" | |
concentration_cols = [col for col in df.columns if col[1] == 'Concentración'] | |
absorbance_cols = [col for col in df.columns if col[1] == 'Absorbancia'] | |
# Procesar los datos de concentración | |
data_concentration = [df[col].values for col in concentration_cols] | |
data_concentration = np.array(data_concentration) # shape (num_experiments, num_time_points) | |
self.datax.append(data_concentration) | |
self.dataxp.append(np.mean(data_concentration, axis=0)) | |
self.datax_std.append(np.std(data_concentration, axis=0, ddof=1)) | |
# Procesar los datos de absorbancia | |
data_absorbance = [df[col].values for col in absorbance_cols] | |
data_absorbance = np.array(data_absorbance) | |
self.datay.append(data_absorbance) | |
self.datayp.append(np.mean(data_absorbance, axis=0)) | |
self.datay_std.append(np.std(data_absorbance, axis=0, ddof=1)) | |
def fit_model(self): | |
self.fit_calibration = self.fit_calibration_linear | |
def fit_calibration_linear(self, concentration, absorbance, bounds): | |
# Eliminar valores NaN e inf | |
mask = ~np.isnan(concentration) & ~np.isnan(absorbance) & np.isfinite(concentration) & np.isfinite(absorbance) | |
concentration = concentration[mask] | |
absorbance = absorbance[mask] | |
if len(concentration) == 0 or len(absorbance) == 0: | |
raise ValueError("No valid data points for fitting.") | |
popt, _ = curve_fit(self.linear, concentration, absorbance, bounds=bounds, maxfev=10000) | |
self.params['calibration'] = {'m': popt[0], 'b': popt[1]} | |
y_pred = self.linear(concentration, *popt) | |
self.r2['calibration'] = 1 - (np.sum((absorbance - y_pred) ** 2) / np.sum((absorbance - np.mean(absorbance)) ** 2)) | |
self.rmse['calibration'] = np.sqrt(mean_squared_error(absorbance, y_pred)) | |
return y_pred | |
# Métodos de visualización de resultados | |
def plot_results(self, concentration, absorbance, y_pred, | |
concentration_std=None, absorbance_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'): | |
sns.set_style(style) # Establecer el estilo seleccionado | |
fig, ax = plt.subplots(figsize=(10, 7)) | |
fig.suptitle(f'{experiment_name}', fontsize=16) | |
if absorbance_std is not None: | |
ax.errorbar(concentration, absorbance, yerr=absorbance_std, fmt=marker_style, color=point_color, | |
label='Datos experimentales', capsize=5) | |
else: | |
ax.plot(concentration, absorbance, marker=marker_style, linestyle='', color=point_color, | |
label='Datos experimentales') | |
ax.plot(concentration, y_pred, linestyle=line_style, color=line_color, label='Modelo') | |
ax.set_xlabel('Concentración') | |
ax.set_ylabel('Absorbancia') | |
if show_legend: | |
ax.legend(loc=legend_position) | |
ax.set_title(f'Curva de Calibrado') | |
if show_params: | |
param_text = '\n'.join([f"{k} = {v:.4f}" for k, v in self.params['calibration'].items()]) | |
text = f"{param_text}\nR² = {self.r2['calibration']:.4f}\nRMSE = {self.rmse['calibration']:.4f}" | |
# Si la posición es 'outside right', ajustar la posición del texto | |
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() | |
return fig | |
# Función de procesamiento de datos | |
def process_data(file, legend_position, params_position, experiment_names, lower_bounds, upper_bounds, | |
style='whitegrid', line_color='#0000FF', point_color='#000000', | |
line_style='-', marker_style='o', show_legend=True, show_params=True): | |
# Leer todas las hojas del archivo Excel | |
xls = pd.ExcelFile(file.name) | |
sheet_names = xls.sheet_names | |
model = CalibrationModel() | |
model.fit_model() | |
figures = [] | |
# Si no se proporcionan suficientes límites, usar valores predeterminados | |
default_lower_bounds = (0, 0) | |
default_upper_bounds = (np.inf, np.inf) | |
experiment_counter = 0 # Contador global de experimentos | |
for sheet_name in sheet_names: | |
df = pd.read_excel(file.name, sheet_name=sheet_name, header=[0, 1]) | |
# Procesar datos | |
model.process_data(df) | |
concentration = model.dataxp[-1] | |
absorbance = model.datayp[-1] | |
# Si hay replicados en el experimento, calcular la desviación estándar | |
concentration_std = None | |
absorbance_std = None | |
if concentration.ndim > 1: | |
concentration_std = np.std(concentration, axis=0, ddof=1) | |
concentration = np.mean(concentration, axis=0) | |
if absorbance.ndim > 1: | |
absorbance_std = np.std(absorbance, axis=0, ddof=1) | |
absorbance = np.mean(absorbance, axis=0) | |
# Obtener límites o usar valores predeterminados | |
lower_bound = lower_bounds[experiment_counter] if experiment_counter < len(lower_bounds) else default_lower_bounds | |
upper_bound = upper_bounds[experiment_counter] if experiment_counter < len(upper_bounds) else default_upper_bounds | |
bounds = (lower_bound, upper_bound) | |
# Ajustar el modelo | |
y_pred = model.fit_calibration(concentration, absorbance, bounds) | |
# Usar el nombre del experimento proporcionado o un nombre por defecto | |
experiment_name = experiment_names[experiment_counter] if experiment_counter < len(experiment_names) else f"Tratamiento {experiment_counter + 1}" | |
fig = model.plot_results(concentration, absorbance, y_pred, | |
concentration_std, absorbance_std, | |
experiment_name, legend_position, params_position, | |
show_legend, show_params, | |
style, | |
line_color, point_color, line_style, marker_style) | |
figures.append(fig) | |
experiment_counter += 1 | |
return figures | |
def create_interface(): | |
with gr.Blocks(theme='upsatwal/mlsc_tiet') as demo: | |
gr.Markdown("# Ajuste de Curva de Calibrado") | |
gr.Markdown( | |
"Sube un archivo Excel con múltiples pestañas. Cada pestaña debe contener columnas 'Concentración' y 'Absorbancia' para cada experimento.") | |
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) | |
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: m,b)", | |
placeholder="0,0\n0,0\n...", | |
lines=5 | |
) | |
with gr.Column(): | |
upper_bounds = gr.Textbox( | |
label="Upper Bounds (uno por línea, formato: m,b)", | |
placeholder="inf,inf\ninf,inf\n...", | |
lines=5 | |
) | |
# Añadir un desplegable para seleccionar el estilo del gráfico | |
styles = ['white', 'dark', 'whitegrid', 'darkgrid', 'ticks'] | |
style_dropdown = gr.Dropdown(choices=styles, label="Selecciona el estilo de gráfico", value='whitegrid') | |
# Añadir color pickers para líneas y puntos | |
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') | |
# Añadir listas desplegables para tipo de línea y tipo de punto | |
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') | |
simulate_btn = gr.Button("Simular") | |
# Definir un componente gr.Gallery para las salidas | |
output_gallery = gr.Gallery(label="Resultados", columns=2, height='auto') | |
def process_and_plot(file, legend_position, params_position, experiment_names, | |
lower_bounds, upper_bounds, style, | |
line_color, point_color, line_style, marker_style, | |
show_legend, show_params): | |
# Dividir los nombres de experimentos y límites en listas | |
experiment_names_list = experiment_names.strip().split('\n') if experiment_names.strip() else [] | |
lower_bounds_list = [tuple(map(float, lb.split(','))) for lb in | |
lower_bounds.strip().split('\n')] if lower_bounds.strip() else [] | |
upper_bounds_list = [tuple(map(float, ub.split(','))) for ub in | |
upper_bounds.strip().split('\n')] if upper_bounds.strip() else [] | |
# Procesar los datos y generar gráficos | |
figures = process_data(file, legend_position, params_position, experiment_names_list, | |
lower_bounds_list, upper_bounds_list, style, | |
line_color, point_color, line_style, marker_style, | |
show_legend, show_params) | |
# Convertir las figuras a imágenes y devolverlas como lista | |
image_list = [] | |
for fig in figures: | |
buf = io.BytesIO() | |
fig.savefig(buf, format='png') | |
buf.seek(0) | |
image = Image.open(buf) | |
image_list.append(image) | |
return image_list | |
simulate_btn.click( | |
fn=process_and_plot, | |
inputs=[file_input, | |
legend_position, | |
params_position, | |
experiment_names, | |
lower_bounds, | |
upper_bounds, | |
style_dropdown, | |
line_color_picker, | |
point_color_picker, | |
line_style_dropdown, | |
marker_style_dropdown, | |
show_legend, | |
show_params], | |
outputs=output_gallery | |
) | |
return demo | |
# Crear y lanzar la interfaz | |
demo = create_interface() | |
demo.launch(share=True) | |