Spaces:
Sleeping
Sleeping
import gradio as gr | |
import pandas as pd | |
import numpy as np | |
import matplotlib.pyplot as plt | |
import seaborn as sns | |
from scipy import stats | |
from datetime import datetime | |
import docx | |
from docx.shared import Inches, Pt | |
from docx.enum.text import WD_PARAGRAPH_ALIGNMENT | |
import os | |
def generar_tabla(n_filas, concentracion_inicial, unidad_medida, n_replicas): | |
valores_base = [1.00, 0.80, 0.60, 0.40, 0.20, 0.10, 0.05] | |
if n_filas <= 7: | |
solucion_inoculo = valores_base[:n_filas] | |
agua = [round(1 - x, 2) for x in solucion_inoculo] | |
else: | |
solucion_inoculo = valores_base.copy() | |
ultimo_valor = valores_base[-1] | |
for _ in range(n_filas - 7): | |
nuevo_valor = round(ultimo_valor / 2, 3) | |
solucion_inoculo.append(nuevo_valor) | |
ultimo_valor = nuevo_valor | |
agua = [round(1 - x, 3) for x in solucion_inoculo] | |
data = { | |
f"Solución de inóculo ({concentracion_inicial} {unidad_medida})": solucion_inoculo, | |
"H2O": agua | |
} | |
df = pd.DataFrame(data) | |
nombre_columna = f"Solución de inóculo ({concentracion_inicial} {unidad_medida})" | |
df["Factor de Dilución"] = df[nombre_columna].apply(lambda x: round(1 / x, 2)) | |
df["Concentración Predicha Numérica"] = df["Factor de Dilución"].apply( | |
lambda x: concentracion_inicial / x | |
) | |
df[f"Concentración Predicha ({unidad_medida})"] = df["Concentración Predicha Numérica"].round(0).astype(str) | |
# Añadir columnas para las réplicas de "Concentración Real" | |
for i in range(1, n_replicas + 1): | |
df[f"Concentración Real {i} ({unidad_medida})"] = None | |
# Las columnas de promedio y desviación estándar se agregarán durante el análisis | |
return df | |
def ajustar_decimales_evento(df, decimales): | |
df = df.copy() | |
# Ajustar decimales en todas las columnas numéricas | |
for col in df.columns: | |
try: | |
df[col] = pd.to_numeric(df[col], errors='ignore') | |
df[col] = df[col].round(decimales) | |
except: | |
pass | |
return df | |
def calcular_promedio_desviacion(df, n_replicas, unidad_medida): | |
df = df.copy() | |
# Obtener las columnas de réplicas | |
col_replicas = [f"Concentración Real {i} ({unidad_medida})" for i in range(1, n_replicas + 1)] | |
# Convertir a numérico | |
for col in col_replicas: | |
df[col] = pd.to_numeric(df[col], errors='coerce') | |
# Calcular el promedio y la desviación estándar | |
df[f"Concentración Real Promedio ({unidad_medida})"] = df[col_replicas].mean(axis=1) | |
if n_replicas > 1: | |
df[f"Desviación Estándar ({unidad_medida})"] = df[col_replicas].std(ddof=1, axis=1) | |
else: | |
df[f"Desviación Estándar ({unidad_medida})"] = 0.0 | |
return df | |
def generar_graficos(df_valid, n_replicas, unidad_medida, palette_puntos, estilo_puntos, | |
palette_linea_ajuste, estilo_linea_ajuste, | |
palette_linea_ideal, estilo_linea_ideal, | |
palette_barras_error, mostrar_linea_ajuste, mostrar_linea_ideal, mostrar_puntos): | |
col_predicha_num = "Concentración Predicha Numérica" | |
col_real_promedio = f"Concentración Real Promedio ({unidad_medida})" | |
col_desviacion = f"Desviación Estándar ({unidad_medida})" | |
# Convertir a numérico | |
df_valid[col_predicha_num] = df_valid[col_predicha_num].astype(float) | |
df_valid[col_real_promedio] = df_valid[col_real_promedio].astype(float) | |
df_valid[col_desviacion] = df_valid[col_desviacion].fillna(0).astype(float) | |
# Calcular regresión lineal | |
slope, intercept, r_value, p_value, std_err = stats.linregress(df_valid[col_predicha_num], df_valid[col_real_promedio]) | |
df_valid['Ajuste Lineal'] = intercept + slope * df_valid[col_predicha_num] | |
# Configurar estilos | |
sns.set(style="whitegrid") | |
plt.rcParams.update({'figure.autolayout': True}) | |
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 6)) | |
# Obtener colores de las paletas | |
colors_puntos = sns.color_palette(palette_puntos, as_cmap=False) | |
colors_linea_ajuste = sns.color_palette(palette_linea_ajuste, as_cmap=False) | |
colors_linea_ideal = sns.color_palette(palette_linea_ideal, as_cmap=False) | |
colors_barras_error = sns.color_palette(palette_barras_error, as_cmap=False) | |
# Seleccionar colores | |
color_puntos = colors_puntos[0] | |
color_linea_ajuste = colors_linea_ajuste[0] | |
color_linea_ideal = colors_linea_ideal[0] | |
color_barras_error = colors_barras_error[0] | |
# Gráfico de dispersión con línea de regresión | |
if mostrar_puntos: | |
if n_replicas > 1: | |
# Incluir barras de error | |
ax1.errorbar( | |
df_valid[col_predicha_num], | |
df_valid[col_real_promedio], | |
yerr=df_valid[col_desviacion], | |
fmt=estilo_puntos, | |
color=color_puntos, | |
ecolor=color_barras_error, | |
elinewidth=2, | |
capsize=3, | |
label='Datos Reales' | |
) | |
else: | |
ax1.scatter( | |
df_valid[col_predicha_num], | |
df_valid[col_real_promedio], | |
color=color_puntos, | |
s=100, | |
label='Datos Reales', | |
marker=estilo_puntos | |
) | |
# Línea de ajuste | |
if mostrar_linea_ajuste: | |
ax1.plot( | |
df_valid[col_predicha_num], | |
df_valid['Ajuste Lineal'], | |
color=color_linea_ajuste, | |
label='Ajuste Lineal', | |
linewidth=2, | |
linestyle=estilo_linea_ajuste | |
) | |
# Línea ideal | |
if mostrar_linea_ideal: | |
min_predicha = df_valid[col_predicha_num].min() | |
max_predicha = df_valid[col_predicha_num].max() | |
ax1.plot( | |
[min_predicha, max_predicha], | |
[min_predicha, max_predicha], | |
color=color_linea_ideal, | |
linestyle=estilo_linea_ideal, | |
label='Ideal' | |
) | |
ax1.set_title('Correlación entre Concentración Predicha y Real', fontsize=14) | |
ax1.set_xlabel('Concentración Predicha', fontsize=12) | |
ax1.set_ylabel('Concentración Real Promedio', fontsize=12) | |
# Añadir ecuación y R² en el gráfico | |
ax1.annotate( | |
f'y = {intercept:.2f} + {slope:.2f}x\n$R^2$ = {r_value**2:.4f}', | |
xy=(0.05, 0.95), | |
xycoords='axes fraction', | |
fontsize=12, | |
backgroundcolor='white', | |
verticalalignment='top' | |
) | |
# Posicionar la leyenda | |
ax1.legend(loc='lower right', fontsize=10) | |
# Gráfico de residuos | |
residuos = df_valid[col_real_promedio] - df_valid['Ajuste Lineal'] | |
ax2.scatter( | |
df_valid[col_predicha_num], | |
residuos, | |
color=color_puntos, | |
s=100, | |
marker=estilo_puntos, | |
label='Residuos' | |
) | |
ax2.axhline(y=0, color='black', linestyle='--', linewidth=1) | |
ax2.set_title('Gráfico de Residuos', fontsize=14) | |
ax2.set_xlabel('Concentración Predicha', fontsize=12) | |
ax2.set_ylabel('Residuo', fontsize=12) | |
ax2.legend(loc='upper right', fontsize=10) | |
plt.tight_layout() | |
plt.savefig('grafico.png') # Guardar el gráfico para incluirlo en el informe | |
return fig | |
def evaluar_calidad_calibracion(df_valid, r_squared, rmse, cv_percent): | |
"""Evaluar la calidad de la calibración y proporcionar recomendaciones""" | |
evaluacion = { | |
"calidad": "", | |
"recomendaciones": [], | |
"estado": "✅" if r_squared >= 0.95 and cv_percent <= 15 else "⚠️" | |
} | |
if r_squared >= 0.95: | |
evaluacion["calidad"] = "Excelente" | |
elif r_squared >= 0.90: | |
evaluacion["calidad"] = "Buena" | |
elif r_squared >= 0.85: | |
evaluacion["calidad"] = "Regular" | |
else: | |
evaluacion["calidad"] = "Deficiente" | |
if r_squared < 0.95: | |
evaluacion["recomendaciones"].append("- Considere repetir algunas mediciones para mejorar la correlación") | |
if cv_percent > 15: | |
evaluacion["recomendaciones"].append("- La variabilidad es alta. Revise el procedimiento de dilución") | |
if rmse > 0.1 * df_valid[df_valid.columns[-1]].astype(float).mean(): | |
evaluacion["recomendaciones"].append("- El error de predicción es significativo. Verifique la técnica de medición") | |
return evaluacion | |
def generar_informe_completo(df_valid, n_replicas, unidad_medida): | |
"""Generar un informe completo en formato markdown""" | |
col_predicha_num = "Concentración Predicha Numérica" | |
col_real_promedio = f"Concentración Real Promedio ({unidad_medida})" | |
# Convertir a numérico | |
df_valid[col_predicha_num] = df_valid[col_predicha_num].astype(float) | |
df_valid[col_real_promedio] = df_valid[col_real_promedio].astype(float) | |
# Calcular estadísticas | |
slope, intercept, r_value, p_value, std_err = stats.linregress(df_valid[col_predicha_num], df_valid[col_real_promedio]) | |
r_squared = r_value ** 2 | |
rmse = np.sqrt(((df_valid[col_real_promedio] - (intercept + slope * df_valid[col_predicha_num])) ** 2).mean()) | |
cv = (df_valid[col_real_promedio].std() / df_valid[col_real_promedio].mean()) * 100 # CV de los valores reales | |
# Evaluar calidad | |
evaluacion = evaluar_calidad_calibracion(df_valid, r_squared, rmse, cv) | |
informe = f"""# Informe de Calibración {evaluacion['estado']} | |
Fecha: {datetime.now().strftime('%d/%m/%Y %H:%M')} | |
## Resumen Estadístico | |
- **Ecuación de Regresión**: y = {intercept:.4f} + {slope:.4f}x | |
- **Coeficiente de correlación (r)**: {r_value:.4f} | |
- **Coeficiente de determinación ($R^2$)**: {r_squared:.4f} | |
- **Valor p**: {p_value:.4e} | |
- **Error estándar de la pendiente**: {std_err:.4f} | |
- **Error cuadrático medio (RMSE)**: {rmse:.4f} | |
- **Coeficiente de variación (CV)**: {cv:.2f}% | |
## Evaluación de Calidad | |
- **Calidad de la calibración**: {evaluacion['calidad']} | |
## Recomendaciones | |
{chr(10).join(evaluacion['recomendaciones']) if evaluacion['recomendaciones'] else "No hay recomendaciones específicas. La calibración cumple con los criterios de calidad."} | |
## Decisión | |
{("✅ APROBADO - La calibración cumple con los criterios de calidad establecidos" if evaluacion['estado'] == "✅" else "⚠️ REQUIERE REVISIÓN - La calibración necesita ajustes según las recomendaciones anteriores")} | |
--- | |
*Nota: Este informe fue generado automáticamente. Por favor, revise los resultados y valide según sus criterios específicos.* | |
""" | |
return informe, evaluacion['estado'] | |
def actualizar_analisis(df, n_replicas, unidad_medida): | |
if df is None or df.empty: | |
return "Error en los datos", None, "No se pueden generar análisis", df | |
# Calcular promedio y desviación estándar dependiendo de las réplicas | |
df = calcular_promedio_desviacion(df, n_replicas, unidad_medida) | |
col_predicha_num = "Concentración Predicha Numérica" | |
col_real_promedio = f"Concentración Real Promedio ({unidad_medida})" | |
# Convertir columnas a numérico | |
df[col_predicha_num] = pd.to_numeric(df[col_predicha_num], errors='coerce') | |
df[col_real_promedio] = pd.to_numeric(df[col_real_promedio], errors='coerce') | |
df_valid = df.dropna(subset=[col_predicha_num, col_real_promedio]) | |
if len(df_valid) < 2: | |
return "Se necesitan más datos", None, "Se requieren al menos dos valores reales para el análisis", df | |
# Calcular la regresión y agregar 'Ajuste Lineal' | |
slope, intercept, r_value, p_value, std_err = stats.linregress(df_valid[col_predicha_num], df_valid[col_real_promedio]) | |
df_valid['Ajuste Lineal'] = intercept + slope * df_valid[col_predicha_num] | |
# Generar gráfico con opciones predeterminadas | |
fig = generar_graficos( | |
df_valid, n_replicas, unidad_medida, | |
palette_puntos='deep', estilo_puntos='o', | |
palette_linea_ajuste='muted', estilo_linea_ajuste='-', | |
palette_linea_ideal='bright', estilo_linea_ideal='--', | |
palette_barras_error='pastel', | |
mostrar_linea_ajuste=True, mostrar_linea_ideal=True, mostrar_puntos=True | |
) | |
informe, estado = generar_informe_completo(df_valid, n_replicas, unidad_medida) | |
return estado, fig, informe, df | |
def actualizar_graficos(df, n_replicas, unidad_medida, | |
palette_puntos, estilo_puntos, | |
palette_linea_ajuste, estilo_linea_ajuste, | |
palette_linea_ideal, estilo_linea_ideal, | |
palette_barras_error, | |
mostrar_linea_ajuste, mostrar_linea_ideal, mostrar_puntos): | |
if df is None or df.empty: | |
return None | |
# Asegurarse de que los cálculos estén actualizados | |
df = calcular_promedio_desviacion(df, n_replicas, unidad_medida) | |
col_predicha_num = "Concentración Predicha Numérica" | |
col_real_promedio = f"Concentración Real Promedio ({unidad_medida})" | |
# Convertir columnas a numérico | |
df[col_predicha_num] = pd.to_numeric(df[col_predicha_num], errors='coerce') | |
df[col_real_promedio] = pd.to_numeric(df[col_real_promedio], errors='coerce') | |
df_valid = df.dropna(subset=[col_predicha_num, col_real_promedio]) | |
if len(df_valid) < 2: | |
return None | |
# Generar gráfico con opciones seleccionadas | |
fig = generar_graficos( | |
df_valid, n_replicas, unidad_medida, | |
palette_puntos, estilo_puntos, | |
palette_linea_ajuste, estilo_linea_ajuste, | |
palette_linea_ideal, estilo_linea_ideal, | |
palette_barras_error, | |
mostrar_linea_ajuste, mostrar_linea_ideal, mostrar_puntos | |
) | |
return fig | |
def exportar_informe_word(df_valid, informe_md, unidad_medida): | |
# Crear documento Word | |
doc = docx.Document() | |
# Estilos APA 7 | |
style = doc.styles['Normal'] | |
font = style.font | |
font.name = 'Times New Roman' | |
font.size = Pt(12) | |
# Título centrado | |
titulo = doc.add_heading('Informe de Calibración', 0) | |
titulo.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER | |
# Fecha | |
fecha = doc.add_paragraph(f"Fecha: {datetime.now().strftime('%d/%m/%Y %H:%M')}") | |
fecha.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER | |
# Insertar gráfico | |
if os.path.exists('grafico.png'): | |
doc.add_picture('grafico.png', width=Inches(6)) | |
ultimo_parrafo = doc.paragraphs[-1] | |
ultimo_parrafo.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER | |
# Leyenda del gráfico en estilo APA 7 | |
leyenda = doc.add_paragraph('Figura 1. Gráfico de calibración.') | |
leyenda_format = leyenda.paragraph_format | |
leyenda_format.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER | |
leyenda.style = doc.styles['Caption'] | |
# Agregar contenido del informe | |
doc.add_heading('Resumen Estadístico', level=1) | |
for linea in informe_md.split('\n'): | |
if linea.startswith('##'): | |
doc.add_heading(linea.replace('##', '').strip(), level=2) | |
else: | |
doc.add_paragraph(linea) | |
# Añadir tabla de datos | |
doc.add_heading('Tabla de Datos de Calibración', level=1) | |
# Convertir DataFrame a lista de listas | |
tabla_datos = df_valid.reset_index(drop=True) | |
tabla_datos = tabla_datos.round(4) # Redondear a 4 decimales si es necesario | |
columnas = tabla_datos.columns.tolist() | |
registros = tabla_datos.values.tolist() | |
# Crear tabla en Word | |
tabla = doc.add_table(rows=1 + len(registros), cols=len(columnas)) | |
tabla.style = 'Table Grid' | |
# Añadir los encabezados | |
hdr_cells = tabla.rows[0].cells | |
for idx, col_name in enumerate(columnas): | |
hdr_cells[idx].text = col_name | |
# Añadir los registros | |
for i, registro in enumerate(registros): | |
row_cells = tabla.rows[i + 1].cells | |
for j, valor in enumerate(registro): | |
row_cells[j].text = str(valor) | |
# Formatear fuente de la tabla | |
for row in tabla.rows: | |
for cell in row.cells: | |
for paragraph in cell.paragraphs: | |
paragraph.style = doc.styles['Normal'] | |
# Guardar documento | |
filename = 'informe_calibracion.docx' | |
doc.save(filename) | |
return filename | |
def exportar_informe_latex(df_valid, informe_md): | |
# Generar código LaTeX | |
informe_tex = r"""\documentclass{article} | |
\usepackage[spanish]{babel} | |
\usepackage{amsmath} | |
\usepackage{graphicx} | |
\usepackage{booktabs} | |
\begin{document} | |
""" | |
informe_tex += informe_md.replace('#', '').replace('**', '\\textbf{').replace('*', '\\textit{') | |
informe_tex += r""" | |
\end{document} | |
""" | |
filename = 'informe_calibracion.tex' | |
with open(filename, 'w') as f: | |
f.write(informe_tex) | |
return filename | |
def exportar_word(df, informe_md, unidad_medida): | |
df_valid = df.copy() | |
col_predicha_num = "Concentración Predicha Numérica" | |
col_real_promedio = f"Concentración Real Promedio ({unidad_medida})" | |
# Convertir columnas a numérico | |
df_valid[col_predicha_num] = pd.to_numeric(df_valid[col_predicha_num], errors='coerce') | |
df_valid[col_real_promedio] = pd.to_numeric(df_valid[col_real_promedio], errors='coerce') | |
df_valid = df_valid.dropna(subset=[col_predicha_num, col_real_promedio]) | |
if df_valid.empty: | |
return None | |
filename = exportar_informe_word(df_valid, informe_md, unidad_medida) | |
return filename # Retornamos el nombre del archivo | |
def exportar_latex(df, informe_md): | |
df_valid = df.copy() | |
col_predicha_num = "Concentración Predicha Numérica" | |
col_real_promedio = [col for col in df_valid.columns if 'Real Promedio' in col][0] | |
# Convertir columnas a numérico | |
df_valid[col_predicha_num] = pd.to_numeric(df_valid[col_predicha_num], errors='coerce') | |
df_valid[col_real_promedio] = pd.to_numeric(df_valid[col_real_promedio], errors='coerce') | |
df_valid = df_valid.dropna(subset=[col_predicha_num, col_real_promedio]) | |
if df_valid.empty: | |
return None | |
filename = exportar_informe_latex(df_valid, informe_md) | |
return filename # Retornamos el nombre del archivo | |
# Funciones de ejemplo | |
def cargar_ejemplo_ufc(n_replicas): | |
df = generar_tabla(7, 2000000, "UFC", n_replicas) | |
# Valores reales de ejemplo | |
for i in range(1, n_replicas + 1): | |
valores_reales = [2000000 - (i - 1) * 10000, 1600000 - (i - 1) * 8000, 1200000 - (i - 1) * 6000, | |
800000 - (i - 1) * 4000, 400000 - (i - 1) * 2000, 200000 - (i - 1) * 1000, | |
100000 - (i - 1) * 500] | |
df[f"Concentración Real {i} (UFC)"] = valores_reales | |
return 2000000, "UFC", 7, df | |
def cargar_ejemplo_od(n_replicas): | |
df = generar_tabla(7, 1.0, "OD", n_replicas) | |
# Valores reales de ejemplo | |
for i in range(1, n_replicas + 1): | |
valores_reales = [1.00 - (i - 1) * 0.05, 0.80 - (i - 1) * 0.04, 0.60 - (i - 1) * 0.03, | |
0.40 - (i - 1) * 0.02, 0.20 - (i - 1) * 0.01, 0.10 - (i - 1) * 0.005, | |
0.05 - (i - 1) * 0.002] | |
df[f"Concentración Real {i} (OD)"] = valores_reales | |
return 1.0, "OD", 7, df | |
def limpiar_datos(n_replicas): | |
df = generar_tabla(7, 2000000, "UFC", n_replicas) | |
return ( | |
2000000, # Concentración Inicial | |
"UFC", # Unidad de Medida | |
7, # Número de filas | |
df, # Tabla Output | |
"", # Estado Output | |
None, # Gráficos Output | |
"" # Informe Output | |
) | |
def generar_datos_sinteticos_evento(df, n_replicas, unidad_medida): | |
df = df.copy() | |
col_predicha_num = "Concentración Predicha Numérica" | |
# Generar datos sintéticos para cada réplica | |
for i in range(1, n_replicas + 1): | |
col_real = f"Concentración Real {i} ({unidad_medida})" | |
df[col_predicha_num] = pd.to_numeric(df[col_predicha_num], errors='coerce') | |
desviacion_std = 0.05 * df[col_predicha_num].mean() # 5% de la media como desviación estándar | |
valores_predichos = df[col_predicha_num].astype(float).values | |
datos_sinteticos = valores_predichos + np.random.normal(0, desviacion_std, size=len(valores_predichos)) | |
datos_sinteticos = np.maximum(0, datos_sinteticos) # Asegurar que no haya valores negativos | |
datos_sinteticos = np.round(datos_sinteticos, 2) | |
df[col_real] = datos_sinteticos | |
return df | |
def actualizar_tabla_evento(df, n_filas, concentracion, unidad, n_replicas): | |
# Actualizar tabla sin borrar "Concentración Real" | |
df_new = generar_tabla(n_filas, concentracion, unidad, n_replicas) | |
# Mapear columnas | |
col_real_new = [col for col in df_new.columns if 'Concentración Real' in col and 'Promedio' not in col and 'Desviación' not in col] | |
col_real_old = [col for col in df.columns if 'Concentración Real' in col and 'Promedio' not in col and 'Desviación' not in col] | |
# Reemplazar valores existentes en "Concentración Real" | |
for col_new, col_old in zip(col_real_new, col_real_old): | |
df_new[col_new] = None | |
for idx in df_new.index: | |
if idx in df.index: | |
df_new.at[idx, col_new] = df.at[idx, col_old] | |
return df_new | |
def cargar_excel(file): | |
# Leer el archivo Excel | |
df = pd.read_excel(file.name, sheet_name=None) | |
# Verificar que el archivo tenga al menos dos pestañas | |
if len(df) < 2: | |
return "El archivo debe tener al menos dos pestañas.", None, None, None, None, None, None | |
# Obtener la primera pestaña como referencia | |
primera_pestaña = next(iter(df.values())) | |
concentracion_inicial = primera_pestaña.iloc[0, 0] | |
unidad_medida = primera_pestaña.columns[0].split('(')[-1].split(')')[0] | |
n_filas = len(primera_pestaña) | |
n_replicas = len(df) | |
# Generar la tabla base | |
df_base = generar_tabla(n_filas, concentracion_inicial, unidad_medida, n_replicas) | |
# Llenar la tabla con los datos de cada pestaña | |
for i, (sheet_name, sheet_df) in enumerate(df.items(), start=1): | |
col_real = f"Concentración Real {i} ({unidad_medida})" | |
df_base[col_real] = sheet_df.iloc[:, 1].values | |
return concentracion_inicial, unidad_medida, n_filas, n_replicas, df_base, "", None, "" | |
# Interfaz Gradio | |
with gr.Blocks(theme=gr.themes.Soft()) as interfaz: | |
gr.Markdown(""" | |
# 📊 Sistema Avanzado de Calibración con Análisis Estadístico | |
Configure los parámetros, edite los valores en la tabla y luego presione "Calcular" para obtener el análisis. | |
""") | |
with gr.Tab("📝 Datos de Calibración"): | |
with gr.Row(): | |
concentracion_input = gr.Number( | |
value=2000000, | |
label="Concentración Inicial", | |
precision=0 | |
) | |
unidad_input = gr.Textbox( | |
value="UFC", | |
label="Unidad de Medida", | |
placeholder="UFC, OD, etc..." | |
) | |
filas_slider = gr.Slider( | |
minimum=1, | |
maximum=20, | |
value=7, | |
step=1, | |
label="Número de filas" | |
) | |
decimales_slider = gr.Slider( | |
minimum=0, | |
maximum=5, | |
value=0, | |
step=1, | |
label="Número de Decimales" | |
) | |
replicas_slider = gr.Slider( | |
minimum=1, | |
maximum=10, | |
value=1, | |
step=1, | |
label="Número de Réplicas" | |
) | |
with gr.Row(): | |
calcular_btn = gr.Button("🔄 Calcular", variant="primary") | |
limpiar_btn = gr.Button("🗑 Limpiar Datos", variant="secondary") | |
ajustar_decimales_btn = gr.Button("🛠 Ajustar Decimales", variant="secondary") | |
with gr.Row(): | |
ejemplo_ufc_btn = gr.Button("📋 Cargar Ejemplo UFC", variant="secondary") | |
ejemplo_od_btn = gr.Button("📋 Cargar Ejemplo OD", variant="secondary") | |
sinteticos_btn = gr.Button("🧪 Generar Datos Sintéticos", variant="secondary") | |
cargar_excel_btn = gr.UploadButton("📂 Cargar Excel", file_types=[".xlsx"], variant="secondary") | |
tabla_output = gr.DataFrame( | |
wrap=True, | |
label="Tabla de Datos", | |
interactive=True, | |
type="pandas", | |
) | |
with gr.Tab("📊 Análisis y Reporte"): | |
estado_output = gr.Textbox(label="Estado", interactive=False) | |
graficos_output = gr.Plot(label="Gráficos de Análisis") | |
# Opciones y botones debajo del gráfico | |
with gr.Row(): | |
# Paletas de colores disponibles en Seaborn | |
paletas_colores = ["deep", "muted", "pastel", "bright", "dark", "colorblind", "Set1", "Set2", "Set3"] | |
palette_puntos_dropdown = gr.Dropdown( | |
choices=paletas_colores, | |
value="deep", | |
label="Paleta para Puntos" | |
) | |
estilo_puntos_dropdown = gr.Dropdown( | |
choices=["o", "s", "^", "D", "v", "<", ">", "h", "H", "p", "*", "X", "d"], | |
value="o", | |
label="Estilo de Puntos" | |
) | |
palette_linea_ajuste_dropdown = gr.Dropdown( | |
choices=paletas_colores, | |
value="muted", | |
label="Paleta Línea de Ajuste" | |
) | |
estilo_linea_ajuste_dropdown = gr.Dropdown( | |
choices=["-", "--", "-.", ":"], | |
value="-", | |
label="Estilo Línea de Ajuste" | |
) | |
with gr.Row(): | |
palette_linea_ideal_dropdown = gr.Dropdown( | |
choices=paletas_colores, | |
value="bright", | |
label="Paleta Línea Ideal" | |
) | |
estilo_linea_ideal_dropdown = gr.Dropdown( | |
choices=["--", "-", "-.", ":"], | |
value="--", | |
label="Estilo Línea Ideal" | |
) | |
palette_barras_error_dropdown = gr.Dropdown( | |
choices=paletas_colores, | |
value="pastel", | |
label="Paleta Barras de Error" | |
) | |
mostrar_linea_ajuste = gr.Checkbox(value=True, label="Mostrar Línea de Ajuste") | |
mostrar_linea_ideal = gr.Checkbox(value=True, label="Mostrar Línea Ideal") | |
mostrar_puntos = gr.Checkbox(value=True, label="Mostrar Puntos") | |
graficar_btn = gr.Button("📊 Graficar", variant="primary") | |
with gr.Row(): | |
copiar_btn = gr.Button("📋 Copiar Informe", variant="secondary") | |
exportar_word_btn = gr.Button("💾 Exportar Informe Word", variant="primary") | |
exportar_latex_btn = gr.Button("💾 Exportar Informe LaTeX", variant="primary") | |
with gr.Row(): | |
exportar_word_file = gr.File(label="Informe en Word") | |
exportar_latex_file = gr.File(label="Informe en LaTeX") | |
# Informe al final | |
informe_output = gr.Markdown(elem_id="informe_output") | |
# Eventos | |
input_components = [tabla_output] | |
output_components = [estado_output, graficos_output, informe_output, tabla_output] | |
# Evento al presionar el botón Calcular | |
calcular_btn.click( | |
fn=actualizar_analisis, | |
inputs=[tabla_output, replicas_slider, unidad_input], | |
outputs=output_components | |
) | |
# Evento para graficar con opciones seleccionadas | |
graficar_btn.click( | |
fn=actualizar_graficos, | |
inputs=[ | |
tabla_output, replicas_slider, unidad_input, | |
palette_puntos_dropdown, estilo_puntos_dropdown, | |
palette_linea_ajuste_dropdown, estilo_linea_ajuste_dropdown, | |
palette_linea_ideal_dropdown, estilo_linea_ideal_dropdown, | |
palette_barras_error_dropdown, | |
mostrar_linea_ajuste, mostrar_linea_ideal, mostrar_puntos | |
], | |
outputs=graficos_output | |
) | |
# Evento para limpiar datos | |
limpiar_btn.click( | |
fn=limpiar_datos, | |
inputs=[replicas_slider], | |
outputs=[concentracion_input, unidad_input, filas_slider, tabla_output, estado_output, graficos_output, informe_output] | |
) | |
# Eventos de los botones de ejemplo | |
ejemplo_ufc_btn.click( | |
fn=cargar_ejemplo_ufc, | |
inputs=[replicas_slider], | |
outputs=[concentracion_input, unidad_input, filas_slider, tabla_output] | |
) | |
ejemplo_od_btn.click( | |
fn=cargar_ejemplo_od, | |
inputs=[replicas_slider], | |
outputs=[concentracion_input, unidad_input, filas_slider, tabla_output] | |
) | |
# Evento para generar datos sintéticos | |
sinteticos_btn.click( | |
fn=generar_datos_sinteticos_evento, | |
inputs=[tabla_output, replicas_slider, unidad_input], | |
outputs=tabla_output | |
) | |
# Evento para cargar archivo Excel | |
cargar_excel_btn.upload( | |
fn=cargar_excel, | |
inputs=[cargar_excel_btn], | |
outputs=[concentracion_input, unidad_input, filas_slider, replicas_slider, tabla_output, estado_output, graficos_output, informe_output] | |
) | |
# Evento al presionar el botón Ajustar Decimales | |
ajustar_decimales_btn.click( | |
fn=ajustar_decimales_evento, | |
inputs=[tabla_output, decimales_slider], | |
outputs=tabla_output | |
) | |
# Actualizar tabla al cambiar los parámetros (sin borrar "Concentración Real") | |
def actualizar_tabla_wrapper(df, filas, conc, unidad, replicas): | |
return actualizar_tabla_evento(df, filas, conc, unidad, replicas) | |
concentracion_input.change( | |
fn=actualizar_tabla_wrapper, | |
inputs=[tabla_output, filas_slider, concentracion_input, unidad_input, replicas_slider], | |
outputs=tabla_output | |
) | |
unidad_input.change( | |
fn=actualizar_tabla_wrapper, | |
inputs=[tabla_output, filas_slider, concentracion_input, unidad_input, replicas_slider], | |
outputs=tabla_output | |
) | |
filas_slider.change( | |
fn=actualizar_tabla_wrapper, | |
inputs=[tabla_output, filas_slider, concentracion_input, unidad_input, replicas_slider], | |
outputs=tabla_output | |
) | |
replicas_slider.change( | |
fn=actualizar_tabla_wrapper, | |
inputs=[tabla_output, filas_slider, concentracion_input, unidad_input, replicas_slider], | |
outputs=tabla_output | |
) | |
# No agregamos un evento para decimales_slider.change, para evitar borrar la columna "Concentración Real" | |
# Evento de copiar informe utilizando JavaScript | |
copiar_btn.click( | |
None, | |
[], | |
[], | |
js=""" | |
function() { | |
const informeElement = document.querySelector('#informe_output'); | |
const range = document.createRange(); | |
range.selectNode(informeElement); | |
window.getSelection().removeAllRanges(); | |
window.getSelection().addRange(range); | |
document.execCommand('copy'); | |
window.getSelection().removeAllRanges(); | |
alert('Informe copiado al portapapeles'); | |
} | |
""" | |
) | |
# Eventos de exportar informes | |
exportar_word_btn.click( | |
fn=exportar_word, | |
inputs=[tabla_output, informe_output, unidad_input], | |
outputs=exportar_word_file | |
) | |
exportar_latex_btn.click( | |
fn=exportar_latex, | |
inputs=[tabla_output, informe_output], | |
outputs=exportar_latex_file | |
) | |
# Inicializar la interfaz con el ejemplo base | |
def iniciar_con_ejemplo(): | |
n_replicas = 1 | |
df = generar_tabla(7, 2000000, "UFC", n_replicas) | |
# Valores reales de ejemplo | |
df[f"Concentración Real 1 (UFC)"] = [2000000, 1600000, 1200000, 800000, 400000, 200000, 100000] | |
estado, fig, informe, df = actualizar_analisis(df, n_replicas, "UFC") | |
return ( | |
2000000, | |
"UFC", | |
7, | |
df, | |
estado, | |
fig, | |
informe | |
) | |
interfaz.load( | |
fn=iniciar_con_ejemplo, | |
outputs=[concentracion_input, unidad_input, filas_slider, tabla_output, estado_output, graficos_output, informe_output] | |
) | |
# Lanzar la interfaz | |
if __name__ == "__main__": | |
interfaz.launch() | |