calibrate2 / app.py
C2MV's picture
Update app.py
981f292 verified
raw
history blame
13.6 kB
# -*- 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
@staticmethod
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)