C2MV commited on
Commit
7edb855
verified
1 Parent(s): 01d2cba

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +99 -764
app.py CHANGED
@@ -9,6 +9,7 @@ import docx
9
  from docx.shared import Inches, Pt
10
  from docx.enum.text import WD_PARAGRAPH_ALIGNMENT
11
  import os
 
12
 
13
  def generar_tabla(n_filas, concentracion_inicial, unidad_medida, n_replicas):
14
  valores_base = [1.000, 0.800, 0.600, 0.400, 0.200, 0.100, 0.050]
@@ -56,87 +57,43 @@ def ajustar_decimales_evento(df, decimales):
56
  pass
57
  return df
58
 
59
- def calcular_promedio_desviacion(df, n_replicas, unidad_medida, decimales):
60
- df = df.copy()
61
- # Obtener las columnas de r茅plicas
62
- col_replicas = [f"Concentraci贸n Real {i} ({unidad_medida})" for i in range(1, n_replicas + 1)]
63
- # Convertir a num茅rico
64
- for col in col_replicas:
65
- df[col] = pd.to_numeric(df[col], errors='coerce')
66
-
67
- # Calcular el promedio y la desviaci贸n est谩ndar
68
- df[f"Concentraci贸n Real Promedio ({unidad_medida})"] = df[col_replicas].mean(axis=1)
69
-
70
- if n_replicas > 1:
71
- df[f"Desviaci贸n Est谩ndar ({unidad_medida})"] = df[col_replicas].std(ddof=1, axis=1)
72
- else:
73
- df[f"Desviaci贸n Est谩ndar ({unidad_medida})"] = 0.0
74
-
75
- # Redondear al n煤mero de decimales especificado
76
- df[f"Concentraci贸n Real Promedio ({unidad_medida})"] = df[f"Concentraci贸n Real Promedio ({unidad_medida})"].round(decimales)
77
- df[f"Desviaci贸n Est谩ndar ({unidad_medida})"] = df[f"Desviaci贸n Est谩ndar ({unidad_medida})"].round(decimales)
78
-
79
- return df
80
-
81
- def generar_graficos(df_valid, n_replicas, unidad_medida, palette_puntos, estilo_puntos,
82
  palette_linea_ajuste, estilo_linea_ajuste,
83
- palette_linea_ideal, estilo_linea_ideal,
84
- palette_barras_error, mostrar_linea_ajuste, mostrar_linea_ideal, mostrar_puntos):
85
  col_predicha_num = "Concentraci贸n Predicha Num茅rica"
86
- col_real_promedio = f"Concentraci贸n Real Promedio ({unidad_medida})"
87
- col_desviacion = f"Desviaci贸n Est谩ndar ({unidad_medida})"
88
 
89
  # Convertir a num茅rico
90
  df_valid[col_predicha_num] = df_valid[col_predicha_num].astype(float)
91
- df_valid[col_real_promedio] = df_valid[col_real_promedio].astype(float)
92
- df_valid[col_desviacion] = df_valid[col_desviacion].fillna(0).astype(float)
93
 
94
  # Calcular regresi贸n lineal
95
- slope, intercept, r_value, p_value, std_err = stats.linregress(df_valid[col_predicha_num], df_valid[col_real_promedio])
96
  df_valid['Ajuste Lineal'] = intercept + slope * df_valid[col_predicha_num]
97
 
98
  # Configurar estilos
99
  sns.set(style="whitegrid")
100
  plt.rcParams.update({'figure.autolayout': True})
101
 
102
- fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 6))
103
 
104
  # Obtener colores de las paletas
105
  colors_puntos = sns.color_palette(palette_puntos, as_cmap=False)
106
  colors_linea_ajuste = sns.color_palette(palette_linea_ajuste, as_cmap=False)
107
- colors_linea_ideal = sns.color_palette(palette_linea_ideal, as_cmap=False)
108
- colors_barras_error = sns.color_palette(palette_barras_error, as_cmap=False)
109
 
110
  # Seleccionar colores
111
  color_puntos = colors_puntos[0]
112
  color_linea_ajuste = colors_linea_ajuste[0]
113
- color_linea_ideal = colors_linea_ideal[0]
114
- color_barras_error = colors_barras_error[0]
115
 
116
  # Gr谩fico de dispersi贸n con l铆nea de regresi贸n
117
  if mostrar_puntos:
118
- if n_replicas > 1:
119
- # Incluir barras de error
120
- ax1.errorbar(
121
- df_valid[col_predicha_num],
122
- df_valid[col_real_promedio],
123
- yerr=df_valid[col_desviacion],
124
- fmt=estilo_puntos,
125
- color=color_puntos,
126
- ecolor=color_barras_error,
127
- elinewidth=2,
128
- capsize=3,
129
- label='Datos Reales'
130
- )
131
- else:
132
- ax1.scatter(
133
- df_valid[col_predicha_num],
134
- df_valid[col_real_promedio],
135
- color=color_puntos,
136
- s=100,
137
- label='Datos Reales',
138
- marker=estilo_puntos
139
- )
140
 
141
  # L铆nea de ajuste
142
  if mostrar_linea_ajuste:
@@ -149,21 +106,9 @@ def generar_graficos(df_valid, n_replicas, unidad_medida, palette_puntos, estilo
149
  linestyle=estilo_linea_ajuste
150
  )
151
 
152
- # L铆nea ideal
153
- if mostrar_linea_ideal:
154
- min_predicha = df_valid[col_predicha_num].min()
155
- max_predicha = df_valid[col_predicha_num].max()
156
- ax1.plot(
157
- [min_predicha, max_predicha],
158
- [min_predicha, max_predicha],
159
- color=color_linea_ideal,
160
- linestyle=estilo_linea_ideal,
161
- label='Ideal'
162
- )
163
-
164
- ax1.set_title('Correlaci贸n entre Concentraci贸n Predicha y Real', fontsize=14)
165
  ax1.set_xlabel('Concentraci贸n Predicha', fontsize=12)
166
- ax1.set_ylabel('Concentraci贸n Real Promedio', fontsize=12)
167
 
168
  # A帽adir ecuaci贸n y R虏 en el gr谩fico
169
  ax1.annotate(
@@ -178,23 +123,6 @@ def generar_graficos(df_valid, n_replicas, unidad_medida, palette_puntos, estilo
178
  # Posicionar la leyenda
179
  ax1.legend(loc='lower right', fontsize=10)
180
 
181
- # Gr谩fico de residuos
182
- residuos = df_valid[col_real_promedio] - df_valid['Ajuste Lineal']
183
- ax2.scatter(
184
- df_valid[col_predicha_num],
185
- residuos,
186
- color=color_puntos,
187
- s=100,
188
- marker=estilo_puntos,
189
- label='Residuos'
190
- )
191
-
192
- ax2.axhline(y=0, color='black', linestyle='--', linewidth=1)
193
- ax2.set_title('Gr谩fico de Residuos', fontsize=14)
194
- ax2.set_xlabel('Concentraci贸n Predicha', fontsize=12)
195
- ax2.set_ylabel('Residuo', fontsize=12)
196
- ax2.legend(loc='upper right', fontsize=10)
197
-
198
  plt.tight_layout()
199
  plt.savefig('grafico.png') # Guardar el gr谩fico para incluirlo en el informe
200
  return fig
@@ -227,20 +155,19 @@ def evaluar_calidad_calibracion(df_valid, r_squared, rmse, cv_percent):
227
 
228
  return evaluacion
229
 
230
- def generar_informe_completo(df_valid, n_replicas, unidad_medida):
231
  """Generar un informe completo en formato markdown"""
232
  col_predicha_num = "Concentraci贸n Predicha Num茅rica"
233
- col_real_promedio = f"Concentraci贸n Real Promedio ({unidad_medida})"
234
 
235
  # Convertir a num茅rico
236
  df_valid[col_predicha_num] = df_valid[col_predicha_num].astype(float)
237
- df_valid[col_real_promedio] = df_valid[col_real_promedio].astype(float)
238
 
239
  # Calcular estad铆sticas
240
- slope, intercept, r_value, p_value, std_err = stats.linregress(df_valid[col_predicha_num], df_valid[col_real_promedio])
241
  r_squared = r_value ** 2
242
- rmse = np.sqrt(((df_valid[col_real_promedio] - (intercept + slope * df_valid[col_predicha_num])) ** 2).mean())
243
- cv = (df_valid[col_real_promedio].std() / df_valid[col_real_promedio].mean()) * 100 # CV de los valores reales
244
 
245
  # Evaluar calidad
246
  evaluacion = evaluar_calidad_calibracion(df_valid, r_squared, rmse, cv)
@@ -271,7 +198,7 @@ Fecha: {datetime.now().strftime('%d/%m/%Y %H:%M')}
271
  """
272
  return informe, evaluacion['estado']
273
 
274
- def actualizar_analisis(df, n_replicas, unidad_medida, filas_seleccionadas, decimales):
275
  if df is None or df.empty:
276
  return "Error en los datos", None, "No se pueden generar an谩lisis", df
277
 
@@ -281,67 +208,64 @@ def actualizar_analisis(df, n_replicas, unidad_medida, filas_seleccionadas, deci
281
 
282
  indices_seleccionados = [int(s.split(' ')[1]) - 1 for s in filas_seleccionadas]
283
 
284
- # Calcular promedio y desviaci贸n est谩ndar dependiendo de las r茅plicas
285
- df = calcular_promedio_desviacion(df, n_replicas, unidad_medida, decimales)
286
-
287
  col_predicha_num = "Concentraci贸n Predicha Num茅rica"
288
- col_real_promedio = f"Concentraci贸n Real Promedio ({unidad_medida})"
 
 
 
289
 
290
  # Convertir columnas a num茅rico
291
  df[col_predicha_num] = pd.to_numeric(df[col_predicha_num], errors='coerce')
292
- df[col_real_promedio] = pd.to_numeric(df[col_real_promedio], errors='coerce')
293
 
294
- df_valid = df.dropna(subset=[col_predicha_num, col_real_promedio])
295
 
296
- # Resetear el 铆ndice para asegurar que sea secuencial
297
  df_valid.reset_index(drop=True, inplace=True)
298
 
299
  # Filtrar filas seg煤n las seleccionadas
300
  df_valid = df_valid.loc[indices_seleccionados]
301
 
302
  if len(df_valid) < 2:
303
- return "Se necesitan m谩s datos", None, "Se requieren al menos dos valores reales para el an谩lisis", df
304
 
305
- # Calcular la regresi贸n y agregar 'Ajuste Lineal'
306
- slope, intercept, r_value, p_value, std_err = stats.linregress(df_valid[col_predicha_num], df_valid[col_real_promedio])
307
  df_valid['Ajuste Lineal'] = intercept + slope * df_valid[col_predicha_num]
308
 
309
  # Generar gr谩fico con opciones predeterminadas
310
  fig = generar_graficos(
311
- df_valid, n_replicas, unidad_medida,
312
  palette_puntos='deep', estilo_puntos='o',
313
  palette_linea_ajuste='muted', estilo_linea_ajuste='-',
314
- palette_linea_ideal='bright', estilo_linea_ideal='--',
315
- palette_barras_error='pastel',
316
  mostrar_linea_ajuste=True,
317
- mostrar_linea_ideal=False, # L铆nea Ideal desmarcada por defecto
318
  mostrar_puntos=True
319
  )
320
- informe, estado = generar_informe_completo(df_valid, n_replicas, unidad_medida)
321
 
322
  return estado, fig, informe, df
323
 
324
- def actualizar_graficos(df, n_replicas, unidad_medida,
325
  palette_puntos, estilo_puntos,
326
  palette_linea_ajuste, estilo_linea_ajuste,
327
- palette_linea_ideal, estilo_linea_ideal,
328
- palette_barras_error,
329
- mostrar_linea_ajuste, mostrar_linea_ideal, mostrar_puntos,
330
- filas_seleccionadas, decimales):
331
  if df is None or df.empty:
332
  return None
333
 
334
  # Asegurarse de que los c谩lculos est茅n actualizados
335
- df = calcular_promedio_desviacion(df, n_replicas, unidad_medida, decimales)
336
-
337
  col_predicha_num = "Concentraci贸n Predicha Num茅rica"
338
- col_real_promedio = f"Concentraci贸n Real Promedio ({unidad_medida})"
 
 
 
339
 
340
  # Convertir columnas a num茅rico
341
  df[col_predicha_num] = pd.to_numeric(df[col_predicha_num], errors='coerce')
342
- df[col_real_promedio] = pd.to_numeric(df[col_real_promedio], errors='coerce')
343
 
344
- df_valid = df.dropna(subset=[col_predicha_num, col_real_promedio])
345
 
346
  # Resetear el 铆ndice para asegurar que sea secuencial
347
  df_valid.reset_index(drop=True, inplace=True)
@@ -360,411 +284,56 @@ def actualizar_graficos(df, n_replicas, unidad_medida,
360
 
361
  # Generar gr谩fico con opciones seleccionadas
362
  fig = generar_graficos(
363
- df_valid, n_replicas, unidad_medida,
364
  palette_puntos, estilo_puntos,
365
  palette_linea_ajuste, estilo_linea_ajuste,
366
- palette_linea_ideal, estilo_linea_ideal,
367
- palette_barras_error,
368
- mostrar_linea_ajuste, mostrar_linea_ideal, mostrar_puntos
369
  )
370
 
371
  return fig
372
 
373
- def exportar_informe_word(df_valid, informe_md, unidad_medida):
374
- # Crear documento Word
375
- doc = docx.Document()
376
-
377
- # Estilos APA 7
378
- style = doc.styles['Normal']
379
- font = style.font
380
- font.name = 'Times New Roman'
381
- font.size = Pt(12)
382
-
383
- # T铆tulo centrado
384
- titulo = doc.add_heading('Informe de Calibraci贸n', 0)
385
- titulo.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
386
-
387
- # Fecha
388
- fecha = doc.add_paragraph(f"Fecha: {datetime.now().strftime('%d/%m/%Y %H:%M')}")
389
- fecha.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
390
-
391
- # Insertar gr谩fico
392
- if os.path.exists('grafico.png'):
393
- doc.add_picture('grafico.png', width=Inches(6))
394
- ultimo_parrafo = doc.paragraphs[-1]
395
- ultimo_parrafo.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
396
-
397
- # Leyenda del gr谩fico en estilo APA 7
398
- leyenda = doc.add_paragraph('Figura 1. Gr谩fico de calibraci贸n.')
399
- leyenda_format = leyenda.paragraph_format
400
- leyenda_format.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
401
- leyenda.style = doc.styles['Caption']
402
-
403
- # Agregar contenido del informe
404
- doc.add_heading('Resumen Estad铆stico', level=1)
405
- for linea in informe_md.split('\n'):
406
- if linea.startswith('##'):
407
- doc.add_heading(linea.replace('##', '').strip(), level=2)
408
- else:
409
- doc.add_paragraph(linea)
410
-
411
- # A帽adir tabla de datos
412
- doc.add_heading('Tabla de Datos de Calibraci贸n', level=1)
413
-
414
- # Convertir DataFrame a lista de listas
415
- tabla_datos = df_valid.reset_index(drop=True)
416
- tabla_datos = tabla_datos.round(4) # Redondear a 4 decimales si es necesario
417
- columnas = tabla_datos.columns.tolist()
418
- registros = tabla_datos.values.tolist()
419
-
420
- # Crear tabla en Word
421
- tabla = doc.add_table(rows=1 + len(registros), cols=len(columnas))
422
- tabla.style = 'Table Grid'
423
-
424
- # A帽adir los encabezados
425
- hdr_cells = tabla.rows[0].cells
426
- for idx, col_name in enumerate(columnas):
427
- hdr_cells[idx].text = col_name
428
-
429
- # A帽adir los registros
430
- for i, registro in enumerate(registros):
431
- row_cells = tabla.rows[i + 1].cells
432
- for j, valor in enumerate(registro):
433
- row_cells[j].text = str(valor)
434
-
435
- # Formatear fuente de la tabla
436
- for row in tabla.rows:
437
- for cell in row.cells:
438
- for paragraph in cell.paragraphs:
439
- paragraph.style = doc.styles['Normal']
440
-
441
- # Guardar documento
442
- filename = 'informe_calibracion.docx'
443
- doc.save(filename)
444
- return filename
445
-
446
- def exportar_informe_latex(df_valid, informe_md):
447
- # Generar c贸digo LaTeX
448
- informe_tex = r"""\documentclass{article}
449
- \usepackage[spanish]{babel}
450
- \usepackage{amsmath}
451
- \usepackage{graphicx}
452
- \usepackage{booktabs}
453
- \begin{document}
454
- """
455
- informe_tex += informe_md.replace('#', '').replace('**', '\\textbf{').replace('*', '\\textit{')
456
- informe_tex += r"""
457
- \end{document}
458
- """
459
- filename = 'informe_calibracion.tex'
460
- with open(filename, 'w') as f:
461
- f.write(informe_tex)
462
- return filename
463
-
464
- def exportar_word(df, informe_md, unidad_medida, filas_seleccionadas):
465
- df_valid = df.copy()
466
- col_predicha_num = "Concentraci贸n Predicha Num茅rica"
467
- col_real_promedio = f"Concentraci贸n Real Promedio ({unidad_medida})"
468
-
469
- # Convertir columnas a num茅rico
470
- df_valid[col_predicha_num] = pd.to_numeric(df_valid[col_predicha_num], errors='coerce')
471
- df_valid[col_real_promedio] = pd.to_numeric(df_valid[col_real_promedio], errors='coerce')
472
-
473
- df_valid = df_valid.dropna(subset=[col_predicha_num, col_real_promedio])
474
-
475
- # Resetear el 铆ndice
476
- df_valid.reset_index(drop=True, inplace=True)
477
-
478
- # Convertir filas_seleccionadas a 铆ndices
479
- if not filas_seleccionadas:
480
- return None
481
-
482
- indices_seleccionados = [int(s.split(' ')[1]) - 1 for s in filas_seleccionadas]
483
-
484
- # Filtrar filas seg煤n las seleccionadas
485
- df_valid = df_valid.loc[indices_seleccionados]
486
-
487
- if df_valid.empty:
488
- return None
489
-
490
- filename = exportar_informe_word(df_valid, informe_md, unidad_medida)
491
-
492
- return filename # Retornamos el nombre del archivo
493
-
494
- def exportar_latex(df, informe_md, filas_seleccionadas):
495
- df_valid = df.copy()
496
- col_predicha_num = "Concentraci贸n Predicha Num茅rica"
497
- col_real_promedio = [col for col in df_valid.columns if 'Real Promedio' in col][0]
498
-
499
- # Convertir columnas a num茅rico
500
- df_valid[col_predicha_num] = pd.to_numeric(df_valid[col_predicha_num], errors='coerce')
501
- df_valid[col_real_promedio] = pd.to_numeric(df_valid[col_real_promedio], errors='coerce')
502
-
503
- df_valid = df_valid.dropna(subset=[col_predicha_num, col_real_promedio])
504
-
505
- # Resetear el 铆ndice
506
- df_valid.reset_index(drop=True, inplace=True)
507
-
508
- # Convertir filas_seleccionadas a 铆ndices
509
- if not filas_seleccionadas:
510
- return None
511
-
512
- indices_seleccionados = [int(s.split(' ')[1]) - 1 for s in filas_seleccionadas]
513
-
514
- # Filtrar filas seg煤n las seleccionadas
515
- df_valid = df_valid.loc[indices_seleccionados]
516
-
517
- if df_valid.empty:
518
- return None
519
-
520
- filename = exportar_informe_latex(df_valid, informe_md)
521
-
522
- return filename # Retornamos el nombre del archivo
523
-
524
- def cargar_ejemplo_ufc(n_replicas):
525
- df = generar_tabla(7, 2000000, "UFC", n_replicas)
526
- # Valores reales de ejemplo
527
- for i in range(1, n_replicas + 1):
528
- valores_reales = [2000000 - (i - 1) * 10000, 1600000 - (i - 1) * 8000, 1200000 - (i - 1) * 6000,
529
- 800000 - (i - 1) * 4000, 400000 - (i - 1) * 2000, 200000 - (i - 1) * 1000,
530
- 100000 - (i - 1) * 500]
531
- df[f"Concentraci贸n Real {i} (UFC)"] = valores_reales
532
- return 2000000, "UFC", 7, df
533
-
534
- def cargar_ejemplo_od(n_replicas):
535
- df = generar_tabla(7, 1.000, "OD", n_replicas)
536
- # Valores reales de ejemplo
537
- for i in range(1, n_replicas + 1):
538
- valores_reales = [1.000 - (i - 1) * 0.050, 0.800 - (i - 1) * 0.040, 0.600 - (i - 1) * 0.030,
539
- 0.400 - (i - 1) * 0.020, 0.200 - (i - 1) * 0.010, 0.100 - (i - 1) * 0.005,
540
- 0.050 - (i - 1) * 0.002]
541
- df[f"Concentraci贸n Real {i} (OD)"] = valores_reales
542
- return 1.000, "OD", 7, df
543
-
544
- def limpiar_datos(n_replicas):
545
- df = generar_tabla(7, 2000000, "UFC", n_replicas)
546
- return (
547
- 2000000, # Concentraci贸n Inicial
548
- "UFC", # Unidad de Medida
549
- 7, # N煤mero de filas
550
- df, # Tabla Output
551
- "", # Estado Output
552
- None, # Gr谩ficos Output
553
- "" # Informe Output
554
- )
555
-
556
- def generar_datos_sinteticos_evento(df, n_replicas, unidad_medida):
557
- df = df.copy()
558
- col_predicha_num = "Concentraci贸n Predicha Num茅rica"
559
-
560
- # Generar datos sint茅ticos para cada r茅plica
561
- for i in range(1, n_replicas + 1):
562
- col_real = f"Concentraci贸n Real {i} ({unidad_medida})"
563
- df[col_predicha_num] = pd.to_numeric(df[col_predicha_num], errors='coerce')
564
- desviacion_std = 0.05 * df[col_predicha_num].mean() # 5% de la media como desviaci贸n est谩ndar
565
- valores_predichos = df[col_predicha_num].astype(float).values
566
- datos_sinteticos = valores_predichos + np.random.normal(0, desviacion_std, size=len(valores_predichos))
567
- datos_sinteticos = np.maximum(0, datos_sinteticos) # Asegurar que no haya valores negativos
568
- datos_sinteticos = np.round(datos_sinteticos, 3)
569
- df[col_real] = datos_sinteticos
570
-
571
- return df
572
-
573
- def actualizar_tabla_evento(df, n_filas, concentracion, unidad, n_replicas, decimales):
574
- # Actualizar tabla sin borrar "Concentraci贸n Real"
575
- df_new = generar_tabla(n_filas, concentracion, unidad, n_replicas)
576
-
577
- # Mapear columnas
578
- 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]
579
- 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]
580
-
581
- # Reemplazar valores existentes en "Concentraci贸n Real"
582
- for col_new, col_old in zip(col_real_new, col_real_old):
583
- df_new[col_new] = None
584
- for idx in df_new.index:
585
- if idx in df.index:
586
- df_new.at[idx, col_new] = df.at[idx, col_old]
587
-
588
- # Ajustar decimales
589
- df_new = ajustar_decimales_evento(df_new, decimales)
590
-
591
- return df_new
592
-
593
  def cargar_excel(file):
594
  # Leer el archivo Excel
595
- df = pd.read_excel(file.name, sheet_name=None)
596
-
597
- # Verificar que el archivo tenga al menos dos pesta帽as
598
- if len(df) < 2:
599
- return "El archivo debe tener al menos dos pesta帽as.", None, None, None, None, None, None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
600
 
601
- # Obtener la primera pesta帽a como referencia
602
- primera_pesta帽a = next(iter(df.values()))
603
- concentracion_inicial = primera_pesta帽a.iloc[0, 0]
604
- unidad_medida = primera_pesta帽a.columns[0].split('(')[-1].split(')')[0]
605
- n_filas = len(primera_pesta帽a)
606
- n_replicas = len(df)
607
 
608
  # Generar la tabla base
609
  df_base = generar_tabla(n_filas, concentracion_inicial, unidad_medida, n_replicas)
610
 
611
- # Llenar la tabla con los datos de cada pesta帽a
612
- for i, (sheet_name, sheet_df) in enumerate(df.items(), start=1):
613
- col_real = f"Concentraci贸n Real {i} ({unidad_medida})"
614
- df_base[col_real] = sheet_df.iloc[:, 1].values
615
-
616
- return concentracion_inicial, unidad_medida, n_filas, n_replicas, df_base, "", None, ""
617
-
618
- def calcular_regresion_tabla_principal(df, unidad_medida, filas_seleccionadas_regresion,
619
- palette_puntos, estilo_puntos,
620
- palette_linea_ajuste, estilo_linea_ajuste,
621
- mostrar_linea_ajuste, mostrar_puntos,
622
- legend_location, decimales,
623
- titulo_grafico_original, titulo_grafico_personalizado,
624
- eje_x_original, eje_y_original,
625
- eje_x_personalizado, eje_y_personalizado):
626
- if df is None or df.empty:
627
- return "Datos insuficientes", None, None, None
628
-
629
- col_concentracion = "Concentraci贸n Predicha Num茅rica"
630
- col_absorbancia = f"Concentraci贸n Real Promedio ({unidad_medida})"
631
- col_desviacion = f"Desviaci贸n Est谩ndar ({unidad_medida})"
632
-
633
- # Calcular promedio y desviaci贸n est谩ndar si es necesario
634
- n_replicas = len([col for col in df.columns if 'Concentraci贸n Real' in col and 'Promedio' not in col and 'Desviaci贸n' not in col])
635
- df = calcular_promedio_desviacion(df, n_replicas, unidad_medida, decimales)
636
-
637
- # Convertir columnas a num茅rico
638
- df[col_concentracion] = pd.to_numeric(df[col_concentracion], errors='coerce')
639
- df[col_absorbancia] = pd.to_numeric(df[col_absorbancia], errors='coerce')
640
- df[col_desviacion] = pd.to_numeric(df[col_desviacion], errors='coerce')
641
-
642
- df_valid = df.dropna(subset=[col_concentracion, col_absorbancia])
643
-
644
- # Resetear el 铆ndice para asegurar que sea secuencial
645
- df_valid.reset_index(drop=True, inplace=True)
646
-
647
- # Asegurar que el gr谩fico original tenga todos los puntos
648
- df_original = df_valid.copy()
649
-
650
- # Convertir filas_seleccionadas a 铆ndices
651
- if not filas_seleccionadas_regresion:
652
- return "Se necesitan m谩s datos", None, None, None
653
-
654
- indices_seleccionados = [int(s.split(' ')[1]) - 1 for s in filas_seleccionadas_regresion]
655
-
656
- # Filtrar filas seg煤n las seleccionadas para el gr谩fico personalizado
657
- df_valid = df_valid.loc[indices_seleccionados]
658
-
659
- if len(df_valid) < 2:
660
- return "Se requieren al menos dos puntos para calcular la regresi贸n", None, None, None
661
-
662
- # Calcular regresi贸n lineal para el gr谩fico personalizado
663
- slope, intercept, r_value, p_value, std_err = stats.linregress(df_valid[col_concentracion], df_valid[col_absorbancia])
664
-
665
- # Generar gr谩fico original (con todos los puntos)
666
- sns.set(style="whitegrid")
667
- fig_original, ax_original = plt.subplots(figsize=(8, 6))
668
-
669
- ax_original.errorbar(
670
- df_original[col_concentracion],
671
- df_original[col_absorbancia],
672
- yerr=df_original[col_desviacion],
673
- fmt='o',
674
- color='blue',
675
- ecolor='gray',
676
- elinewidth=1,
677
- capsize=3,
678
- label='Datos'
679
- )
680
-
681
- # Calcular regresi贸n para todos los puntos
682
- slope_all, intercept_all, r_value_all, p_value_all, std_err_all = stats.linregress(df_original[col_concentracion], df_original[col_absorbancia])
683
-
684
- ax_original.plot(
685
- df_original[col_concentracion],
686
- intercept_all + slope_all * df_original[col_concentracion],
687
- color='red',
688
- linestyle='-',
689
- label='Ajuste Lineal'
690
- )
691
-
692
- # T铆tulo y etiquetas personalizadas para el gr谩fico original
693
- ax_original.set_xlabel(eje_x_original if eje_x_original else 'Concentraci贸n Predicha Num茅rica')
694
- ax_original.set_ylabel(eje_y_original if eje_y_original else f'Concentraci贸n Real Promedio ({unidad_medida})')
695
- ax_original.set_title(titulo_grafico_original if titulo_grafico_original else 'Regresi贸n Lineal: Concentraci贸n Real vs Concentraci贸n Predicha (Original)')
696
-
697
- # Posicionar la leyenda seg煤n la opci贸n seleccionada (por defecto 'lower right')
698
- ax_original.legend(loc=legend_location)
699
-
700
- # A帽adir ecuaci贸n y R虏 en el gr谩fico
701
- ax_original.annotate(
702
- f'y = {intercept_all:.4f} + {slope_all:.4f}x\n$R^2$ = {r_value_all**2:.4f}',
703
- xy=(0.05, 0.95),
704
- xycoords='axes fraction',
705
- fontsize=12,
706
- backgroundcolor='white',
707
- verticalalignment='top'
708
- )
709
-
710
- # Generar gr谩fico personalizado
711
- sns.set(style="whitegrid")
712
- fig_personalizado, ax_personalizado = plt.subplots(figsize=(8, 6))
713
-
714
- # Obtener colores de las paletas
715
- colors_puntos = sns.color_palette(palette_puntos, as_cmap=False)
716
- colors_linea_ajuste = sns.color_palette(palette_linea_ajuste, as_cmap=False)
717
-
718
- color_puntos = colors_puntos[0]
719
- color_linea_ajuste = colors_linea_ajuste[0]
720
-
721
- if mostrar_puntos:
722
- ax_personalizado.errorbar(
723
- df_valid[col_concentracion],
724
- df_valid[col_absorbancia],
725
- yerr=df_valid[col_desviacion],
726
- fmt=estilo_puntos,
727
- color=color_puntos,
728
- ecolor='gray',
729
- elinewidth=1,
730
- capsize=3,
731
- label='Datos'
732
- )
733
-
734
- if mostrar_linea_ajuste:
735
- ax_personalizado.plot(
736
- df_valid[col_concentracion],
737
- intercept + slope * df_valid[col_concentracion],
738
- color=color_linea_ajuste,
739
- linestyle=estilo_linea_ajuste,
740
- label='Ajuste Lineal'
741
- )
742
-
743
- # T铆tulo y etiquetas personalizadas para el gr谩fico personalizado
744
- ax_personalizado.set_xlabel(eje_x_personalizado if eje_x_personalizado else 'Concentraci贸n Predicha Num茅rica')
745
- ax_personalizado.set_ylabel(eje_y_personalizado if eje_y_personalizado else f'Concentraci贸n Real Promedio ({unidad_medida})')
746
- ax_personalizado.set_title(titulo_grafico_personalizado if titulo_grafico_personalizado else 'Regresi贸n Lineal Personalizada')
747
-
748
- # Posicionar la leyenda seg煤n la opci贸n seleccionada
749
- ax_personalizado.legend(loc=legend_location)
750
-
751
- # A帽adir ecuaci贸n y R虏 en el gr谩fico
752
- ax_personalizado.annotate(
753
- f'y = {intercept:.4f} + {slope:.4f}x\n$R^2$ = {r_value**2:.4f}',
754
- xy=(0.05, 0.95),
755
- xycoords='axes fraction',
756
- fontsize=12,
757
- backgroundcolor='white',
758
- verticalalignment='top'
759
- )
760
 
761
- # Crear tabla resumida
762
- df_resumen = df_valid[[col_concentracion, col_absorbancia, col_desviacion]].copy()
763
- df_resumen.columns = ['Concentraci贸n Predicha', 'Absorbancia Promedio', 'Desviaci贸n Est谩ndar']
764
 
765
- return "Regresi贸n calculada exitosamente", fig_original, fig_personalizado, df_resumen
766
 
767
- # Funci贸n corregida para actualizar las opciones de filas
768
  def actualizar_opciones_filas(df):
769
  if df is None or df.empty:
770
  update = gr.update(choices=[], value=[])
@@ -825,6 +394,12 @@ with gr.Blocks(theme=gr.themes.Soft()) as interfaz:
825
  sinteticos_btn = gr.Button("馃И Generar Datos Sint茅ticos", variant="secondary")
826
  cargar_excel_btn = gr.UploadButton("馃搨 Cargar Excel", file_types=[".xlsx"], variant="secondary")
827
 
 
 
 
 
 
 
828
  tabla_output = gr.DataFrame(
829
  wrap=True,
830
  label="Tabla de Datos",
@@ -836,16 +411,13 @@ with gr.Blocks(theme=gr.themes.Soft()) as interfaz:
836
  estado_output = gr.Textbox(label="Estado", interactive=False)
837
  graficos_output = gr.Plot(label="Gr谩ficos de An谩lisis")
838
 
839
- # Reemplazar Multiselect por CheckboxGroup
840
  filas_seleccionadas = gr.CheckboxGroup(
841
  label="Seleccione las filas a incluir en el an谩lisis",
842
  choices=[],
843
  value=[],
844
  )
845
 
846
- # Opciones y botones debajo del gr谩fico
847
  with gr.Row():
848
- # Paletas de colores disponibles en Seaborn
849
  paletas_colores = ["deep", "muted", "pastel", "bright", "dark", "colorblind", "Set1", "Set2", "Set3"]
850
 
851
  palette_puntos_dropdown = gr.Dropdown(
@@ -870,23 +442,7 @@ with gr.Blocks(theme=gr.themes.Soft()) as interfaz:
870
  )
871
 
872
  with gr.Row():
873
- palette_linea_ideal_dropdown = gr.Dropdown(
874
- choices=paletas_colores,
875
- value="bright",
876
- label="Paleta L铆nea Ideal"
877
- )
878
- estilo_linea_ideal_dropdown = gr.Dropdown(
879
- choices=["--", "-", "-.", ":"],
880
- value="--",
881
- label="Estilo L铆nea Ideal"
882
- )
883
- palette_barras_error_dropdown = gr.Dropdown(
884
- choices=paletas_colores,
885
- value="pastel",
886
- label="Paleta Barras de Error"
887
- )
888
  mostrar_linea_ajuste = gr.Checkbox(value=True, label="Mostrar L铆nea de Ajuste")
889
- mostrar_linea_ideal = gr.Checkbox(value=False, label="Mostrar L铆nea Ideal") # Desmarcado por defecto
890
  mostrar_puntos = gr.Checkbox(value=True, label="Mostrar Puntos")
891
  graficar_btn = gr.Button("馃搳 Graficar", variant="primary")
892
 
@@ -899,106 +455,19 @@ with gr.Blocks(theme=gr.themes.Soft()) as interfaz:
899
  exportar_word_file = gr.File(label="Informe en Word")
900
  exportar_latex_file = gr.File(label="Informe en LaTeX")
901
 
902
- # Informe al final
903
  informe_output = gr.Markdown(elem_id="informe_output")
904
 
905
- with gr.Tab("馃搱 Regresi贸n Absorbancia vs Concentraci贸n"):
906
- gr.Markdown("## Ajuste de Regresi贸n utilizando datos de la Tabla Principal")
907
-
908
- # Casillas para seleccionar filas
909
- filas_seleccionadas_regresion = gr.CheckboxGroup(
910
- label="Seleccione las filas a incluir en el an谩lisis de regresi贸n",
911
- choices=[],
912
- value=[],
913
- )
914
-
915
- # Opciones de personalizaci贸n
916
- with gr.Row():
917
- paletas_colores = ["deep", "muted", "pastel", "bright", "dark", "colorblind", "Set1", "Set2", "Set3"]
918
- palette_puntos_regresion = gr.Dropdown(
919
- choices=paletas_colores,
920
- value="deep",
921
- label="Paleta para Puntos"
922
- )
923
- estilo_puntos_regresion = gr.Dropdown(
924
- choices=["o", "s", "^", "D", "v", "<", ">", "h", "H", "p", "*", "X", "d"],
925
- value="o",
926
- label="Estilo de Puntos"
927
- )
928
- palette_linea_ajuste_regresion = gr.Dropdown(
929
- choices=paletas_colores,
930
- value="muted",
931
- label="Paleta L铆nea de Ajuste"
932
- )
933
- estilo_linea_ajuste_regresion = gr.Dropdown(
934
- choices=["-", "--", "-.", ":"],
935
- value="-",
936
- label="Estilo L铆nea de Ajuste"
937
- )
938
- mostrar_linea_ajuste_regresion = gr.Checkbox(value=True, label="Mostrar L铆nea de Ajuste")
939
- mostrar_puntos_regresion = gr.Checkbox(value=True, label="Mostrar Puntos")
940
-
941
- with gr.Row():
942
- legend_location_dropdown = gr.Dropdown(
943
- choices=[
944
- 'best', 'upper right', 'upper left', 'lower left', 'lower right',
945
- 'right', 'center left', 'center right', 'lower center',
946
- 'upper center', 'center'
947
- ],
948
- value='lower right', # Por defecto 'lower right'
949
- label='Ubicaci贸n de la Leyenda'
950
- )
951
-
952
- # Campos de texto para personalizar t铆tulo y ejes
953
- with gr.Row():
954
- titulo_grafico_original = gr.Textbox(
955
- label="T铆tulo del Gr谩fico Original",
956
- placeholder="Regresi贸n Lineal: Concentraci贸n Real vs Concentraci贸n Predicha (Original)"
957
- )
958
- titulo_grafico_personalizado = gr.Textbox(
959
- label="T铆tulo del Gr谩fico Personalizado",
960
- placeholder="Regresi贸n Lineal Personalizada"
961
- )
962
-
963
- with gr.Row():
964
- eje_x_original = gr.Textbox(
965
- label="Etiqueta del Eje X (Gr谩fico Original)",
966
- placeholder="Concentraci贸n Predicha Num茅rica"
967
- )
968
- eje_y_original = gr.Textbox(
969
- label="Etiqueta del Eje Y (Gr谩fico Original)",
970
- placeholder=f"Concentraci贸n Real Promedio ({unidad_input.value})"
971
- )
972
-
973
- with gr.Row():
974
- eje_x_personalizado = gr.Textbox(
975
- label="Etiqueta del Eje X (Gr谩fico Personalizado)",
976
- placeholder="Concentraci贸n Predicha Num茅rica"
977
- )
978
- eje_y_personalizado = gr.Textbox(
979
- label="Etiqueta del Eje Y (Gr谩fico Personalizado)",
980
- placeholder=f"Concentraci贸n Real Promedio ({unidad_input.value})"
981
- )
982
-
983
- calcular_regresion_btn = gr.Button("Calcular Regresi贸n")
984
-
985
- # Salidas
986
- estado_regresion_output = gr.Textbox(label="Estado de la Regresi贸n", interactive=False)
987
- grafico_original_output = gr.Plot(label="Gr谩fico Original")
988
- grafico_personalizado_output = gr.Plot(label="Gr谩fico Personalizado")
989
- tabla_resumen_output = gr.DataFrame(label="Tabla Resumida")
990
-
991
  # Eventos para actualizar las opciones de filas
992
  tabla_output.change(
993
  fn=actualizar_opciones_filas,
994
  inputs=[tabla_output],
995
- outputs=[filas_seleccionadas, filas_seleccionadas_regresion]
996
  )
997
 
998
  # Evento al presionar el bot贸n Calcular
999
  calcular_btn.click(
1000
  fn=actualizar_analisis,
1001
- inputs=[tabla_output, replicas_slider, unidad_input, filas_seleccionadas, decimales_slider],
1002
  outputs=[estado_output, graficos_output, informe_output, tabla_output]
1003
  )
1004
 
@@ -1006,145 +475,26 @@ with gr.Blocks(theme=gr.themes.Soft()) as interfaz:
1006
  graficar_btn.click(
1007
  fn=actualizar_graficos,
1008
  inputs=[
1009
- tabla_output, replicas_slider, unidad_input,
1010
  palette_puntos_dropdown, estilo_puntos_dropdown,
1011
  palette_linea_ajuste_dropdown, estilo_linea_ajuste_dropdown,
1012
- palette_linea_ideal_dropdown, estilo_linea_ideal_dropdown,
1013
- palette_barras_error_dropdown,
1014
- mostrar_linea_ajuste, mostrar_linea_ideal, mostrar_puntos,
1015
- filas_seleccionadas, decimales_slider
1016
  ],
1017
  outputs=graficos_output
1018
  )
1019
 
1020
- # Asegurar que la l铆nea ideal est茅 desmarcada por defecto
1021
- def resetear_linea_ideal():
1022
- return gr.update(value=False)
1023
-
1024
- # Desmarcar 'Mostrar L铆nea Ideal' en eventos de botones
1025
- calcular_btn.click(
1026
- fn=resetear_linea_ideal,
1027
- outputs=mostrar_linea_ideal
1028
- )
1029
- limpiar_btn.click(
1030
- fn=resetear_linea_ideal,
1031
- outputs=mostrar_linea_ideal
1032
- )
1033
- ajustar_decimales_btn.click(
1034
- fn=resetear_linea_ideal,
1035
- outputs=mostrar_linea_ideal
1036
- )
1037
- sinteticos_btn.click(
1038
- fn=resetear_linea_ideal,
1039
- outputs=mostrar_linea_ideal
1040
- )
1041
-
1042
- # Eventos de los botones adicionales, como limpiar, cargar ejemplos, ajustar decimales, etc.
1043
  # Evento para limpiar datos
1044
  limpiar_btn.click(
1045
- fn=limpiar_datos,
1046
- inputs=[replicas_slider],
1047
- outputs=[concentracion_input, unidad_input, filas_slider, tabla_output, estado_output, graficos_output, informe_output]
1048
- )
1049
-
1050
- # Eventos de los botones de ejemplo
1051
- ejemplo_ufc_btn.click(
1052
- fn=cargar_ejemplo_ufc,
1053
- inputs=[replicas_slider],
1054
- outputs=[concentracion_input, unidad_input, filas_slider, tabla_output]
1055
- )
1056
-
1057
- ejemplo_od_btn.click(
1058
- fn=cargar_ejemplo_od,
1059
- inputs=[replicas_slider],
1060
- outputs=[concentracion_input, unidad_input, filas_slider, tabla_output]
1061
- )
1062
-
1063
- # Evento para generar datos sint茅ticos
1064
- sinteticos_btn.click(
1065
- fn=generar_datos_sinteticos_evento,
1066
- inputs=[tabla_output, replicas_slider, unidad_input],
1067
- outputs=tabla_output
1068
  )
1069
 
1070
  # Evento para cargar archivo Excel
1071
  cargar_excel_btn.upload(
1072
  fn=cargar_excel,
1073
  inputs=[cargar_excel_btn],
1074
- outputs=[concentracion_input, unidad_input, filas_slider, replicas_slider, tabla_output, estado_output, graficos_output, informe_output]
1075
- )
1076
-
1077
- # Evento al presionar el bot贸n Ajustar Decimales
1078
- ajustar_decimales_btn.click(
1079
- fn=ajustar_decimales_evento,
1080
- inputs=[tabla_output, decimales_slider],
1081
- outputs=tabla_output
1082
- )
1083
-
1084
- # Actualizar tabla al cambiar los par谩metros (sin borrar "Concentraci贸n Real")
1085
- def actualizar_tabla_wrapper(df, filas, conc, unidad, replicas, decimales):
1086
- return actualizar_tabla_evento(df, filas, conc, unidad, replicas, decimales)
1087
-
1088
- concentracion_input.change(
1089
- fn=actualizar_tabla_wrapper,
1090
- inputs=[tabla_output, filas_slider, concentracion_input, unidad_input, replicas_slider, decimales_slider],
1091
- outputs=tabla_output
1092
- )
1093
-
1094
- unidad_input.change(
1095
- fn=actualizar_tabla_wrapper,
1096
- inputs=[tabla_output, filas_slider, concentracion_input, unidad_input, replicas_slider, decimales_slider],
1097
- outputs=tabla_output
1098
- )
1099
-
1100
- filas_slider.change(
1101
- fn=actualizar_tabla_wrapper,
1102
- inputs=[tabla_output, filas_slider, concentracion_input, unidad_input, replicas_slider, decimales_slider],
1103
- outputs=tabla_output
1104
- )
1105
-
1106
- replicas_slider.change(
1107
- fn=actualizar_tabla_wrapper,
1108
- inputs=[tabla_output, filas_slider, concentracion_input, unidad_input, replicas_slider, decimales_slider],
1109
- outputs=tabla_output
1110
- )
1111
-
1112
- decimales_slider.change(
1113
- fn=ajustar_decimales_evento,
1114
- inputs=[tabla_output, decimales_slider],
1115
- outputs=tabla_output
1116
- )
1117
-
1118
- # Evento de copiar informe utilizando JavaScript
1119
- copiar_btn.click(
1120
- None,
1121
- [],
1122
- [],
1123
- js="""
1124
- function() {
1125
- const informeElement = document.querySelector('#informe_output');
1126
- const range = document.createRange();
1127
- range.selectNode(informeElement);
1128
- window.getSelection().removeAllRanges();
1129
- window.getSelection().addRange(range);
1130
- document.execCommand('copy');
1131
- window.getSelection().removeAllRanges();
1132
- alert('Informe copiado al portapapeles');
1133
- }
1134
- """
1135
- )
1136
-
1137
- # Eventos de exportar informes
1138
- exportar_word_btn.click(
1139
- fn=exportar_word,
1140
- inputs=[tabla_output, informe_output, unidad_input, filas_seleccionadas],
1141
- outputs=exportar_word_file
1142
- )
1143
-
1144
- exportar_latex_btn.click(
1145
- fn=exportar_latex,
1146
- inputs=[tabla_output, informe_output, filas_seleccionadas],
1147
- outputs=exportar_latex_file
1148
  )
1149
 
1150
  # Inicializar la interfaz con el ejemplo base
@@ -1152,11 +502,10 @@ with gr.Blocks(theme=gr.themes.Soft()) as interfaz:
1152
  n_replicas = 1
1153
  df = generar_tabla(7, 2000000, "UFC", n_replicas)
1154
  # Valores reales de ejemplo
1155
- df[f"Concentraci贸n Real 1 (UFC)"] = [2000000, 1600000, 1200000, 800000, 400000, 200000, 100000]
1156
- # Calcular promedio y desviaci贸n est谩ndar
1157
- df = calcular_promedio_desviacion(df, n_replicas, "UFC", 3)
1158
  filas_seleccionadas_inicial = [f"Fila {i+1}" for i in df.index]
1159
- estado, fig, informe, df = actualizar_analisis(df, n_replicas, "UFC", filas_seleccionadas_inicial, 3)
 
1160
  return (
1161
  2000000,
1162
  "UFC",
@@ -1166,28 +515,14 @@ with gr.Blocks(theme=gr.themes.Soft()) as interfaz:
1166
  fig,
1167
  informe,
1168
  filas_seleccionadas_inicial,
1169
- 3 # N煤mero de decimales
 
 
1170
  )
1171
 
1172
  interfaz.load(
1173
  fn=iniciar_con_ejemplo,
1174
- outputs=[concentracion_input, unidad_input, filas_slider, tabla_output, estado_output, graficos_output, informe_output, filas_seleccionadas, decimales_slider]
1175
- )
1176
-
1177
- # Evento al presionar el bot贸n de calcular regresi贸n
1178
- calcular_regresion_btn.click(
1179
- fn=calcular_regresion_tabla_principal,
1180
- inputs=[
1181
- tabla_output, unidad_input, filas_seleccionadas_regresion,
1182
- palette_puntos_regresion, estilo_puntos_regresion,
1183
- palette_linea_ajuste_regresion, estilo_linea_ajuste_regresion,
1184
- mostrar_linea_ajuste_regresion, mostrar_puntos_regresion,
1185
- legend_location_dropdown, decimales_slider,
1186
- titulo_grafico_original, titulo_grafico_personalizado,
1187
- eje_x_original, eje_y_original,
1188
- eje_x_personalizado, eje_y_personalizado
1189
- ],
1190
- outputs=[estado_regresion_output, grafico_original_output, grafico_personalizado_output, tabla_resumen_output]
1191
  )
1192
 
1193
  # Lanzar la interfaz
 
9
  from docx.shared import Inches, Pt
10
  from docx.enum.text import WD_PARAGRAPH_ALIGNMENT
11
  import os
12
+ import re
13
 
14
  def generar_tabla(n_filas, concentracion_inicial, unidad_medida, n_replicas):
15
  valores_base = [1.000, 0.800, 0.600, 0.400, 0.200, 0.100, 0.050]
 
57
  pass
58
  return df
59
 
60
+ def generar_graficos(df_valid, unidad_medida, col_absorbancia, palette_puntos, estilo_puntos,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
  palette_linea_ajuste, estilo_linea_ajuste,
62
+ mostrar_linea_ajuste, mostrar_puntos):
 
63
  col_predicha_num = "Concentraci贸n Predicha Num茅rica"
 
 
64
 
65
  # Convertir a num茅rico
66
  df_valid[col_predicha_num] = df_valid[col_predicha_num].astype(float)
67
+ df_valid[col_absorbancia] = df_valid[col_absorbancia].astype(float)
 
68
 
69
  # Calcular regresi贸n lineal
70
+ slope, intercept, r_value, p_value, std_err = stats.linregress(df_valid[col_predicha_num], df_valid[col_absorbancia])
71
  df_valid['Ajuste Lineal'] = intercept + slope * df_valid[col_predicha_num]
72
 
73
  # Configurar estilos
74
  sns.set(style="whitegrid")
75
  plt.rcParams.update({'figure.autolayout': True})
76
 
77
+ fig, ax1 = plt.subplots(figsize=(7, 6))
78
 
79
  # Obtener colores de las paletas
80
  colors_puntos = sns.color_palette(palette_puntos, as_cmap=False)
81
  colors_linea_ajuste = sns.color_palette(palette_linea_ajuste, as_cmap=False)
 
 
82
 
83
  # Seleccionar colores
84
  color_puntos = colors_puntos[0]
85
  color_linea_ajuste = colors_linea_ajuste[0]
 
 
86
 
87
  # Gr谩fico de dispersi贸n con l铆nea de regresi贸n
88
  if mostrar_puntos:
89
+ ax1.scatter(
90
+ df_valid[col_predicha_num],
91
+ df_valid[col_absorbancia],
92
+ color=color_puntos,
93
+ s=100,
94
+ label='Datos',
95
+ marker=estilo_puntos
96
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
 
98
  # L铆nea de ajuste
99
  if mostrar_linea_ajuste:
 
106
  linestyle=estilo_linea_ajuste
107
  )
108
 
109
+ ax1.set_title('Correlaci贸n entre Concentraci贸n Predicha y Absorbancia', fontsize=14)
 
 
 
 
 
 
 
 
 
 
 
 
110
  ax1.set_xlabel('Concentraci贸n Predicha', fontsize=12)
111
+ ax1.set_ylabel('Absorbancia', fontsize=12)
112
 
113
  # A帽adir ecuaci贸n y R虏 en el gr谩fico
114
  ax1.annotate(
 
123
  # Posicionar la leyenda
124
  ax1.legend(loc='lower right', fontsize=10)
125
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
126
  plt.tight_layout()
127
  plt.savefig('grafico.png') # Guardar el gr谩fico para incluirlo en el informe
128
  return fig
 
155
 
156
  return evaluacion
157
 
158
+ def generar_informe_completo(df_valid, unidad_medida, col_absorbancia):
159
  """Generar un informe completo en formato markdown"""
160
  col_predicha_num = "Concentraci贸n Predicha Num茅rica"
 
161
 
162
  # Convertir a num茅rico
163
  df_valid[col_predicha_num] = df_valid[col_predicha_num].astype(float)
164
+ df_valid[col_absorbancia] = df_valid[col_absorbancia].astype(float)
165
 
166
  # Calcular estad铆sticas
167
+ slope, intercept, r_value, p_value, std_err = stats.linregress(df_valid[col_predicha_num], df_valid[col_absorbancia])
168
  r_squared = r_value ** 2
169
+ rmse = np.sqrt(((df_valid[col_absorbancia] - (intercept + slope * df_valid[col_predicha_num])) ** 2).mean())
170
+ cv = (df_valid[col_absorbancia].std() / df_valid[col_absorbancia].mean()) * 100 # CV de los valores
171
 
172
  # Evaluar calidad
173
  evaluacion = evaluar_calidad_calibracion(df_valid, r_squared, rmse, cv)
 
198
  """
199
  return informe, evaluacion['estado']
200
 
201
+ def actualizar_analisis(df, unidad_medida, filas_seleccionadas, decimales, absorbancia_seleccionada):
202
  if df is None or df.empty:
203
  return "Error en los datos", None, "No se pueden generar an谩lisis", df
204
 
 
208
 
209
  indices_seleccionados = [int(s.split(' ')[1]) - 1 for s in filas_seleccionadas]
210
 
211
+ # Usar la columna de absorbancia seleccionada
 
 
212
  col_predicha_num = "Concentraci贸n Predicha Num茅rica"
213
+ col_absorbancia = absorbancia_seleccionada
214
+
215
+ if col_absorbancia not in df.columns:
216
+ return "La columna seleccionada no existe en los datos", None, "Error en los datos", df
217
 
218
  # Convertir columnas a num茅rico
219
  df[col_predicha_num] = pd.to_numeric(df[col_predicha_num], errors='coerce')
220
+ df[col_absorbancia] = pd.to_numeric(df[col_absorbancia], errors='coerce')
221
 
222
+ df_valid = df.dropna(subset=[col_predicha_num, col_absorbancia])
223
 
224
+ # Resetear el 铆ndice
225
  df_valid.reset_index(drop=True, inplace=True)
226
 
227
  # Filtrar filas seg煤n las seleccionadas
228
  df_valid = df_valid.loc[indices_seleccionados]
229
 
230
  if len(df_valid) < 2:
231
+ return "Se necesitan m谩s datos", None, "Se requieren al menos dos valores para el an谩lisis", df
232
 
233
+ # Calcular regresi贸n lineal
234
+ slope, intercept, r_value, p_value, std_err = stats.linregress(df_valid[col_predicha_num], df_valid[col_absorbancia])
235
  df_valid['Ajuste Lineal'] = intercept + slope * df_valid[col_predicha_num]
236
 
237
  # Generar gr谩fico con opciones predeterminadas
238
  fig = generar_graficos(
239
+ df_valid, unidad_medida, col_absorbancia,
240
  palette_puntos='deep', estilo_puntos='o',
241
  palette_linea_ajuste='muted', estilo_linea_ajuste='-',
 
 
242
  mostrar_linea_ajuste=True,
 
243
  mostrar_puntos=True
244
  )
245
+ informe, estado = generar_informe_completo(df_valid, unidad_medida, col_absorbancia)
246
 
247
  return estado, fig, informe, df
248
 
249
+ def actualizar_graficos(df, unidad_medida,
250
  palette_puntos, estilo_puntos,
251
  palette_linea_ajuste, estilo_linea_ajuste,
252
+ mostrar_linea_ajuste, mostrar_puntos,
253
+ filas_seleccionadas, decimales, absorbancia_seleccionada):
 
 
254
  if df is None or df.empty:
255
  return None
256
 
257
  # Asegurarse de que los c谩lculos est茅n actualizados
 
 
258
  col_predicha_num = "Concentraci贸n Predicha Num茅rica"
259
+ col_absorbancia = absorbancia_seleccionada
260
+
261
+ if col_absorbancia not in df.columns:
262
+ return None
263
 
264
  # Convertir columnas a num茅rico
265
  df[col_predicha_num] = pd.to_numeric(df[col_predicha_num], errors='coerce')
266
+ df[col_absorbancia] = pd.to_numeric(df[col_absorbancia], errors='coerce')
267
 
268
+ df_valid = df.dropna(subset=[col_predicha_num, col_absorbancia])
269
 
270
  # Resetear el 铆ndice para asegurar que sea secuencial
271
  df_valid.reset_index(drop=True, inplace=True)
 
284
 
285
  # Generar gr谩fico con opciones seleccionadas
286
  fig = generar_graficos(
287
+ df_valid, unidad_medida, col_absorbancia,
288
  palette_puntos, estilo_puntos,
289
  palette_linea_ajuste, estilo_linea_ajuste,
290
+ mostrar_linea_ajuste, mostrar_puntos
 
 
291
  )
292
 
293
  return fig
294
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
295
  def cargar_excel(file):
296
  # Leer el archivo Excel
297
+ df = pd.read_excel(file.name, sheet_name=0) # Leer la primera pesta帽a
298
+
299
+ # Verificar que las columnas 'Absorbancia' existan
300
+ absorbancia_columns = [col for col in df.columns if 'Absorbancia' in col]
301
+ if not absorbancia_columns:
302
+ return "El archivo debe tener columnas 'Absorbancia'", None, None, None, None, None, None, None, None
303
+
304
+ # Obtener concentraci贸n inicial y unidad de medida desde el nombre de la primera columna
305
+ first_column_name = df.columns[0]
306
+ try:
307
+ # Extraer concentraci贸n y unidad
308
+ match = re.search(r'\(([\d\.]+)\s*(\w+)\)', first_column_name)
309
+ if match:
310
+ concentracion_inicial = float(match.group(1))
311
+ unidad_medida = match.group(2)
312
+ else:
313
+ concentracion_inicial = 0
314
+ unidad_medida = ''
315
+ except:
316
+ concentracion_inicial = 0
317
+ unidad_medida = ''
318
 
319
+ n_filas = len(df)
320
+ n_replicas = 1 # Solo se selecciona una columna de absorbancia para el an谩lisis
 
 
 
 
321
 
322
  # Generar la tabla base
323
  df_base = generar_tabla(n_filas, concentracion_inicial, unidad_medida, n_replicas)
324
 
325
+ # Copiar las columnas relevantes al DataFrame base
326
+ for col in df.columns:
327
+ if col in df_base.columns:
328
+ df_base[col] = df[col]
329
+ else:
330
+ df_base[col] = df[col]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
331
 
332
+ # Obtener las opciones de absorbancia
333
+ absorbancia_options = absorbancia_columns # Nombres tal como aparecen en el archivo Excel
 
334
 
335
+ return concentracion_inicial, unidad_medida, n_filas, n_replicas, df_base, absorbancia_options, "", None, ""
336
 
 
337
  def actualizar_opciones_filas(df):
338
  if df is None or df.empty:
339
  update = gr.update(choices=[], value=[])
 
394
  sinteticos_btn = gr.Button("馃И Generar Datos Sint茅ticos", variant="secondary")
395
  cargar_excel_btn = gr.UploadButton("馃搨 Cargar Excel", file_types=[".xlsx"], variant="secondary")
396
 
397
+ absorbancia_dropdown = gr.Dropdown(
398
+ label="Seleccione la Absorbancia para el an谩lisis",
399
+ choices=[],
400
+ value=None
401
+ )
402
+
403
  tabla_output = gr.DataFrame(
404
  wrap=True,
405
  label="Tabla de Datos",
 
411
  estado_output = gr.Textbox(label="Estado", interactive=False)
412
  graficos_output = gr.Plot(label="Gr谩ficos de An谩lisis")
413
 
 
414
  filas_seleccionadas = gr.CheckboxGroup(
415
  label="Seleccione las filas a incluir en el an谩lisis",
416
  choices=[],
417
  value=[],
418
  )
419
 
 
420
  with gr.Row():
 
421
  paletas_colores = ["deep", "muted", "pastel", "bright", "dark", "colorblind", "Set1", "Set2", "Set3"]
422
 
423
  palette_puntos_dropdown = gr.Dropdown(
 
442
  )
443
 
444
  with gr.Row():
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
445
  mostrar_linea_ajuste = gr.Checkbox(value=True, label="Mostrar L铆nea de Ajuste")
 
446
  mostrar_puntos = gr.Checkbox(value=True, label="Mostrar Puntos")
447
  graficar_btn = gr.Button("馃搳 Graficar", variant="primary")
448
 
 
455
  exportar_word_file = gr.File(label="Informe en Word")
456
  exportar_latex_file = gr.File(label="Informe en LaTeX")
457
 
 
458
  informe_output = gr.Markdown(elem_id="informe_output")
459
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
460
  # Eventos para actualizar las opciones de filas
461
  tabla_output.change(
462
  fn=actualizar_opciones_filas,
463
  inputs=[tabla_output],
464
+ outputs=[filas_seleccionadas, ]
465
  )
466
 
467
  # Evento al presionar el bot贸n Calcular
468
  calcular_btn.click(
469
  fn=actualizar_analisis,
470
+ inputs=[tabla_output, unidad_input, filas_seleccionadas, decimales_slider, absorbancia_dropdown],
471
  outputs=[estado_output, graficos_output, informe_output, tabla_output]
472
  )
473
 
 
475
  graficar_btn.click(
476
  fn=actualizar_graficos,
477
  inputs=[
478
+ tabla_output, unidad_input,
479
  palette_puntos_dropdown, estilo_puntos_dropdown,
480
  palette_linea_ajuste_dropdown, estilo_linea_ajuste_dropdown,
481
+ mostrar_linea_ajuste, mostrar_puntos,
482
+ filas_seleccionadas, decimales_slider, absorbancia_dropdown
 
 
483
  ],
484
  outputs=graficos_output
485
  )
486
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
487
  # Evento para limpiar datos
488
  limpiar_btn.click(
489
+ fn=lambda: (2000000, "UFC", 7, generar_tabla(7, 2000000, "UFC", 1), "", None, "", [], 3, [], None),
490
+ outputs=[concentracion_input, unidad_input, filas_slider, tabla_output, estado_output, graficos_output, informe_output, filas_seleccionadas, decimales_slider, absorbancia_dropdown, absorbancia_dropdown]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
491
  )
492
 
493
  # Evento para cargar archivo Excel
494
  cargar_excel_btn.upload(
495
  fn=cargar_excel,
496
  inputs=[cargar_excel_btn],
497
+ outputs=[concentracion_input, unidad_input, filas_slider, replicas_slider, tabla_output, absorbancia_dropdown, estado_output, graficos_output, informe_output]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
498
  )
499
 
500
  # Inicializar la interfaz con el ejemplo base
 
502
  n_replicas = 1
503
  df = generar_tabla(7, 2000000, "UFC", n_replicas)
504
  # Valores reales de ejemplo
505
+ df["Absorbancia 1"] = [0.8, 0.65, 0.5, 0.35, 0.2, 0.1, 0.05]
 
 
506
  filas_seleccionadas_inicial = [f"Fila {i+1}" for i in df.index]
507
+ absorbancia_options = ["Absorbancia 1"]
508
+ estado, fig, informe, df = actualizar_analisis(df, "UFC", filas_seleccionadas_inicial, 3, "Absorbancia 1")
509
  return (
510
  2000000,
511
  "UFC",
 
515
  fig,
516
  informe,
517
  filas_seleccionadas_inicial,
518
+ 3, # N煤mero de decimales
519
+ absorbancia_options,
520
+ "Absorbancia 1"
521
  )
522
 
523
  interfaz.load(
524
  fn=iniciar_con_ejemplo,
525
+ outputs=[concentracion_input, unidad_input, filas_slider, tabla_output, estado_output, graficos_output, informe_output, filas_seleccionadas, decimales_slider, absorbancia_dropdown, absorbancia_dropdown]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
526
  )
527
 
528
  # Lanzar la interfaz