C2MV commited on
Commit
1abcb17
verified
1 Parent(s): 8de20e9

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +157 -87
app.py CHANGED
@@ -10,7 +10,7 @@ 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):
14
  valores_base = [1.00, 0.80, 0.60, 0.40, 0.20, 0.10, 0.05]
15
 
16
  if n_filas <= 7:
@@ -37,7 +37,17 @@ def generar_tabla(n_filas, concentracion_inicial, unidad_medida):
37
  lambda x: concentracion_inicial / x
38
  )
39
  df[f"Concentraci贸n Predicha ({unidad_medida})"] = df["Concentraci贸n Predicha Num茅rica"].round(0).astype(str)
40
- df[f"Concentraci贸n Real ({unidad_medida})"] = None
 
 
 
 
 
 
 
 
 
 
41
 
42
  return df
43
 
@@ -55,30 +65,36 @@ def ajustar_decimales_evento(df, decimales):
55
 
56
  return df
57
 
58
- def generar_datos_sinteticos(df, desviacion_std):
59
- col_predicha_num = "Concentraci贸n Predicha Num茅rica"
60
- col_real = [col for col in df.columns if 'Real' in col][0]
 
 
 
 
61
 
62
- # Generar datos sint茅ticos
63
- valores_predichos = df[col_predicha_num].astype(float).values
64
- datos_sinteticos = valores_predichos + np.random.normal(0, desviacion_std, size=len(valores_predichos))
65
- datos_sinteticos = np.maximum(0, datos_sinteticos) # Asegurar que no haya valores negativos
66
- datos_sinteticos = np.round(datos_sinteticos, 2)
67
 
68
- df[col_real] = datos_sinteticos
 
 
 
69
 
70
  return df
71
 
72
- def generar_graficos(df_valid):
73
  col_predicha_num = "Concentraci贸n Predicha Num茅rica"
74
- col_real = [col for col in df_valid.columns if 'Real' in col][0]
 
75
 
76
  # Convertir a num茅rico
77
  df_valid[col_predicha_num] = df_valid[col_predicha_num].astype(float)
78
- df_valid[col_real] = df_valid[col_real].astype(float)
 
79
 
80
  # Calcular regresi贸n lineal
81
- slope, intercept, r_value, p_value, std_err = stats.linregress(df_valid[col_predicha_num], df_valid[col_real])
82
  df_valid['Ajuste Lineal'] = intercept + slope * df_valid[col_predicha_num]
83
 
84
  # Configurar estilos
@@ -88,16 +104,30 @@ def generar_graficos(df_valid):
88
  fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 6))
89
 
90
  # Gr谩fico de dispersi贸n con l铆nea de regresi贸n
91
- sns.scatterplot(
92
- data=df_valid,
93
- x=col_predicha_num,
94
- y=col_real,
95
- ax=ax1,
96
- color='blue',
97
- s=100,
98
- label='Datos Reales',
99
- marker='o'
100
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
101
 
102
  # L铆nea de ajuste
103
  sns.lineplot(
@@ -122,7 +152,7 @@ def generar_graficos(df_valid):
122
 
123
  ax1.set_title('Correlaci贸n entre Concentraci贸n Predicha y Real', fontsize=14)
124
  ax1.set_xlabel('Concentraci贸n Predicha', fontsize=12)
125
- ax1.set_ylabel('Concentraci贸n Real', fontsize=12)
126
 
127
  # A帽adir ecuaci贸n y R虏 en el gr谩fico
128
  ax1.annotate(
@@ -138,7 +168,7 @@ def generar_graficos(df_valid):
138
  ax1.legend(loc='lower right', fontsize=10)
139
 
140
  # Gr谩fico de residuos
141
- residuos = df_valid[col_real] - df_valid['Ajuste Lineal']
142
  sns.scatterplot(
143
  data=df_valid,
144
  x=col_predicha_num,
@@ -188,20 +218,20 @@ def evaluar_calidad_calibracion(df_valid, r_squared, rmse, cv_percent):
188
 
189
  return evaluacion
190
 
191
- def generar_informe_completo(df_valid):
192
  """Generar un informe completo en formato markdown"""
193
  col_predicha_num = "Concentraci贸n Predicha Num茅rica"
194
- col_real = [col for col in df_valid.columns if 'Real' in col][0]
195
 
196
  # Convertir a num茅rico
197
  df_valid[col_predicha_num] = df_valid[col_predicha_num].astype(float)
198
- df_valid[col_real] = df_valid[col_real].astype(float)
199
 
200
  # Calcular estad铆sticas
201
- slope, intercept, r_value, p_value, std_err = stats.linregress(df_valid[col_predicha_num], df_valid[col_real])
202
  r_squared = r_value ** 2
203
- rmse = np.sqrt(((df_valid[col_real] - (intercept + slope * df_valid[col_predicha_num])) ** 2).mean())
204
- cv = (df_valid[col_real].std() / df_valid[col_real].mean()) * 100 # CV de los valores reales
205
 
206
  # Evaluar calidad
207
  evaluacion = evaluar_calidad_calibracion(df_valid, r_squared, rmse, cv)
@@ -232,32 +262,35 @@ Fecha: {datetime.now().strftime('%d/%m/%Y %H:%M')}
232
  """
233
  return informe, evaluacion['estado']
234
 
235
- def actualizar_analisis(df):
236
  if df is None or df.empty:
237
  return "Error en los datos", None, "No se pueden generar an谩lisis"
238
 
239
  col_predicha_num = "Concentraci贸n Predicha Num茅rica"
240
- col_real = [col for col in df.columns if 'Real' in col][0]
 
 
 
241
 
242
  # Convertir columnas a num茅rico
243
  df[col_predicha_num] = pd.to_numeric(df[col_predicha_num], errors='coerce')
244
- df[col_real] = pd.to_numeric(df[col_real], errors='coerce')
245
 
246
- df_valid = df.dropna(subset=[col_predicha_num, col_real])
247
 
248
  if len(df_valid) < 2:
249
  return "Se necesitan m谩s datos", None, "Se requieren al menos dos valores reales para el an谩lisis"
250
 
251
  # Calcular la regresi贸n y agregar 'Ajuste Lineal'
252
- slope, intercept, r_value, p_value, std_err = stats.linregress(df_valid[col_predicha_num], df_valid[col_real])
253
  df_valid['Ajuste Lineal'] = intercept + slope * df_valid[col_predicha_num]
254
 
255
- fig = generar_graficos(df_valid)
256
- informe, estado = generar_informe_completo(df_valid)
257
 
258
  return estado, fig, informe
259
 
260
- def exportar_informe_word(df_valid, informe_md):
261
  # Crear documento Word
262
  doc = docx.Document()
263
 
@@ -348,34 +381,34 @@ def exportar_informe_latex(df_valid, informe_md):
348
  f.write(informe_tex)
349
  return filename
350
 
351
- def exportar_word(df, informe_md):
352
  df_valid = df.copy()
353
  col_predicha_num = "Concentraci贸n Predicha Num茅rica"
354
- col_real = [col for col in df_valid.columns if 'Real' in col][0]
355
 
356
  # Convertir columnas a num茅rico
357
  df_valid[col_predicha_num] = pd.to_numeric(df_valid[col_predicha_num], errors='coerce')
358
- df_valid[col_real] = pd.to_numeric(df_valid[col_real], errors='coerce')
359
 
360
- df_valid = df_valid.dropna(subset=[col_predicha_num, col_real])
361
 
362
  if df_valid.empty:
363
  return None
364
 
365
- filename = exportar_informe_word(df_valid, informe_md)
366
 
367
  return filename # Retornamos el nombre del archivo
368
 
369
  def exportar_latex(df, informe_md):
370
  df_valid = df.copy()
371
  col_predicha_num = "Concentraci贸n Predicha Num茅rica"
372
- col_real = [col for col in df_valid.columns if 'Real' in col][0]
373
 
374
  # Convertir columnas a num茅rico
375
  df_valid[col_predicha_num] = pd.to_numeric(df_valid[col_predicha_num], errors='coerce')
376
- df_valid[col_real] = pd.to_numeric(df_valid[col_real], errors='coerce')
377
 
378
- df_valid = df_valid.dropna(subset=[col_predicha_num, col_real])
379
 
380
  if df_valid.empty:
381
  return None
@@ -385,20 +418,28 @@ def exportar_latex(df, informe_md):
385
  return filename # Retornamos el nombre del archivo
386
 
387
  # Funciones de ejemplo
388
- def cargar_ejemplo_ufc():
389
- df = generar_tabla(7, 2000000, "UFC")
390
- valores_reales = [2000000, 1600000, 1200000, 800000, 400000, 200000, 100000]
391
- df[f"Concentraci贸n Real (UFC)"] = valores_reales
 
 
 
 
392
  return 2000000, "UFC", 7, df
393
 
394
- def cargar_ejemplo_od():
395
- df = generar_tabla(7, 1.0, "OD")
396
- valores_reales = [1.000, 0.800, 0.600, 0.400, 0.200, 0.100, 0.050]
397
- df[f"Concentraci贸n Real (OD)"] = valores_reales
 
 
 
 
398
  return 1.0, "OD", 7, df
399
 
400
- def limpiar_datos():
401
- df = generar_tabla(7, 2000000, "UFC")
402
  return (
403
  2000000, # Concentraci贸n Inicial
404
  "UFC", # Unidad de Medida
@@ -409,27 +450,37 @@ def limpiar_datos():
409
  "" # Informe Output
410
  )
411
 
412
- def generar_datos_sinteticos_evento(df):
413
  df = df.copy()
414
  col_predicha_num = "Concentraci贸n Predicha Num茅rica"
415
- df[col_predicha_num] = pd.to_numeric(df[col_predicha_num], errors='coerce')
416
- desviacion_std = 0.05 * df[col_predicha_num].mean() # 5% de la media como desviaci贸n est谩ndar
417
- df = generar_datos_sinteticos(df, desviacion_std)
 
 
 
 
 
 
 
 
 
418
  return df
419
 
420
- def actualizar_tabla_evento(df, n_filas, concentracion, unidad):
421
  # Actualizar tabla sin borrar "Concentraci贸n Real"
422
- df_new = generar_tabla(n_filas, concentracion, unidad)
423
 
424
  # Mapear columnas
425
- col_real_new = [col for col in df_new.columns if 'Concentraci贸n Real' in col][0]
426
- col_real_old = [col for col in df.columns if 'Concentraci贸n Real' in col][0]
427
 
428
  # Reemplazar valores existentes en "Concentraci贸n Real"
429
- df_new[col_real_new] = None
430
- for idx in df_new.index:
431
- if idx in df.index:
432
- df_new.at[idx, col_real_new] = df.at[idx, col_real_old]
 
433
 
434
  return df_new
435
 
@@ -466,6 +517,13 @@ with gr.Blocks(theme=gr.themes.Soft()) as interfaz:
466
  step=1,
467
  label="N煤mero de Decimales"
468
  )
 
 
 
 
 
 
 
469
 
470
  with gr.Row():
471
  calcular_btn = gr.Button("馃攧 Calcular", variant="primary")
@@ -479,11 +537,11 @@ with gr.Blocks(theme=gr.themes.Soft()) as interfaz:
479
 
480
  tabla_output = gr.DataFrame(
481
  row_count=(1, "dynamic"),
482
- col_count=(6, "fixed"),
483
  wrap=True,
484
  label="Tabla de Datos",
485
  interactive=True,
486
- datatype=["number", "number", "number", "number", "str", "number"],
487
  type="pandas",
488
  )
489
 
@@ -507,32 +565,34 @@ with gr.Blocks(theme=gr.themes.Soft()) as interfaz:
507
  # Evento al presionar el bot贸n Calcular
508
  calcular_btn.click(
509
  fn=actualizar_analisis,
510
- inputs=tabla_output,
511
  outputs=output_components
512
  )
513
 
514
  # Evento para limpiar datos
515
  limpiar_btn.click(
516
  fn=limpiar_datos,
517
- inputs=[],
518
  outputs=[concentracion_input, unidad_input, filas_slider, tabla_output, estado_output, graficos_output, informe_output]
519
  )
520
 
521
  # Eventos de los botones de ejemplo
522
  ejemplo_ufc_btn.click(
523
  fn=cargar_ejemplo_ufc,
 
524
  outputs=[concentracion_input, unidad_input, filas_slider, tabla_output]
525
  )
526
 
527
  ejemplo_od_btn.click(
528
  fn=cargar_ejemplo_od,
 
529
  outputs=[concentracion_input, unidad_input, filas_slider, tabla_output]
530
  )
531
 
532
  # Evento para generar datos sint茅ticos
533
  sinteticos_btn.click(
534
  fn=generar_datos_sinteticos_evento,
535
- inputs=tabla_output,
536
  outputs=tabla_output
537
  )
538
 
@@ -544,21 +604,30 @@ with gr.Blocks(theme=gr.themes.Soft()) as interfaz:
544
  )
545
 
546
  # Actualizar tabla al cambiar los par谩metros (sin borrar "Concentraci贸n Real")
 
 
 
547
  concentracion_input.change(
548
- fn=actualizar_tabla_evento,
549
- inputs=[tabla_output, filas_slider, concentracion_input, unidad_input],
550
  outputs=tabla_output
551
  )
552
 
553
  unidad_input.change(
554
- fn=actualizar_tabla_evento,
555
- inputs=[tabla_output, filas_slider, concentracion_input, unidad_input],
556
  outputs=tabla_output
557
  )
558
 
559
  filas_slider.change(
560
- fn=actualizar_tabla_evento,
561
- inputs=[tabla_output, filas_slider, concentracion_input, unidad_input],
 
 
 
 
 
 
562
  outputs=tabla_output
563
  )
564
 
@@ -586,7 +655,7 @@ with gr.Blocks(theme=gr.themes.Soft()) as interfaz:
586
  # Eventos de exportar informes
587
  exportar_word_btn.click(
588
  fn=exportar_word,
589
- inputs=[tabla_output, informe_output],
590
  outputs=exportar_word_file
591
  )
592
 
@@ -598,10 +667,11 @@ with gr.Blocks(theme=gr.themes.Soft()) as interfaz:
598
 
599
  # Inicializar la interfaz con el ejemplo base
600
  def iniciar_con_ejemplo():
601
- df = generar_tabla(7, 2000000, "UFC")
602
- valores_reales = [2000000, 1600000, 1200000, 800000, 400000, 200000, 100000]
603
- df[f"Concentraci贸n Real (UFC)"] = valores_reales
604
- estado, fig, informe = actualizar_analisis(df)
 
605
  return (
606
  2000000,
607
  "UFC",
 
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.00, 0.80, 0.60, 0.40, 0.20, 0.10, 0.05]
15
 
16
  if n_filas <= 7:
 
37
  lambda x: concentracion_inicial / x
38
  )
39
  df[f"Concentraci贸n Predicha ({unidad_medida})"] = df["Concentraci贸n Predicha Num茅rica"].round(0).astype(str)
40
+
41
+ # A帽adir columnas para las r茅plicas de "Concentraci贸n Real"
42
+ for i in range(1, n_replicas + 1):
43
+ df[f"Concentraci贸n Real {i} ({unidad_medida})"] = None
44
+
45
+ # A帽adir columnas para promedio y desviaci贸n est谩ndar si hay m谩s de una r茅plica
46
+ if n_replicas > 1:
47
+ df[f"Concentraci贸n Real Promedio ({unidad_medida})"] = None
48
+ df[f"Desviaci贸n Est谩ndar ({unidad_medida})"] = None
49
+ else:
50
+ df[f"Concentraci贸n Real Promedio ({unidad_medida})"] = None
51
 
52
  return df
53
 
 
65
 
66
  return df
67
 
68
+ def calcular_promedio_desviacion(df, n_replicas, unidad_medida):
69
+ df = df.copy()
70
+ # Obtener las columnas de r茅plicas
71
+ col_replicas = [f"Concentraci贸n Real {i} ({unidad_medida})" for i in range(1, n_replicas + 1)]
72
+ # Convertir a num茅rico
73
+ for col in col_replicas:
74
+ df[col] = pd.to_numeric(df[col], errors='coerce')
75
 
76
+ # Calcular el promedio y la desviaci贸n est谩ndar
77
+ df[f"Concentraci贸n Real Promedio ({unidad_medida})"] = df[col_replicas].mean(axis=1)
 
 
 
78
 
79
+ if n_replicas > 1:
80
+ df[f"Desviaci贸n Est谩ndar ({unidad_medida})"] = df[col_replicas].std(ddof=1, axis=1)
81
+ else:
82
+ df[f"Desviaci贸n Est谩ndar ({unidad_medida})"] = 0.0
83
 
84
  return df
85
 
86
+ def generar_graficos(df_valid, n_replicas, unidad_medida):
87
  col_predicha_num = "Concentraci贸n Predicha Num茅rica"
88
+ col_real_promedio = f"Concentraci贸n Real Promedio ({unidad_medida})"
89
+ col_desviacion = f"Desviaci贸n Est谩ndar ({unidad_medida})"
90
 
91
  # Convertir a num茅rico
92
  df_valid[col_predicha_num] = df_valid[col_predicha_num].astype(float)
93
+ df_valid[col_real_promedio] = df_valid[col_real_promedio].astype(float)
94
+ df_valid[col_desviacion] = df_valid[col_desviacion].astype(float)
95
 
96
  # Calcular regresi贸n lineal
97
+ slope, intercept, r_value, p_value, std_err = stats.linregress(df_valid[col_predicha_num], df_valid[col_real_promedio])
98
  df_valid['Ajuste Lineal'] = intercept + slope * df_valid[col_predicha_num]
99
 
100
  # Configurar estilos
 
104
  fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 6))
105
 
106
  # Gr谩fico de dispersi贸n con l铆nea de regresi贸n
107
+ if n_replicas > 1:
108
+ # Incluir barras de error
109
+ ax1.errorbar(
110
+ df_valid[col_predicha_num],
111
+ df_valid[col_real_promedio],
112
+ yerr=df_valid[col_desviacion],
113
+ fmt='o',
114
+ color='blue',
115
+ ecolor='lightgray',
116
+ elinewidth=3,
117
+ capsize=0,
118
+ label='Datos Reales'
119
+ )
120
+ else:
121
+ sns.scatterplot(
122
+ data=df_valid,
123
+ x=col_predicha_num,
124
+ y=col_real_promedio,
125
+ ax=ax1,
126
+ color='blue',
127
+ s=100,
128
+ label='Datos Reales',
129
+ marker='o'
130
+ )
131
 
132
  # L铆nea de ajuste
133
  sns.lineplot(
 
152
 
153
  ax1.set_title('Correlaci贸n entre Concentraci贸n Predicha y Real', fontsize=14)
154
  ax1.set_xlabel('Concentraci贸n Predicha', fontsize=12)
155
+ ax1.set_ylabel('Concentraci贸n Real Promedio', fontsize=12)
156
 
157
  # A帽adir ecuaci贸n y R虏 en el gr谩fico
158
  ax1.annotate(
 
168
  ax1.legend(loc='lower right', fontsize=10)
169
 
170
  # Gr谩fico de residuos
171
+ residuos = df_valid[col_real_promedio] - df_valid['Ajuste Lineal']
172
  sns.scatterplot(
173
  data=df_valid,
174
  x=col_predicha_num,
 
218
 
219
  return evaluacion
220
 
221
+ def generar_informe_completo(df_valid, n_replicas, unidad_medida):
222
  """Generar un informe completo en formato markdown"""
223
  col_predicha_num = "Concentraci贸n Predicha Num茅rica"
224
+ col_real_promedio = f"Concentraci贸n Real Promedio ({unidad_medida})"
225
 
226
  # Convertir a num茅rico
227
  df_valid[col_predicha_num] = df_valid[col_predicha_num].astype(float)
228
+ df_valid[col_real_promedio] = df_valid[col_real_promedio].astype(float)
229
 
230
  # Calcular estad铆sticas
231
+ slope, intercept, r_value, p_value, std_err = stats.linregress(df_valid[col_predicha_num], df_valid[col_real_promedio])
232
  r_squared = r_value ** 2
233
+ rmse = np.sqrt(((df_valid[col_real_promedio] - (intercept + slope * df_valid[col_predicha_num])) ** 2).mean())
234
+ cv = (df_valid[col_real_promedio].std() / df_valid[col_real_promedio].mean()) * 100 # CV de los valores reales
235
 
236
  # Evaluar calidad
237
  evaluacion = evaluar_calidad_calibracion(df_valid, r_squared, rmse, cv)
 
262
  """
263
  return informe, evaluacion['estado']
264
 
265
+ def actualizar_analisis(df, n_replicas, unidad_medida):
266
  if df is None or df.empty:
267
  return "Error en los datos", None, "No se pueden generar an谩lisis"
268
 
269
  col_predicha_num = "Concentraci贸n Predicha Num茅rica"
270
+ col_real_promedio = f"Concentraci贸n Real Promedio ({unidad_medida})"
271
+
272
+ # Calcular promedio y desviaci贸n est谩ndar
273
+ df = calcular_promedio_desviacion(df, n_replicas, unidad_medida)
274
 
275
  # Convertir columnas a num茅rico
276
  df[col_predicha_num] = pd.to_numeric(df[col_predicha_num], errors='coerce')
277
+ df[col_real_promedio] = pd.to_numeric(df[col_real_promedio], errors='coerce')
278
 
279
+ df_valid = df.dropna(subset=[col_predicha_num, col_real_promedio])
280
 
281
  if len(df_valid) < 2:
282
  return "Se necesitan m谩s datos", None, "Se requieren al menos dos valores reales para el an谩lisis"
283
 
284
  # Calcular la regresi贸n y agregar 'Ajuste Lineal'
285
+ slope, intercept, r_value, p_value, std_err = stats.linregress(df_valid[col_predicha_num], df_valid[col_real_promedio])
286
  df_valid['Ajuste Lineal'] = intercept + slope * df_valid[col_predicha_num]
287
 
288
+ fig = generar_graficos(df_valid, n_replicas, unidad_medida)
289
+ informe, estado = generar_informe_completo(df_valid, n_replicas, unidad_medida)
290
 
291
  return estado, fig, informe
292
 
293
+ def exportar_informe_word(df_valid, informe_md, unidad_medida):
294
  # Crear documento Word
295
  doc = docx.Document()
296
 
 
381
  f.write(informe_tex)
382
  return filename
383
 
384
+ def exportar_word(df, informe_md, unidad_medida):
385
  df_valid = df.copy()
386
  col_predicha_num = "Concentraci贸n Predicha Num茅rica"
387
+ col_real_promedio = f"Concentraci贸n Real Promedio ({unidad_medida})"
388
 
389
  # Convertir columnas a num茅rico
390
  df_valid[col_predicha_num] = pd.to_numeric(df_valid[col_predicha_num], errors='coerce')
391
+ df_valid[col_real_promedio] = pd.to_numeric(df_valid[col_real_promedio], errors='coerce')
392
 
393
+ df_valid = df_valid.dropna(subset=[col_predicha_num, col_real_promedio])
394
 
395
  if df_valid.empty:
396
  return None
397
 
398
+ filename = exportar_informe_word(df_valid, informe_md, unidad_medida)
399
 
400
  return filename # Retornamos el nombre del archivo
401
 
402
  def exportar_latex(df, informe_md):
403
  df_valid = df.copy()
404
  col_predicha_num = "Concentraci贸n Predicha Num茅rica"
405
+ col_real_promedio = [col for col in df_valid.columns if 'Real Promedio' in col][0]
406
 
407
  # Convertir columnas a num茅rico
408
  df_valid[col_predicha_num] = pd.to_numeric(df_valid[col_predicha_num], errors='coerce')
409
+ df_valid[col_real_promedio] = pd.to_numeric(df_valid[col_real_promedio], errors='coerce')
410
 
411
+ df_valid = df_valid.dropna(subset=[col_predicha_num, col_real_promedio])
412
 
413
  if df_valid.empty:
414
  return None
 
418
  return filename # Retornamos el nombre del archivo
419
 
420
  # Funciones de ejemplo
421
+ def cargar_ejemplo_ufc(n_replicas):
422
+ df = generar_tabla(7, 2000000, "UFC", n_replicas)
423
+ # Valores reales de ejemplo
424
+ for i in range(1, n_replicas + 1):
425
+ valores_reales = [2000000 - (i - 1) * 10000, 1600000 - (i - 1) * 8000, 1200000 - (i - 1) * 6000,
426
+ 800000 - (i - 1) * 4000, 400000 - (i - 1) * 2000, 200000 - (i - 1) * 1000,
427
+ 100000 - (i - 1) * 500]
428
+ df[f"Concentraci贸n Real {i} (UFC)"] = valores_reales
429
  return 2000000, "UFC", 7, df
430
 
431
+ def cargar_ejemplo_od(n_replicas):
432
+ df = generar_tabla(7, 1.0, "OD", n_replicas)
433
+ # Valores reales de ejemplo
434
+ for i in range(1, n_replicas + 1):
435
+ valores_reales = [1.00 - (i - 1) * 0.05, 0.80 - (i - 1) * 0.04, 0.60 - (i - 1) * 0.03,
436
+ 0.40 - (i - 1) * 0.02, 0.20 - (i - 1) * 0.01, 0.10 - (i - 1) * 0.005,
437
+ 0.05 - (i - 1) * 0.002]
438
+ df[f"Concentraci贸n Real {i} (OD)"] = valores_reales
439
  return 1.0, "OD", 7, df
440
 
441
+ def limpiar_datos(n_replicas):
442
+ df = generar_tabla(7, 2000000, "UFC", n_replicas)
443
  return (
444
  2000000, # Concentraci贸n Inicial
445
  "UFC", # Unidad de Medida
 
450
  "" # Informe Output
451
  )
452
 
453
+ def generar_datos_sinteticos_evento(df, n_replicas, unidad_medida):
454
  df = df.copy()
455
  col_predicha_num = "Concentraci贸n Predicha Num茅rica"
456
+
457
+ # Generar datos sint茅ticos para cada r茅plica
458
+ for i in range(1, n_replicas + 1):
459
+ col_real = f"Concentraci贸n Real {i} ({unidad_medida})"
460
+ df[col_predicha_num] = pd.to_numeric(df[col_predicha_num], errors='coerce')
461
+ desviacion_std = 0.05 * df[col_predicha_num].mean() # 5% de la media como desviaci贸n est谩ndar
462
+ valores_predichos = df[col_predicha_num].astype(float).values
463
+ datos_sinteticos = valores_predichos + np.random.normal(0, desviacion_std, size=len(valores_predichos))
464
+ datos_sinteticos = np.maximum(0, datos_sinteticos) # Asegurar que no haya valores negativos
465
+ datos_sinteticos = np.round(datos_sinteticos, 2)
466
+ df[col_real] = datos_sinteticos
467
+
468
  return df
469
 
470
+ def actualizar_tabla_evento(df, n_filas, concentracion, unidad, n_replicas):
471
  # Actualizar tabla sin borrar "Concentraci贸n Real"
472
+ df_new = generar_tabla(n_filas, concentracion, unidad, n_replicas)
473
 
474
  # Mapear columnas
475
+ col_real_new = [col for col in df_new.columns if 'Concentraci贸n Real' in col]
476
+ col_real_old = [col for col in df.columns if 'Concentraci贸n Real' in col]
477
 
478
  # Reemplazar valores existentes en "Concentraci贸n Real"
479
+ for col_new, col_old in zip(col_real_new, col_real_old):
480
+ df_new[col_new] = None
481
+ for idx in df_new.index:
482
+ if idx in df.index:
483
+ df_new.at[idx, col_new] = df.at[idx, col_old]
484
 
485
  return df_new
486
 
 
517
  step=1,
518
  label="N煤mero de Decimales"
519
  )
520
+ replicas_slider = gr.Slider(
521
+ minimum=1,
522
+ maximum=5,
523
+ value=1,
524
+ step=1,
525
+ label="N煤mero de R茅plicas"
526
+ )
527
 
528
  with gr.Row():
529
  calcular_btn = gr.Button("馃攧 Calcular", variant="primary")
 
537
 
538
  tabla_output = gr.DataFrame(
539
  row_count=(1, "dynamic"),
540
+ col_count=(6, "dynamic"),
541
  wrap=True,
542
  label="Tabla de Datos",
543
  interactive=True,
544
+ datatype="auto",
545
  type="pandas",
546
  )
547
 
 
565
  # Evento al presionar el bot贸n Calcular
566
  calcular_btn.click(
567
  fn=actualizar_analisis,
568
+ inputs=[tabla_output, replicas_slider, unidad_input],
569
  outputs=output_components
570
  )
571
 
572
  # Evento para limpiar datos
573
  limpiar_btn.click(
574
  fn=limpiar_datos,
575
+ inputs=[replicas_slider],
576
  outputs=[concentracion_input, unidad_input, filas_slider, tabla_output, estado_output, graficos_output, informe_output]
577
  )
578
 
579
  # Eventos de los botones de ejemplo
580
  ejemplo_ufc_btn.click(
581
  fn=cargar_ejemplo_ufc,
582
+ inputs=[replicas_slider],
583
  outputs=[concentracion_input, unidad_input, filas_slider, tabla_output]
584
  )
585
 
586
  ejemplo_od_btn.click(
587
  fn=cargar_ejemplo_od,
588
+ inputs=[replicas_slider],
589
  outputs=[concentracion_input, unidad_input, filas_slider, tabla_output]
590
  )
591
 
592
  # Evento para generar datos sint茅ticos
593
  sinteticos_btn.click(
594
  fn=generar_datos_sinteticos_evento,
595
+ inputs=[tabla_output, replicas_slider, unidad_input],
596
  outputs=tabla_output
597
  )
598
 
 
604
  )
605
 
606
  # Actualizar tabla al cambiar los par谩metros (sin borrar "Concentraci贸n Real")
607
+ def actualizar_tabla_wrapper(df, filas, conc, unidad, replicas):
608
+ return actualizar_tabla_evento(df, filas, conc, unidad, replicas)
609
+
610
  concentracion_input.change(
611
+ fn=actualizar_tabla_wrapper,
612
+ inputs=[tabla_output, filas_slider, concentracion_input, unidad_input, replicas_slider],
613
  outputs=tabla_output
614
  )
615
 
616
  unidad_input.change(
617
+ fn=actualizar_tabla_wrapper,
618
+ inputs=[tabla_output, filas_slider, concentracion_input, unidad_input, replicas_slider],
619
  outputs=tabla_output
620
  )
621
 
622
  filas_slider.change(
623
+ fn=actualizar_tabla_wrapper,
624
+ inputs=[tabla_output, filas_slider, concentracion_input, unidad_input, replicas_slider],
625
+ outputs=tabla_output
626
+ )
627
+
628
+ replicas_slider.change(
629
+ fn=actualizar_tabla_wrapper,
630
+ inputs=[tabla_output, filas_slider, concentracion_input, unidad_input, replicas_slider],
631
  outputs=tabla_output
632
  )
633
 
 
655
  # Eventos de exportar informes
656
  exportar_word_btn.click(
657
  fn=exportar_word,
658
+ inputs=[tabla_output, informe_output, unidad_input],
659
  outputs=exportar_word_file
660
  )
661
 
 
667
 
668
  # Inicializar la interfaz con el ejemplo base
669
  def iniciar_con_ejemplo():
670
+ n_replicas = 1
671
+ df = generar_tabla(7, 2000000, "UFC", n_replicas)
672
+ # Valores reales de ejemplo
673
+ df[f"Concentraci贸n Real 1 (UFC)"] = [2000000, 1600000, 1200000, 800000, 400000, 200000, 100000]
674
+ estado, fig, informe = actualizar_analisis(df, n_replicas, "UFC")
675
  return (
676
  2000000,
677
  "UFC",