C2MV commited on
Commit
44774fb
verified
1 Parent(s): 2d3a70b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +284 -443
app.py CHANGED
@@ -266,6 +266,7 @@ def actualizar_analisis(df, n_replicas, unidad_predicha, unidad_replicas, filas_
266
  return estado, fig, informe, df
267
 
268
  def exportar_informe_word(df_valid, informe_md, unidad_predicha, unidad_replicas):
 
269
  doc = docx.Document()
270
 
271
  style = doc.styles['Normal']
@@ -397,25 +398,25 @@ def exportar_latex(df, informe_md, filas_seleccionadas):
397
 
398
  def limpiar_datos(n_replicas):
399
  unidad_predicha = "mg/L"
 
400
  unidad_replicas = "Abs"
401
-
402
  df = pd.DataFrame({
403
  "Soluci贸n": [1/(2**i) for i in range(7)],
404
  "H2O": [1-(1/(2**i)) for i in range(7)],
405
- "Factor de Diluci贸n": [(1/(1/(2**i))) for i in range(7)],
406
- f"Concentraci贸n Predicha ({unidad_predicha})": [2000000/(1/(1/(2**i))) for i in range(7)]
407
  })
408
  for i in range(1, n_replicas+1):
409
  df[f"Absorbancia Real {i} ({unidad_replicas})"] = np.nan
410
 
411
  return (
412
- 2000000,
413
  unidad_predicha,
414
  7,
415
  df,
416
- "",
417
- None,
418
- ""
419
  )
420
 
421
  def generar_datos_sinteticos_evento(df, n_replicas, unidad_predicha, unidad_replicas):
@@ -423,16 +424,17 @@ def generar_datos_sinteticos_evento(df, n_replicas, unidad_predicha, unidad_repl
423
  col_predicha = f"Concentraci贸n Predicha ({unidad_predicha})"
424
  if col_predicha in df.columns:
425
  df[col_predicha] = pd.to_numeric(df[col_predicha], errors='coerce')
426
- for i in range(1, n_replicas + 1):
427
- col_real = f"Absorbancia Real {i} ({unidad_replicas})"
428
- desviacion_std = 0.05 * df[col_predicha].mean()
429
- valores_predichos = df[col_predicha].dropna().values
430
- if len(valores_predichos) == 0:
431
- continue
432
- datos_sinteticos = valores_predichos + np.random.normal(0, desviacion_std, size=len(valores_predichos))
433
- datos_sinteticos = np.maximum(0, datos_sinteticos)
434
- datos_sinteticos = np.round(datos_sinteticos, 3)
435
- df.loc[df[col_predicha].notna(), col_real] = datos_sinteticos
 
436
  return df
437
 
438
  def actualizar_tabla_evento(df, n_filas, conc, unidad_predicha, unidad_replicas, n_replicas, decimales):
@@ -449,7 +451,7 @@ def cargar_excel(file):
449
  all_sheets = pd.read_excel(file.name, sheet_name=None)
450
 
451
  if len(all_sheets) < 3:
452
- return "El archivo debe tener al menos tres pesta帽as (Hoja1, Hoja2, Hoja3).", None, None, None, None, None, None, ""
453
 
454
  sheet_names = list(all_sheets.keys())
455
  sheet1_name = sheet_names[0]
@@ -470,10 +472,9 @@ def cargar_excel(file):
470
  except:
471
  unidad_predicha = "mg/L"
472
 
 
473
  unidad_replicas = "Abs"
474
-
475
- concentracion_inicial = 2000000.0
476
-
477
  n_filas = len(df_base)
478
  n_replicas = 2
479
 
@@ -485,160 +486,34 @@ def cargar_excel(file):
485
  df_sistema[f"Absorbancia Real 1 ({unidad_replicas})"] = col_replica_1
486
  df_sistema[f"Absorbancia Real 2 ({unidad_replicas})"] = col_replica_2
487
 
488
- return concentracion_inicial, unidad_predicha, n_filas, n_replicas, df_sistema, "", None, ""
489
 
490
  def actualizar_opciones_filas(df):
491
  if df is None or df.empty:
492
  update = gr.update(choices=[], value=[])
 
493
  else:
494
  opciones = [f"Fila {i+1}" for i in df.index]
 
495
  update = gr.update(choices=opciones, value=opciones)
496
- return update, update
497
-
498
- def calcular_regresion_tabla_principal(df, unidad_predicha, unidad_replicas, filas_seleccionadas_regresion,
499
- color_puntos, estilo_puntos,
500
- color_linea_ajuste, estilo_linea_ajuste,
501
- mostrar_linea_ajuste, mostrar_puntos,
502
- legend_location, decimales,
503
- titulo_grafico_original, titulo_grafico_personalizado,
504
- eje_x_original, eje_y_original,
505
- eje_x_personalizado, eje_y_personalizado):
506
-
507
- if df is None or df.empty:
508
- return "Datos insuficientes", None, None, None
509
-
510
- color_puntos = safe_color(color_puntos)
511
- color_linea_ajuste = safe_color(color_linea_ajuste)
512
-
513
- col_predicha = f"Concentraci贸n Predicha ({unidad_predicha})"
514
- col_real_promedio = f"Absorbancia Real Promedio ({unidad_replicas})"
515
- col_desviacion = f"Desviaci贸n Est谩ndar ({unidad_replicas})"
516
-
517
- n_replicas = len([c for c in df.columns if 'Absorbancia Real ' in c and 'Promedio' not in c and 'Desviaci贸n' not in c])
518
- df = calcular_promedio_desviacion(df, n_replicas, unidad_predicha, unidad_replicas, decimales)
519
-
520
- if col_predicha not in df.columns or col_real_promedio not in df.columns:
521
- return "Faltan columnas necesarias", None, None, None
522
-
523
- df[col_predicha] = pd.to_numeric(df[col_predicha], errors='coerce')
524
- df[col_real_promedio] = pd.to_numeric(df[col_real_promedio], errors='coerce')
525
- df[col_desviacion] = pd.to_numeric(df[col_desviacion], errors='coerce').fillna(0)
526
-
527
- df_valid = df.dropna(subset=[col_predicha, col_real_promedio])
528
- df_valid.reset_index(drop=True, inplace=True)
529
-
530
- df_original = df_valid.copy()
531
-
532
- if not filas_seleccionadas_regresion:
533
- return "Se necesitan m谩s datos", None, None, None
534
-
535
- indices_seleccionados = [int(s.split(' ')[1]) - 1 for s in filas_seleccionadas_regresion]
536
- if len(indices_seleccionados) < 2:
537
- return "Se requieren al menos dos puntos para calcular la regresi贸n", None, None, None
538
-
539
- df_valid = df_valid.loc[indices_seleccionados]
540
-
541
- slope, intercept, r_value, p_value, std_err = stats.linregress(df_valid[col_predicha], df_valid[col_real_promedio])
542
-
543
- sns.set(style="whitegrid")
544
- fig_original, ax_original = plt.subplots(figsize=(8, 6))
545
-
546
- # Gr谩fico Original
547
- ax_original.errorbar(
548
- df_original[col_predicha],
549
- df_original[col_real_promedio],
550
- yerr=df_original[col_desviacion],
551
- fmt=estilo_puntos,
552
- color=color_puntos,
553
- ecolor='gray',
554
- elinewidth=1,
555
- capsize=3,
556
- label='Datos'
557
- )
558
-
559
- slope_all, intercept_all, r_value_all, p_value_all, std_err_all = stats.linregress(df_original[col_predicha], df_original[col_real_promedio])
560
-
561
- ax_original.plot(
562
- df_original[col_predicha],
563
- intercept_all + slope_all * df_original[col_predicha],
564
- color=color_linea_ajuste,
565
- linestyle='-',
566
- label='Ajuste Lineal'
567
- )
568
-
569
- ax_original.set_xlabel(eje_x_original if eje_x_original else f'Concentraci贸n Predicha ({unidad_predicha})')
570
- ax_original.set_ylabel(eje_y_original if eje_y_original else f'Absorbancia Real Promedio ({unidad_replicas})')
571
- ax_original.set_title(titulo_grafico_original if titulo_grafico_original else 'Regresi贸n Lineal: Absorbancia Real vs Concentraci贸n Predicha (Original)')
572
- ax_original.legend(loc=legend_location)
573
-
574
- ax_original.annotate(
575
- f'y = {intercept_all:.4f} + {slope_all:.4f}x\n$R^2$ = {r_value_all**2:.4f}',
576
- xy=(0.05, 0.95),
577
- xycoords='axes fraction',
578
- fontsize=12,
579
- backgroundcolor='white',
580
- verticalalignment='top'
581
- )
582
-
583
- # Gr谩fico Personalizado
584
- sns.set(style="whitegrid")
585
- fig_personalizado, ax_personalizado = plt.subplots(figsize=(8, 6))
586
-
587
- if mostrar_puntos:
588
- ax_personalizado.errorbar(
589
- df_valid[col_predicha],
590
- df_valid[col_real_promedio],
591
- yerr=df_valid[col_desviacion],
592
- fmt=estilo_puntos,
593
- color=color_puntos,
594
- ecolor='gray',
595
- elinewidth=1,
596
- capsize=3,
597
- label='Datos'
598
- )
599
-
600
- if mostrar_linea_ajuste:
601
- ax_personalizado.plot(
602
- df_valid[col_predicha],
603
- intercept + slope * df_valid[col_predicha],
604
- color=color_linea_ajuste,
605
- linestyle=estilo_linea_ajuste,
606
- label='Ajuste Lineal'
607
- )
608
-
609
- ax_personalizado.set_xlabel(eje_x_personalizado if eje_x_personalizado else f'Concentraci贸n Predicha ({unidad_predicha})')
610
- ax_personalizado.set_ylabel(eje_y_personalizado if eje_y_personalizado else f'Absorbancia Real Promedio ({unidad_replicas})')
611
- ax_personalizado.set_title(titulo_grafico_personalizado if titulo_grafico_personalizado else 'Regresi贸n Lineal Personalizada')
612
- ax_personalizado.legend(loc=legend_location)
613
-
614
- ax_personalizado.annotate(
615
- f'y = {intercept:.4f} + {slope:.4f}x\n$R^2$ = {r_value**2:.4f}',
616
- xy=(0.05, 0.95),
617
- xycoords='axes fraction',
618
- fontsize=12,
619
- backgroundcolor='white',
620
- verticalalignment='top'
621
- )
622
-
623
- df_resumen = df_valid[[col_predicha, col_real_promedio, col_desviacion]].copy()
624
- df_resumen.columns = [f'Concentraci贸n Predicha ({unidad_predicha})', 'Absorbancia Promedio', 'Desviaci贸n Est谩ndar']
625
-
626
- return "Regresi贸n calculada exitosamente", fig_original, fig_personalizado, df_resumen
627
 
628
  def iniciar_con_ejemplo():
629
  unidad_predicha = "mg/L"
630
- unidad_replicas = "Abs"
631
  df = pd.DataFrame({
632
  "Soluci贸n": [1.00,0.80,0.67,0.60,0.53,0.47,0.40],
633
  "H2O": [0.00,0.20,0.33,0.40,0.47,0.53,0.60],
634
- "Factor de Diluci贸n": [1.00,1.25,1.50,1.67,1.87,2.14,2.50],
635
  f"Concentraci贸n Predicha ({unidad_predicha})": [150,120,100,90,80,70,60],
636
  f"Absorbancia Real 1 ({unidad_replicas})": [1.715,1.089,0.941,0.552,0.703,0.801,0.516]
637
  })
638
  n_replicas = 1
639
  estado, fig, informe, df = actualizar_analisis(df, n_replicas, unidad_predicha, unidad_replicas, [f"Fila {i+1}" for i in df.index], 3)
 
640
  return (
641
- 2000000,
642
  unidad_predicha,
643
  unidad_replicas,
644
  7,
@@ -646,85 +521,99 @@ def iniciar_con_ejemplo():
646
  estado,
647
  fig,
648
  informe,
649
- [f"Fila {i+1}" for i in df.index],
650
  3
651
  )
652
 
653
- import gradio as gr
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
654
 
655
  with gr.Blocks(theme=gr.themes.Soft()) as interfaz:
656
  gr.Markdown("""
657
  # 馃搳 Sistema Avanzado de Calibraci贸n con An谩lisis Estad铆stico
658
 
659
- En esta interfaz puede:
660
- - Ingresar y/o cargar datos de calibraci贸n.
661
- - Ajustar unidades, n煤mero de decimales, r茅plicas, etc.
662
- - Calcular la regresi贸n, visualizar gr谩ficos y generar informes.
663
-
664
- **Sugerencia:** Cargue sus datos o use un ejemplo, luego presione "Calcular" para obtener el an谩lisis.
665
  """)
666
 
667
  with gr.Tab("馃摑 Datos de Calibraci贸n"):
668
- gr.Markdown("""
669
- ### Ingrese Par谩metros Iniciales
670
- Ajuste las unidades y la concentraci贸n inicial, as铆 como el n煤mero de filas y r茅plicas para la tabla.
671
- """)
672
  with gr.Row():
673
  with gr.Column():
674
- concentracion_input = gr.Number(
675
- value=2000000,
676
- label="Concentraci贸n Inicial",
677
- precision=0
678
- )
679
- unidad_predicha_input = gr.Textbox(
680
- value="mg/L",
681
- label="Unidad de Medida (Predicha)",
682
- placeholder="Ejemplo: mg/L"
683
- )
684
- unidad_replicas_input = gr.Textbox(
685
- value="UFC",
686
- label="Unidad de Medida (Absorbancias)",
687
- placeholder="Ejemplo: Abs, OD, UFC, etc."
688
- )
689
-
690
  with gr.Column():
691
- filas_slider = gr.Slider(
692
- minimum=1,
693
- maximum=20,
694
- value=7,
695
- step=1,
696
- label="N煤mero de Filas"
697
- )
698
- decimales_slider = gr.Slider(
699
- minimum=0,
700
- maximum=5,
701
- value=3,
702
- step=1,
703
- label="N煤mero de Decimales"
704
- )
705
- replicas_slider = gr.Slider(
706
- minimum=1,
707
- maximum=10,
708
- value=1,
709
- step=1,
710
- label="N煤mero de R茅plicas"
711
- )
712
 
713
  with gr.Row():
714
  with gr.Column():
715
- gr.Markdown("### Acciones sobre la Tabla")
716
  calcular_btn = gr.Button("馃攧 Calcular", variant="primary")
717
  ajustar_decimales_btn = gr.Button("馃洜 Ajustar Decimales", variant="secondary")
718
  limpiar_btn = gr.Button("馃棏 Limpiar Datos", variant="secondary")
719
-
720
  with gr.Column():
721
- gr.Markdown("### Ejemplos y Carga de Datos")
722
- ejemplo_ufc_btn = gr.Button("馃搵 Cargar Ejemplo UFC", variant="secondary")
723
  ejemplo_od_btn = gr.Button("馃搵 Cargar Ejemplo OD", variant="secondary")
724
  sinteticos_btn = gr.Button("馃И Generar Datos Sint茅ticos", variant="secondary")
725
  cargar_excel_btn = gr.UploadButton("馃搨 Cargar Excel", file_types=[".xlsx"], variant="secondary")
726
 
727
- gr.Markdown("### Tabla de Datos")
728
  tabla_output = gr.DataFrame(
729
  wrap=True,
730
  label="Tabla de Datos",
@@ -733,33 +622,15 @@ with gr.Blocks(theme=gr.themes.Soft()) as interfaz:
733
  )
734
 
735
  with gr.Tab("馃搳 An谩lisis y Reporte"):
736
- gr.Markdown("""
737
- ### An谩lisis y Resultados
738
- Seleccione las filas a utilizar en el an谩lisis y presione Calcular. Ajuste colores y estilo seg煤n su preferencia.
739
- """)
740
- with gr.Row():
741
- filas_seleccionadas = gr.CheckboxGroup(
742
- label="Seleccione filas para el an谩lisis",
743
- choices=[],
744
- value=[],
745
- )
746
-
747
  with gr.Row():
748
  with gr.Column():
749
  color_puntos_picker = gr.ColorPicker(label="Color de Puntos", value="#0000FF")
750
- estilo_puntos_dropdown = gr.Dropdown(
751
- choices=["o", "s", "^", "D", "v", "<", ">", "h", "H", "p", "*", "X", "d"],
752
- value="o",
753
- label="Estilo de Punto"
754
- )
755
 
756
  with gr.Column():
757
  color_linea_ajuste_picker = gr.ColorPicker(label="Color de la L铆nea de Ajuste", value="#00FF00")
758
- estilo_linea_ajuste_dropdown = gr.Dropdown(
759
- choices=["-", "--", "-.", ":"],
760
- value="-",
761
- label="Estilo L铆nea de Ajuste"
762
- )
763
 
764
  with gr.Column():
765
  color_barras_error_picker = gr.ColorPicker(label="Color Barras de Error", value="#FFA500")
@@ -768,13 +639,12 @@ with gr.Blocks(theme=gr.themes.Soft()) as interfaz:
768
 
769
  with gr.Row():
770
  graficar_btn = gr.Button("馃搳 Graficar", variant="primary")
771
- recalcular_btn = gr.Button("馃攧 Recalcular", variant="secondary")
772
 
773
  estado_output = gr.Textbox(label="Estado del An谩lisis", interactive=False)
774
  graficos_output = gr.Plot(label="Gr谩ficos de An谩lisis")
775
- informe_output = gr.Markdown(elem_id="informe_output")
776
 
777
- gr.Markdown("### Exportar Informe")
778
  with gr.Row():
779
  copiar_btn = gr.Button("馃搵 Copiar Informe", variant="secondary")
780
  exportar_word_btn = gr.Button("馃捑 Exportar Informe Word", variant="primary")
@@ -785,72 +655,24 @@ with gr.Blocks(theme=gr.themes.Soft()) as interfaz:
785
  exportar_latex_file = gr.File(label="Informe en LaTeX")
786
 
787
  with gr.Tab("馃搱 Regresi贸n Absorbancia vs Concentraci贸n"):
788
- gr.Markdown("""
789
- ### An谩lisis de Regresi贸n Adicional
790
- Seleccione nuevamente las filas para la regresi贸n y personalice los gr谩ficos.
791
- """)
792
-
793
- filas_seleccionadas_regresion = gr.CheckboxGroup(
794
- label="Seleccione filas para la regresi贸n",
795
- choices=[],
796
- value=[],
797
- )
798
-
799
- with gr.Row():
800
- color_puntos_regresion_picker = gr.ColorPicker(label="Color Puntos (Regresi贸n)", value="#0000FF")
801
- estilo_puntos_regresion = gr.Dropdown(
802
- choices=["o", "s", "^", "D", "v", "<", ">", "h", "H", "p", "*", "X", "d"],
803
- value="o",
804
- label="Estilo de Puntos (Regresi贸n)"
805
- )
806
- color_linea_ajuste_regresion_picker = gr.ColorPicker(label="Color L铆nea Ajuste (Regresi贸n)", value="#00FF00")
807
- estilo_linea_ajuste_regresion = gr.Dropdown(
808
- choices=["-", "--", "-.", ":"],
809
- value="-",
810
- label="Estilo L铆nea de Ajuste (Regresi贸n)"
811
- )
812
- mostrar_linea_ajuste_regresion = gr.Checkbox(value=True, label="Mostrar L铆nea de Ajuste (Regresi贸n)")
813
- mostrar_puntos_regresion = gr.Checkbox(value=True, label="Mostrar Puntos (Regresi贸n)")
814
 
815
  legend_location_dropdown = gr.Dropdown(
816
  choices=[
817
- 'best', 'upper right', 'upper left', 'lower left', 'lower right',
818
- 'right', 'center left', 'center right', 'lower center',
819
- 'upper center', 'center'
820
  ],
821
  value='lower right',
822
  label='Ubicaci贸n de la Leyenda'
823
  )
824
 
825
- with gr.Row():
826
- titulo_grafico_original = gr.Textbox(
827
- label="T铆tulo del Gr谩fico Original",
828
- placeholder="Ej: Regresi贸n Lineal: Abs vs Conc (Original)"
829
- )
830
- titulo_grafico_personalizado = gr.Textbox(
831
- label="T铆tulo del Gr谩fico Personalizado",
832
- placeholder="Ej: Regresi贸n Lineal Personalizada"
833
- )
834
-
835
- with gr.Row():
836
- eje_x_original = gr.Textbox(
837
- label="Etiqueta Eje X (Original)",
838
- placeholder="Concentraci贸n Predicha (mg/L)"
839
- )
840
- eje_y_original = gr.Textbox(
841
- label="Etiqueta Eje Y (Original)",
842
- placeholder="Absorbancia Real Promedio (UFC)"
843
- )
844
-
845
- with gr.Row():
846
- eje_x_personalizado = gr.Textbox(
847
- label="Etiqueta Eje X (Personalizado)",
848
- placeholder="Concentraci贸n Predicha (mg/L)"
849
- )
850
- eje_y_personalizado = gr.Textbox(
851
- label="Etiqueta Eje Y (Personalizado)",
852
- placeholder="Absorbancia Real Promedio (UFC)"
853
- )
854
 
855
  calcular_regresion_btn = gr.Button("Calcular Regresi贸n")
856
 
@@ -859,123 +681,50 @@ with gr.Blocks(theme=gr.themes.Soft()) as interfaz:
859
  grafico_personalizado_output = gr.Plot(label="Gr谩fico Personalizado")
860
  tabla_resumen_output = gr.DataFrame(label="Tabla Resumida")
861
 
862
- tabla_output.change(
863
- fn=actualizar_opciones_filas,
864
- inputs=[tabla_output],
865
- outputs=[filas_seleccionadas, filas_seleccionadas_regresion]
866
- )
867
 
868
  calcular_btn.click(
869
- fn=actualizar_analisis,
870
  inputs=[tabla_output, replicas_slider, unidad_predicha_input, unidad_replicas_input, filas_seleccionadas, decimales_slider],
871
  outputs=[estado_output, graficos_output, informe_output, tabla_output]
872
  )
873
 
874
- def actualizar_graficos_custom(df, n_replicas, unidad_predicha, unidad_replicas,
875
- color_puntos, estilo_puntos,
876
- color_linea_ajuste, estilo_linea_ajuste,
877
- color_barras_error,
878
- mostrar_linea_ajuste, mostrar_puntos,
879
- filas_seleccionadas, decimales):
880
- if df is None or df.empty:
881
- return None
882
-
883
- df = calcular_promedio_desviacion(df, n_replicas, unidad_predicha, unidad_replicas, decimales)
884
-
885
- col_predicha = f"Concentraci贸n Predicha ({unidad_predicha})"
886
- col_real_promedio = f"Absorbancia Real Promedio ({unidad_replicas})"
887
-
888
- df[col_predicha] = pd.to_numeric(df[col_predicha], errors='coerce')
889
- df[col_real_promedio] = pd.to_numeric(df[col_real_promedio], errors='coerce')
890
-
891
- df_valid = df.dropna(subset=[col_predicha, col_real_promedio])
892
- df_valid.reset_index(drop=True, inplace=True)
893
-
894
- if not filas_seleccionadas:
895
- return None
896
-
897
- indices_seleccionados = [int(s.split(' ')[1]) - 1 for s in filas_seleccionadas]
898
- df_valid = df_valid.loc[indices_seleccionados]
899
-
900
- if len(df_valid) < 2:
901
- return None
902
-
903
- fig = generar_graficos(
904
- df_valid, n_replicas, unidad_predicha, unidad_replicas,
905
- color_puntos, estilo_puntos,
906
- color_linea_ajuste, estilo_linea_ajuste,
907
- color_barras_error,
908
- mostrar_linea_ajuste, mostrar_puntos
909
- )
910
- return fig
911
-
912
- graficar_btn.click(
913
- fn=actualizar_graficos_custom,
914
- inputs=[
915
- tabla_output, replicas_slider, unidad_predicha_input, unidad_replicas_input,
916
- color_puntos_picker, estilo_puntos_dropdown,
917
- color_linea_ajuste_picker, estilo_linea_ajuste_dropdown,
918
- color_barras_error_picker,
919
- mostrar_linea_ajuste, mostrar_puntos,
920
- filas_seleccionadas, decimales_slider
921
- ],
922
- outputs=graficos_output
923
- )
924
-
925
- recalcular_btn.click(
926
- fn=actualizar_analisis,
927
- inputs=[tabla_output, replicas_slider, unidad_predicha_input, unidad_replicas_input, filas_seleccionadas, decimales_slider],
928
- outputs=[estado_output, graficos_output, informe_output, tabla_output]
929
  )
930
 
931
- def resetear_valores():
932
- return gr.update(value=True), gr.update(value=True)
933
-
934
- calcular_btn.click(fn=resetear_valores, outputs=[mostrar_linea_ajuste, mostrar_puntos])
935
- limpiar_btn.click(fn=resetear_valores, outputs=[mostrar_linea_ajuste, mostrar_puntos])
936
- ajustar_decimales_btn.click(fn=resetear_valores, outputs=[mostrar_linea_ajuste, mostrar_puntos])
937
- sinteticos_btn.click(fn=resetear_valores, outputs=[mostrar_linea_ajuste, mostrar_puntos])
938
 
939
  limpiar_btn.click(
940
- fn=limpiar_datos,
941
  inputs=[replicas_slider],
942
- outputs=[concentracion_input, unidad_predicha_input, filas_slider, tabla_output, estado_output, graficos_output, informe_output]
943
  )
944
 
945
- def cargar_ejemplo_ufc(n_replicas):
946
- unidad_predicha = "mg/L"
947
- unidad_replicas = "Abs"
948
- df = pd.DataFrame({
949
- "Soluci贸n": [1.00,0.80,0.67,0.60,0.53,0.47,0.40],
950
- "H2O": [0.00,0.20,0.33,0.40,0.47,0.53,0.60],
951
- "Factor de Diluci贸n": [1.00,1.25,1.50,1.67,1.87,2.14,2.50],
952
- f"Concentraci贸n Predicha ({unidad_predicha})": [150,120,100,90,80,70,60]
953
- })
954
- for i in range(1, n_replicas + 1):
955
- df[f"Absorbancia Real {i} ({unidad_replicas})"] = np.nan
956
- return 2000000, unidad_predicha, unidad_replicas, 7, df
957
-
958
- def cargar_ejemplo_od(n_replicas):
959
  unidad_predicha = "mg/L"
960
- unidad_replicas = "OD"
961
  df = pd.DataFrame({
962
  "Soluci贸n": [1.00,0.80,0.60,0.40,0.20,0.10,0.05],
963
  "H2O": [0.00,0.20,0.40,0.60,0.80,0.90,0.95],
964
- "Factor de Diluci贸n": [1.00,1.25,1.67,2.50,5.00,10.00,20.00],
965
  f"Concentraci贸n Predicha ({unidad_predicha})": [1.0,0.8,0.6,0.4,0.2,0.1,0.05]
966
  })
967
  for i in range(1, n_replicas + 1):
968
  df[f"Absorbancia Real {i} ({unidad_replicas})"] = np.nan
969
- return 1.000, unidad_predicha, unidad_replicas, 7, df
970
-
971
- ejemplo_ufc_btn.click(
972
- fn=cargar_ejemplo_ufc,
973
- inputs=[replicas_slider],
974
- outputs=[concentracion_input, unidad_predicha_input, unidad_replicas_input, filas_slider, tabla_output]
975
- )
976
 
977
  ejemplo_od_btn.click(
978
- fn=cargar_ejemplo_od,
979
  inputs=[replicas_slider],
980
  outputs=[concentracion_input, unidad_predicha_input, unidad_replicas_input, filas_slider, tabla_output]
981
  )
@@ -989,53 +738,18 @@ with gr.Blocks(theme=gr.themes.Soft()) as interfaz:
989
  cargar_excel_btn.upload(
990
  fn=cargar_excel,
991
  inputs=[cargar_excel_btn],
992
- outputs=[concentracion_input, unidad_predicha_input, filas_slider, replicas_slider, tabla_output, estado_output, graficos_output, informe_output]
993
- )
994
-
995
- ajustar_decimales_btn.click(
996
- fn=ajustar_decimales_evento,
997
- inputs=[tabla_output, decimales_slider],
998
- outputs=tabla_output
999
  )
1000
 
1001
- def actualizar_tabla_wrapper(df, filas, conc, unidad_predicha, unidad_replicas, replicas, decimales):
1002
- return actualizar_tabla_evento(df, filas, conc, unidad_predicha, unidad_replicas, replicas, decimales)
1003
-
1004
- concentracion_input.change(
1005
- fn=actualizar_tabla_wrapper,
1006
- inputs=[tabla_output, filas_slider, concentracion_input, unidad_predicha_input, unidad_replicas_input, replicas_slider, decimales_slider],
1007
- outputs=tabla_output
1008
- )
1009
 
1010
- unidad_predicha_input.change(
1011
- fn=actualizar_tabla_wrapper,
1012
- inputs=[tabla_output, filas_slider, concentracion_input, unidad_predicha_input, unidad_replicas_input, replicas_slider, decimales_slider],
1013
- outputs=tabla_output
1014
- )
1015
-
1016
- unidad_replicas_input.change(
1017
- fn=actualizar_tabla_wrapper,
1018
- inputs=[tabla_output, filas_slider, concentracion_input, unidad_predicha_input, unidad_replicas_input, replicas_slider, decimales_slider],
1019
- outputs=tabla_output
1020
- )
1021
-
1022
- filas_slider.change(
1023
- fn=actualizar_tabla_wrapper,
1024
- inputs=[tabla_output, filas_slider, concentracion_input, unidad_predicha_input, unidad_replicas_input, replicas_slider, decimales_slider],
1025
- outputs=tabla_output
1026
- )
1027
-
1028
- replicas_slider.change(
1029
- fn=actualizar_tabla_wrapper,
1030
- inputs=[tabla_output, filas_slider, concentracion_input, unidad_predicha_input, unidad_replicas_input, replicas_slider, decimales_slider],
1031
- outputs=tabla_output
1032
- )
1033
-
1034
- decimales_slider.change(
1035
- fn=ajustar_decimales_evento,
1036
- inputs=[tabla_output, decimales_slider],
1037
- outputs=tabla_output
1038
- )
1039
 
1040
  copiar_btn.click(
1041
  None,
@@ -1067,25 +781,152 @@ with gr.Blocks(theme=gr.themes.Soft()) as interfaz:
1067
  outputs=exportar_latex_file
1068
  )
1069
 
1070
- interfaz.load(
1071
- fn=iniciar_con_ejemplo,
1072
- outputs=[concentracion_input, unidad_predicha_input, unidad_replicas_input, filas_slider, tabla_output, estado_output, graficos_output, informe_output, filas_seleccionadas, decimales_slider]
 
 
 
1073
  )
1074
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1075
  calcular_regresion_btn.click(
1076
  fn=calcular_regresion_tabla_principal,
1077
- inputs=[
1078
- tabla_output, unidad_predicha_input, unidad_replicas_input, filas_seleccionadas_regresion,
1079
- color_puntos_regresion_picker, estilo_puntos_regresion,
1080
- color_linea_ajuste_regresion_picker, estilo_linea_ajuste_regresion,
1081
- mostrar_linea_ajuste_regresion, mostrar_puntos_regresion,
1082
- legend_location_dropdown, decimales_slider,
1083
- titulo_grafico_original, titulo_grafico_personalizado,
1084
- eje_x_original, eje_y_original,
1085
- eje_x_personalizado, eje_y_personalizado
1086
- ],
1087
  outputs=[estado_regresion_output, grafico_original_output, grafico_personalizado_output, tabla_resumen_output]
1088
  )
1089
 
 
 
 
 
 
1090
  if __name__ == "__main__":
1091
  interfaz.launch()
 
266
  return estado, fig, informe, df
267
 
268
  def exportar_informe_word(df_valid, informe_md, unidad_predicha, unidad_replicas):
269
+ import docx
270
  doc = docx.Document()
271
 
272
  style = doc.styles['Normal']
 
398
 
399
  def limpiar_datos(n_replicas):
400
  unidad_predicha = "mg/L"
401
+ # Unidad de replicas por defecto Abs
402
  unidad_replicas = "Abs"
 
403
  df = pd.DataFrame({
404
  "Soluci贸n": [1/(2**i) for i in range(7)],
405
  "H2O": [1-(1/(2**i)) for i in range(7)],
406
+ "Dimensi贸n de Diluci贸n": [(1/(1/(2**i))) for i in range(7)],
407
+ f"Concentraci贸n Predicha ({unidad_predicha})": [150/(1/(1/(2**i))) for i in range(7)]
408
  })
409
  for i in range(1, n_replicas+1):
410
  df[f"Absorbancia Real {i} ({unidad_replicas})"] = np.nan
411
 
412
  return (
413
+ 150,
414
  unidad_predicha,
415
  7,
416
  df,
417
+ "", # estado
418
+ None, # graficos
419
+ "" # informe
420
  )
421
 
422
  def generar_datos_sinteticos_evento(df, n_replicas, unidad_predicha, unidad_replicas):
 
424
  col_predicha = f"Concentraci贸n Predicha ({unidad_predicha})"
425
  if col_predicha in df.columns:
426
  df[col_predicha] = pd.to_numeric(df[col_predicha], errors='coerce')
427
+ if not df[col_predicha].empty:
428
+ for i in range(1, n_replicas + 1):
429
+ col_real = f"Absorbancia Real {i} ({unidad_replicas})"
430
+ desviacion_std = 0.05 * df[col_predicha].mean()
431
+ valores_predichos = df[col_predicha].dropna().values
432
+ if len(valores_predichos) == 0:
433
+ continue
434
+ datos_sinteticos = valores_predichos + np.random.normal(0, desviacion_std, size=len(valores_predichos))
435
+ datos_sinteticos = np.maximum(0, datos_sinteticos)
436
+ datos_sinteticos = np.round(datos_sinteticos, 3)
437
+ df.loc[df[col_predicha].notna(), col_real] = datos_sinteticos
438
  return df
439
 
440
  def actualizar_tabla_evento(df, n_filas, conc, unidad_predicha, unidad_replicas, n_replicas, decimales):
 
451
  all_sheets = pd.read_excel(file.name, sheet_name=None)
452
 
453
  if len(all_sheets) < 3:
454
+ return "El archivo debe tener al menos tres pesta帽as (Hoja1, Hoja2, Hoja3).", None, None, None, None, None, None, "", [], []
455
 
456
  sheet_names = list(all_sheets.keys())
457
  sheet1_name = sheet_names[0]
 
472
  except:
473
  unidad_predicha = "mg/L"
474
 
475
+ # Unidad por defecto Abs
476
  unidad_replicas = "Abs"
477
+ concentracion_inicial = 150.0
 
 
478
  n_filas = len(df_base)
479
  n_replicas = 2
480
 
 
486
  df_sistema[f"Absorbancia Real 1 ({unidad_replicas})"] = col_replica_1
487
  df_sistema[f"Absorbancia Real 2 ({unidad_replicas})"] = col_replica_2
488
 
489
+ return concentracion_inicial, unidad_predicha, n_filas, n_replicas, df_sistema, "", None, "", [], []
490
 
491
  def actualizar_opciones_filas(df):
492
  if df is None or df.empty:
493
  update = gr.update(choices=[], value=[])
494
+ update_regresion = gr.update(choices=[], value=[])
495
  else:
496
  opciones = [f"Fila {i+1}" for i in df.index]
497
+ # Marcamos todas las filas por defecto
498
  update = gr.update(choices=opciones, value=opciones)
499
+ update_regresion = gr.update(choices=opciones, value=opciones)
500
+ return update, update_regresion
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
501
 
502
  def iniciar_con_ejemplo():
503
  unidad_predicha = "mg/L"
504
+ unidad_replicas = "Abs" # Por defecto Abs
505
  df = pd.DataFrame({
506
  "Soluci贸n": [1.00,0.80,0.67,0.60,0.53,0.47,0.40],
507
  "H2O": [0.00,0.20,0.33,0.40,0.47,0.53,0.60],
508
+ "Dimensi贸n de Diluci贸n": [1.00,1.25,1.50,1.67,1.87,2.14,2.50],
509
  f"Concentraci贸n Predicha ({unidad_predicha})": [150,120,100,90,80,70,60],
510
  f"Absorbancia Real 1 ({unidad_replicas})": [1.715,1.089,0.941,0.552,0.703,0.801,0.516]
511
  })
512
  n_replicas = 1
513
  estado, fig, informe, df = actualizar_analisis(df, n_replicas, unidad_predicha, unidad_replicas, [f"Fila {i+1}" for i in df.index], 3)
514
+ filas = [f"Fila {i+1}" for i in df.index]
515
  return (
516
+ 150,
517
  unidad_predicha,
518
  unidad_replicas,
519
  7,
 
521
  estado,
522
  fig,
523
  informe,
524
+ filas,
525
  3
526
  )
527
 
528
+ def recalcular_y_graficar(df, n_replicas, unidad_predicha, unidad_replicas, filas_seleccionadas, decimales,
529
+ color_puntos, estilo_puntos, color_linea_ajuste, estilo_linea_ajuste,
530
+ color_barras_error, mostrar_linea_ajuste, mostrar_puntos):
531
+ estado, fig_base, informe, df = actualizar_analisis(df, n_replicas, unidad_predicha, unidad_replicas, filas_seleccionadas, decimales)
532
+ fig_custom = actualizar_graficos_custom(df, n_replicas, unidad_predicha, unidad_replicas,
533
+ color_puntos, estilo_puntos,
534
+ color_linea_ajuste, estilo_linea_ajuste,
535
+ color_barras_error,
536
+ mostrar_linea_ajuste, mostrar_puntos,
537
+ filas_seleccionadas, decimales)
538
+ return estado, fig_custom, informe
539
+
540
+ def actualizar_graficos_custom(df, n_replicas, unidad_predicha, unidad_replicas,
541
+ color_puntos, estilo_puntos,
542
+ color_linea_ajuste, estilo_linea_ajuste,
543
+ color_barras_error,
544
+ mostrar_linea_ajuste, mostrar_puntos,
545
+ filas_seleccionadas, decimales):
546
+ if df is None or df.empty:
547
+ return None
548
+
549
+ df = calcular_promedio_desviacion(df, n_replicas, unidad_predicha, unidad_replicas, decimales)
550
+ col_predicha = f"Concentraci贸n Predicha ({unidad_predicha})"
551
+ col_real_promedio = f"Absorbancia Real Promedio ({unidad_replicas})"
552
+
553
+ df[col_predicha] = pd.to_numeric(df[col_predicha], errors='coerce')
554
+ df[col_real_promedio] = pd.to_numeric(df[col_real_promedio], errors='coerce')
555
+
556
+ df_valid = df.dropna(subset=[col_predicha, col_real_promedio])
557
+ df_valid.reset_index(drop=True, inplace=True)
558
+
559
+ if not filas_seleccionadas:
560
+ return None
561
+
562
+ indices_seleccionados = [int(s.split(' ')[1]) - 1 for s in filas_seleccionadas if s.startswith('Fila')]
563
+ df_valid = df_valid.loc[indices_seleccionados]
564
+
565
+ if len(df_valid) < 2:
566
+ return None
567
+
568
+ fig = generar_graficos(
569
+ df_valid, n_replicas, unidad_predicha, unidad_replicas,
570
+ color_puntos, estilo_puntos,
571
+ color_linea_ajuste, estilo_linea_ajuste,
572
+ color_barras_error,
573
+ mostrar_linea_ajuste, mostrar_puntos
574
+ )
575
+ return fig
576
+
577
+ def resetear_ajustes():
578
+ return (
579
+ gr.update(value=True), # mostrar_linea_ajuste
580
+ gr.update(value=True), # mostrar_puntos
581
+ gr.update(value="#0000FF"), # color_puntos
582
+ gr.update(value="o"), # estilo_puntos
583
+ gr.update(value="#00FF00"), # color_linea_ajuste
584
+ gr.update(value="-"), # estilo_linea_ajuste
585
+ gr.update(value="#FFA500") # color_barras_error
586
+ )
587
 
588
  with gr.Blocks(theme=gr.themes.Soft()) as interfaz:
589
  gr.Markdown("""
590
  # 馃搳 Sistema Avanzado de Calibraci贸n con An谩lisis Estad铆stico
591
 
592
+ Ahora la unidad de medida por defecto es "Abs", tanto al limpiar datos como al cargar ejemplo OD.
 
 
 
 
 
593
  """)
594
 
595
  with gr.Tab("馃摑 Datos de Calibraci贸n"):
 
 
 
 
596
  with gr.Row():
597
  with gr.Column():
598
+ concentracion_input = gr.Number(value=150,label="Concentraci贸n Inicial",precision=0)
599
+ unidad_predicha_input = gr.Textbox(value="mg/L",label="Unidad de Medida (Predicha)")
600
+ # Por defecto Abs
601
+ unidad_replicas_input = gr.Textbox(value="Abs",label="Unidad de Medida (Absorbancias)")
 
 
 
 
 
 
 
 
 
 
 
 
602
  with gr.Column():
603
+ filas_slider = gr.Slider(minimum=1,maximum=20,value=7,step=1,label="N煤mero de Filas")
604
+ decimales_slider = gr.Slider(minimum=0,maximum=5,value=3,step=1,label="N煤mero de Decimales")
605
+ replicas_slider = gr.Slider(minimum=1,maximum=10,value=1,step=1,label="N煤mero de R茅plicas")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
606
 
607
  with gr.Row():
608
  with gr.Column():
 
609
  calcular_btn = gr.Button("馃攧 Calcular", variant="primary")
610
  ajustar_decimales_btn = gr.Button("馃洜 Ajustar Decimales", variant="secondary")
611
  limpiar_btn = gr.Button("馃棏 Limpiar Datos", variant="secondary")
 
612
  with gr.Column():
 
 
613
  ejemplo_od_btn = gr.Button("馃搵 Cargar Ejemplo OD", variant="secondary")
614
  sinteticos_btn = gr.Button("馃И Generar Datos Sint茅ticos", variant="secondary")
615
  cargar_excel_btn = gr.UploadButton("馃搨 Cargar Excel", file_types=[".xlsx"], variant="secondary")
616
 
 
617
  tabla_output = gr.DataFrame(
618
  wrap=True,
619
  label="Tabla de Datos",
 
622
  )
623
 
624
  with gr.Tab("馃搳 An谩lisis y Reporte"):
625
+ filas_seleccionadas = gr.CheckboxGroup(label="Seleccione filas para el an谩lisis",choices=[],value=[])
 
 
 
 
 
 
 
 
 
 
626
  with gr.Row():
627
  with gr.Column():
628
  color_puntos_picker = gr.ColorPicker(label="Color de Puntos", value="#0000FF")
629
+ estilo_puntos_dropdown = gr.Dropdown(choices=["o","s","^","D","v","<",">","h","H","p","*","X","d"],value="o",label="Estilo de Punto")
 
 
 
 
630
 
631
  with gr.Column():
632
  color_linea_ajuste_picker = gr.ColorPicker(label="Color de la L铆nea de Ajuste", value="#00FF00")
633
+ estilo_linea_ajuste_dropdown = gr.Dropdown(choices=["-","--","-.",":"],value="-",label="Estilo L铆nea de Ajuste")
 
 
 
 
634
 
635
  with gr.Column():
636
  color_barras_error_picker = gr.ColorPicker(label="Color Barras de Error", value="#FFA500")
 
639
 
640
  with gr.Row():
641
  graficar_btn = gr.Button("馃搳 Graficar", variant="primary")
642
+ resetear_btn = gr.Button("馃攧 Resetear Ajustes", variant="secondary")
643
 
644
  estado_output = gr.Textbox(label="Estado del An谩lisis", interactive=False)
645
  graficos_output = gr.Plot(label="Gr谩ficos de An谩lisis")
646
+ informe_output = gr.Markdown()
647
 
 
648
  with gr.Row():
649
  copiar_btn = gr.Button("馃搵 Copiar Informe", variant="secondary")
650
  exportar_word_btn = gr.Button("馃捑 Exportar Informe Word", variant="primary")
 
655
  exportar_latex_file = gr.File(label="Informe en LaTeX")
656
 
657
  with gr.Tab("馃搱 Regresi贸n Absorbancia vs Concentraci贸n"):
658
+ filas_seleccionadas_regresion = gr.CheckboxGroup(label="Seleccione filas para la regresi贸n",choices=[],value=[])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
659
 
660
  legend_location_dropdown = gr.Dropdown(
661
  choices=[
662
+ 'best','upper right','upper left','lower left','lower right',
663
+ 'right','center left','center right','lower center',
664
+ 'upper center','center'
665
  ],
666
  value='lower right',
667
  label='Ubicaci贸n de la Leyenda'
668
  )
669
 
670
+ titulo_grafico_original = gr.Textbox(label="T铆tulo del Gr谩fico Original")
671
+ titulo_grafico_personalizado = gr.Textbox(label="T铆tulo del Gr谩fico Personalizado")
672
+ eje_x_original = gr.Textbox(label="Etiqueta Eje X (Original)")
673
+ eje_y_original = gr.Textbox(label="Etiqueta Eje Y (Original)")
674
+ eje_x_personalizado = gr.Textbox(label="Etiqueta Eje X (Personalizado)")
675
+ eje_y_personalizado = gr.Textbox(label="Etiqueta Eje Y (Personalizado)")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
676
 
677
  calcular_regresion_btn = gr.Button("Calcular Regresi贸n")
678
 
 
681
  grafico_personalizado_output = gr.Plot(label="Gr谩fico Personalizado")
682
  tabla_resumen_output = gr.DataFrame(label="Tabla Resumida")
683
 
684
+ tabla_output.change(fn=actualizar_opciones_filas, inputs=[tabla_output], outputs=[filas_seleccionadas, filas_seleccionadas_regresion])
685
+
686
+ def calcular_y_marcar(df, replicas, up, ur, sel, dec):
687
+ estado, fig, informe, df = actualizar_analisis(df, replicas, up, ur, sel, dec)
688
+ return estado, fig, informe, df
689
 
690
  calcular_btn.click(
691
+ fn=calcular_y_marcar,
692
  inputs=[tabla_output, replicas_slider, unidad_predicha_input, unidad_replicas_input, filas_seleccionadas, decimales_slider],
693
  outputs=[estado_output, graficos_output, informe_output, tabla_output]
694
  )
695
 
696
+ ajustar_decimales_btn.click(
697
+ fn=ajustar_decimales_evento,
698
+ inputs=[tabla_output, decimales_slider],
699
+ outputs=tabla_output
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
700
  )
701
 
702
+ def limpiar_todo(n_replicas):
703
+ c,u_pred,f_slider,df,est,g,inf = limpiar_datos(n_replicas)
704
+ return c,u_pred,"Abs",f_slider,df,"",None,"",[],[],3
 
 
 
 
705
 
706
  limpiar_btn.click(
707
+ fn=limpiar_todo,
708
  inputs=[replicas_slider],
709
+ outputs=[concentracion_input, unidad_predicha_input, unidad_replicas_input, filas_slider, tabla_output, estado_output, graficos_output, informe_output, filas_seleccionadas, filas_seleccionadas_regresion, decimales_slider]
710
  )
711
 
712
+ # Cargar ejemplo OD, pero ahora con unidad_replicas = "Abs"
713
+ def cargar_ejemplo_od_func(n_replicas):
 
 
 
 
 
 
 
 
 
 
 
 
714
  unidad_predicha = "mg/L"
715
+ unidad_replicas = "Abs" # Cambiado a Abs
716
  df = pd.DataFrame({
717
  "Soluci贸n": [1.00,0.80,0.60,0.40,0.20,0.10,0.05],
718
  "H2O": [0.00,0.20,0.40,0.60,0.80,0.90,0.95],
719
+ "Dimensi贸n de Diluci贸n": [1.00,1.25,1.67,2.50,5.00,10.00,20.00],
720
  f"Concentraci贸n Predicha ({unidad_predicha})": [1.0,0.8,0.6,0.4,0.2,0.1,0.05]
721
  })
722
  for i in range(1, n_replicas + 1):
723
  df[f"Absorbancia Real {i} ({unidad_replicas})"] = np.nan
724
+ return 150, unidad_predicha, unidad_replicas, 7, df
 
 
 
 
 
 
725
 
726
  ejemplo_od_btn.click(
727
+ fn=cargar_ejemplo_od_func,
728
  inputs=[replicas_slider],
729
  outputs=[concentracion_input, unidad_predicha_input, unidad_replicas_input, filas_slider, tabla_output]
730
  )
 
738
  cargar_excel_btn.upload(
739
  fn=cargar_excel,
740
  inputs=[cargar_excel_btn],
741
+ outputs=[concentracion_input, unidad_predicha_input, filas_slider, replicas_slider, tabla_output, estado_output, graficos_output, informe_output, filas_seleccionadas, filas_seleccionadas_regresion]
 
 
 
 
 
 
742
  )
743
 
744
+ def actualizar_tabla_wrapper(df, filas, conc, up, ur, rep, dec):
745
+ return actualizar_tabla_evento(df, filas, conc, up, ur, rep, dec)
 
 
 
 
 
 
746
 
747
+ concentracion_input.change(actualizar_tabla_wrapper,[tabla_output,filas_slider,concentracion_input,unidad_predicha_input,unidad_replicas_input,replicas_slider,decimales_slider],tabla_output)
748
+ unidad_predicha_input.change(actualizar_tabla_wrapper,[tabla_output,filas_slider,concentracion_input,unidad_predicha_input,unidad_replicas_input,replicas_slider,decimales_slider],tabla_output)
749
+ unidad_replicas_input.change(actualizar_tabla_wrapper,[tabla_output,filas_slider,concentracion_input,unidad_predicha_input,unidad_replicas_input,replicas_slider,decimales_slider],tabla_output)
750
+ filas_slider.change(actualizar_tabla_wrapper,[tabla_output,filas_slider,concentracion_input,unidad_predicha_input,unidad_replicas_input,replicas_slider,decimales_slider],tabla_output)
751
+ replicas_slider.change(actualizar_tabla_wrapper,[tabla_output,filas_slider,concentracion_input,unidad_predicha_input,unidad_replicas_input,replicas_slider,decimales_slider],tabla_output)
752
+ decimales_slider.change(ajustar_decimales_evento,[tabla_output,decimales_slider],tabla_output)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
753
 
754
  copiar_btn.click(
755
  None,
 
781
  outputs=exportar_latex_file
782
  )
783
 
784
+ graficar_btn.click(
785
+ fn=recalcular_y_graficar,
786
+ inputs=[tabla_output, replicas_slider, unidad_predicha_input, unidad_replicas_input, filas_seleccionadas, decimales_slider,
787
+ color_puntos_picker, estilo_puntos_dropdown, color_linea_ajuste_picker, estilo_linea_ajuste_dropdown,
788
+ color_barras_error_picker, mostrar_linea_ajuste, mostrar_puntos],
789
+ outputs=[estado_output, graficos_output, informe_output]
790
  )
791
 
792
+ resetear_btn.click(fn=resetear_ajustes, outputs=[mostrar_linea_ajuste, mostrar_puntos, color_puntos_picker, estilo_puntos_dropdown, color_linea_ajuste_picker, estilo_linea_ajuste_dropdown, color_barras_error_picker])
793
+
794
+ def calcular_regresion_tabla_principal(df, up, ur, fsr,
795
+ cp, ep, cla, ela,
796
+ mla, mp,
797
+ ll, dec,
798
+ tgo, tgp,
799
+ exo, eyo,
800
+ exp, eyp):
801
+
802
+ if df is None or df.empty:
803
+ return "Datos insuficientes", None, None, None
804
+
805
+ col_predicha = f"Concentraci贸n Predicha ({up})"
806
+ col_real_promedio = f"Absorbancia Real Promedio ({ur})"
807
+ col_desviacion = f"Desviaci贸n Est谩ndar ({ur})"
808
+
809
+ n_replicas = len([c for c in df.columns if 'Absorbancia Real ' in c and 'Promedio' not in c and 'Desviaci贸n' not in c])
810
+ df = calcular_promedio_desviacion(df, n_replicas, up, ur, dec)
811
+
812
+ if col_predicha not in df.columns or col_real_promedio not in df.columns:
813
+ return "Faltan columnas necesarias", None, None, None
814
+
815
+ df[col_predicha] = pd.to_numeric(df[col_predicha], errors='coerce')
816
+ df[col_real_promedio] = pd.to_numeric(df[col_real_promedio], errors='coerce')
817
+ df[col_desviacion] = pd.to_numeric(df[col_desviacion], errors='coerce').fillna(0)
818
+
819
+ df_valid = df.dropna(subset=[col_predicha, col_real_promedio])
820
+ df_valid.reset_index(drop=True, inplace=True)
821
+
822
+ df_original = df_valid.copy()
823
+
824
+ if not fsr or len(fsr) < 2:
825
+ return "Se necesitan m谩s datos", None, None, None
826
+
827
+ indices_seleccionados = [int(s.split(' ')[1]) - 1 for s in fsr]
828
+ if len(indices_seleccionados) < 2:
829
+ return "Se requieren al menos dos puntos para calcular la regresi贸n", None, None, None
830
+
831
+ df_valid = df_valid.loc[indices_seleccionados]
832
+
833
+ slope, intercept, r_value, p_value, std_err = stats.linregress(df_valid[col_predicha], df_valid[col_real_promedio])
834
+
835
+ sns.set(style="whitegrid")
836
+ fig_original, ax_original = plt.subplots(figsize=(8, 6))
837
+ ax_original.errorbar(
838
+ df_original[col_predicha],
839
+ df_original[col_real_promedio],
840
+ yerr=df_original[col_desviacion],
841
+ fmt=ep,
842
+ color=safe_color(cp),
843
+ ecolor='gray',
844
+ elinewidth=1,
845
+ capsize=3,
846
+ label='Datos'
847
+ )
848
+ slope_all, intercept_all, r_value_all, p_value_all, std_err_all = stats.linregress(df_original[col_predicha], df_original[col_real_promedio])
849
+ ax_original.plot(
850
+ df_original[col_predicha],
851
+ intercept_all + slope_all * df_original[col_predicha],
852
+ color=safe_color(cla),
853
+ linestyle='-',
854
+ label='Ajuste Lineal'
855
+ )
856
+
857
+ ax_original.set_xlabel(exo if exo else f'Concentraci贸n Predicha ({up})')
858
+ ax_original.set_ylabel(eyo if eyo else f'Absorbancia Real Promedio ({ur})')
859
+ ax_original.set_title(tgo if tgo else 'Regresi贸n Lineal: Absorbancia Real vs Concentraci贸n Predicha (Original)')
860
+ ax_original.legend(loc=ll)
861
+ ax_original.annotate(
862
+ f'y = {intercept_all:.4f} + {slope_all:.4f}x\n$R^2$ = {r_value_all**2:.4f}',
863
+ xy=(0.05, 0.95),
864
+ xycoords='axes fraction',
865
+ fontsize=12,
866
+ backgroundcolor='white',
867
+ verticalalignment='top'
868
+ )
869
+
870
+ sns.set(style="whitegrid")
871
+ fig_personalizado, ax_personalizado = plt.subplots(figsize=(8, 6))
872
+
873
+ if mp:
874
+ ax_personalizado.errorbar(
875
+ df_valid[col_predicha],
876
+ df_valid[col_real_promedio],
877
+ yerr=df_valid[col_desviacion],
878
+ fmt=ep,
879
+ color=safe_color(cp),
880
+ ecolor='gray',
881
+ elinewidth=1,
882
+ capsize=3,
883
+ label='Datos'
884
+ )
885
+
886
+ if mla:
887
+ ax_personalizado.plot(
888
+ df_valid[col_predicha],
889
+ intercept + slope * df_valid[col_predicha],
890
+ color=safe_color(cla),
891
+ linestyle=ela,
892
+ label='Ajuste Lineal'
893
+ )
894
+
895
+ ax_personalizado.set_xlabel(exp if exp else f'Concentraci贸n Predicha ({up})')
896
+ ax_personalizado.set_ylabel(eyp if eyp else f'Absorbancia Real Promedio ({ur})')
897
+ ax_personalizado.set_title(tgp if tgp else 'Regresi贸n Lineal Personalizada')
898
+ ax_personalizado.legend(loc=ll)
899
+ ax_personalizado.annotate(
900
+ f'y = {intercept:.4f} + {slope:.4f}x\n$R^2$ = {r_value**2:.4f}',
901
+ xy=(0.05, 0.95),
902
+ xycoords='axes fraction',
903
+ fontsize=12,
904
+ backgroundcolor='white',
905
+ verticalalignment='top'
906
+ )
907
+
908
+ df_resumen = df_valid[[col_predicha, col_real_promedio, col_desviacion]].copy()
909
+ df_resumen.columns = [f'Concentraci贸n Predicha ({up})', 'Absorbancia Promedio', 'Desviaci贸n Est谩ndar']
910
+
911
+ return "Regresi贸n calculada exitosamente", fig_original, fig_personalizado, df_resumen
912
+
913
  calcular_regresion_btn.click(
914
  fn=calcular_regresion_tabla_principal,
915
+ inputs=[tabla_output, unidad_predicha_input, unidad_replicas_input, filas_seleccionadas_regresion,
916
+ color_puntos_picker, estilo_puntos_dropdown,
917
+ color_linea_ajuste_picker, estilo_linea_ajuste_dropdown,
918
+ mostrar_linea_ajuste, mostrar_puntos,
919
+ legend_location_dropdown, decimales_slider,
920
+ titulo_grafico_original, titulo_grafico_personalizado,
921
+ eje_x_original, eje_y_original,
922
+ eje_x_personalizado, eje_y_personalizado],
 
 
923
  outputs=[estado_regresion_output, grafico_original_output, grafico_personalizado_output, tabla_resumen_output]
924
  )
925
 
926
+ interfaz.load(
927
+ fn=iniciar_con_ejemplo,
928
+ outputs=[concentracion_input, unidad_predicha_input, unidad_replicas_input, filas_slider, tabla_output, estado_output, graficos_output, informe_output, filas_seleccionadas, decimales_slider]
929
+ )
930
+
931
  if __name__ == "__main__":
932
  interfaz.launch()