File size: 13,200 Bytes
017603a
 
 
 
 
 
 
 
 
 
 
 
b526130
017603a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b526130
017603a
 
 
 
 
 
aab4dcb
 
 
 
 
b526130
aab4dcb
 
017603a
 
 
 
 
aab4dcb
017603a
 
 
 
 
 
 
 
 
 
 
 
b526130
 
 
 
017603a
b526130
 
017603a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b526130
 
017603a
aab4dcb
 
017603a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b526130
017603a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b526130
 
017603a
 
b526130
 
017603a
 
b526130
 
017603a
 
b526130
 
017603a
 
b526130
 
017603a
9d86063
b526130
 
9d86063
 
b526130
 
9d86063
017603a
 
b526130
 
017603a
 
b526130
 
017603a
 
b526130
 
017603a
 
b526130
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
017603a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
aab4dcb
 
 
 
 
 
 
017603a
 
 
 
aab4dcb
017603a
 
 
 
e55a579
017603a
 
 
 
 
 
 
 
 
 
 
 
 
 
aab4dcb
017603a
 
 
 
 
 
e55a579
 
4f904dd
017603a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
aab4dcb
 
 
 
 
 
 
b526130
017603a
 
b526130
017603a
 
 
 
 
b526130
017603a
 
 
 
 
b526130
aab4dcb
 
 
b526130
017603a
aab4dcb
017603a
786e5b8
4643997
 
 
786e5b8
 
 
 
 
 
 
 
 
 
 
017603a
 
e55a579
017603a
 
 
aab4dcb
017603a
 
 
 
 
aab4dcb
017603a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4f904dd
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
import gradio as gr
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from scipy import stats
from datetime import datetime
import docx
from docx.shared import Inches, Pt
from docx.enum.text import WD_PARAGRAPH_ALIGNMENT
import os

def generar_tabla(n_filas, concentracion_inicial, unidad_medida):
    valores_base = [1.00, 0.80, 0.60, 0.40, 0.20, 0.10, 0.05]

    if n_filas <= 7:
        solucion_inoculo = valores_base[:n_filas]
        agua = [round(1 - x, 2) for x in solucion_inoculo]
    else:
        solucion_inoculo = valores_base.copy()
        ultimo_valor = valores_base[-1]
        for _ in range(n_filas - 7):
            nuevo_valor = round(ultimo_valor / 2, 3)
            solucion_inoculo.append(nuevo_valor)
            ultimo_valor = nuevo_valor
        agua = [round(1 - x, 3) for x in solucion_inoculo]

    data = {
        f"Soluci贸n de in贸culo ({concentracion_inicial} {unidad_medida})": solucion_inoculo,
        "H2O": agua
    }
    df = pd.DataFrame(data)

    nombre_columna = f"Soluci贸n de in贸culo ({concentracion_inicial} {unidad_medida})"
    df["Factor de Diluci贸n"] = df[nombre_columna].apply(lambda x: round(1 / x, 2))
    df[f"Concentraci贸n Predicha ({unidad_medida})"] = df["Factor de Diluci贸n"].apply(
        lambda x: round(concentracion_inicial / x, 0)
    )

    df[f"Concentraci贸n Real ({unidad_medida})"] = None

    return df

def ajustar_decimales_evento(df, decimales):
    df = df.copy()
    # Identificar la columna de Concentraci贸n Predicha
    col_predicha = [col for col in df.columns if 'Concentraci贸n Predicha' in col][0]
    # Redondear la columna al n煤mero de decimales especificado
    df[col_predicha] = df[col_predicha].astype(float).round(decimales)
    return df

def generar_datos_sinteticos(df, desviacion_std):
    col_predicha = [col for col in df.columns if 'Predicha' in col][0]
    col_real = [col for col in df.columns if 'Real' in col][0]

    # Generar datos sint茅ticos
    valores_predichos = df[col_predicha].astype(float).values
    datos_sinteticos = valores_predichos + np.random.normal(0, desviacion_std, size=len(valores_predichos))
    datos_sinteticos = np.maximum(0, datos_sinteticos)  # Asegurar que no haya valores negativos
    datos_sinteticos = np.round(datos_sinteticos, 2)

    df[col_real] = datos_sinteticos

    return df

def generar_graficos(df_valid):
    col_predicha = [col for col in df_valid.columns if 'Predicha' in col][0]
    col_real = [col for col in df_valid.columns if 'Real' in col][0]

    # Convertir a num茅rico
    df_valid[col_predicha] = df_valid[col_predicha].astype(float)
    df_valid[col_real] = df_valid[col_real].astype(float)

    # Calcular regresi贸n lineal
    slope, intercept, r_value, p_value, std_err = stats.linregress(df_valid[col_predicha], df_valid[col_real])
    df_valid['Ajuste Lineal'] = intercept + slope * df_valid[col_predicha]

    # Configurar estilos
    sns.set(style="whitegrid")
    plt.rcParams.update({'figure.autolayout': True})

    fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 6))

    # Gr谩fico de dispersi贸n con l铆nea de regresi贸n
    sns.scatterplot(
        data=df_valid,
        x=col_predicha,
        y=col_real,
        ax=ax1,
        color='blue',
        s=100,
        label='Datos Reales',
        marker='o'
    )

    # L铆nea de ajuste
    sns.lineplot(
        x=df_valid[col_predicha],
        y=df_valid['Ajuste Lineal'],
        ax=ax1,
        color='green',
        label='Ajuste Lineal',
        linewidth=2
    )

    # L铆nea ideal
    min_predicha = df_valid[col_predicha].min()
    max_predicha = df_valid[col_predicha].max()
    ax1.plot(
        [min_predicha, max_predicha],
        [min_predicha, max_predicha],
        color='red',
        linestyle='--',
        label='Ideal'
    )

    ax1.set_title('Correlaci贸n entre Concentraci贸n Predicha y Real', fontsize=14)
    ax1.set_xlabel('Concentraci贸n Predicha', fontsize=12)
    ax1.set_ylabel('Concentraci贸n Real', fontsize=12)

    # A帽adir ecuaci贸n y R虏 en el gr谩fico
    ax1.annotate(
        f'y = {intercept:.2f} + {slope:.2f}x\n$R^2$ = {r_value**2:.4f}',
        xy=(0.05, 0.95),
        xycoords='axes fraction',
        fontsize=12,
        backgroundcolor='white',
        verticalalignment='top'
    )

    # Posicionar la leyenda
    ax1.legend(loc='lower right', fontsize=10)

    # Gr谩fico de residuos
    residuos = df_valid[col_real] - df_valid['Ajuste Lineal']
    sns.scatterplot(
        data=df_valid,
        x=col_predicha,
        y=residuos,
        ax=ax2,
        color='purple',
        s=100,
        marker='D',
        label='Residuos'
    )

    ax2.axhline(y=0, color='black', linestyle='--', linewidth=1)
    ax2.set_title('Gr谩fico de Residuos', fontsize=14)
    ax2.set_xlabel('Concentraci贸n Predicha', fontsize=12)
    ax2.set_ylabel('Residuo', fontsize=12)
    ax2.legend(loc='upper right', fontsize=10)

    plt.tight_layout()
    plt.savefig('grafico.png')  # Guardar el gr谩fico para incluirlo en el informe
    return fig

def evaluar_calidad_calibracion(df_valid, r_squared, rmse, cv_percent):
    # Funci贸n de evaluaci贸n (sin cambios)
    # ...

def generar_informe_completo(df_valid):
    # Generar el informe completo (sin cambios)
    # ...

def actualizar_analisis(df):
    # Actualizar el an谩lisis (sin cambios)
    # ...

def exportar_informe_word(df_valid, informe_md):
    # Exportar informe a Word (sin cambios)
    # ...

def exportar_informe_latex(df_valid, informe_md):
    # Exportar informe a LaTeX (sin cambios)
    # ...

def exportar_word(df, informe_md):
    # Funci贸n para exportar a Word (sin cambios)
    # ...

def exportar_latex(df, informe_md):
    # Funci贸n para exportar a LaTeX (sin cambios)
    # ...

# Funciones de ejemplo
def cargar_ejemplo_ufc():
    # Cargar ejemplo UFC (sin cambios)
    # ...

def cargar_ejemplo_od():
    # Cargar ejemplo OD (sin cambios)
    # ...

def limpiar_datos():
    # Limpiar datos (sin cambios)
    # ...

def generar_datos_sinteticos_evento(df):
    # Generar datos sint茅ticos (sin cambios)
    # ...

def actualizar_tabla_evento(df, n_filas, concentracion, unidad):
    # Actualizar tabla sin borrar "Concentraci贸n Real"
    df_new = generar_tabla(n_filas, concentracion, unidad)

    # Mapear columnas
    col_predicha_new = [col for col in df_new.columns if 'Concentraci贸n Predicha' in col][0]
    col_predicha_old = [col for col in df.columns if 'Concentraci贸n Predicha' in col][0]
    col_real_new = [col for col in df_new.columns if 'Concentraci贸n Real' in col][0]
    col_real_old = [col for col in df.columns if 'Concentraci贸n Real' in col][0]

    # Reemplazar valores existentes en "Concentraci贸n Real"
    df_new[col_real_new] = None
    for idx in df_new.index:
        if idx in df.index:
            df_new.at[idx, col_real_new] = df.at[idx, col_real_old]

    return df_new

# Interfaz Gradio
with gr.Blocks(theme=gr.themes.Soft()) as interfaz:
    gr.Markdown("""
    # 馃搳 Sistema Avanzado de Calibraci贸n con An谩lisis Estad铆stico
    Configure los par谩metros, edite los valores en la tabla y luego presione "Calcular" para obtener el an谩lisis.
    """)

    with gr.Tab("馃摑 Datos de Calibraci贸n"):
        with gr.Row():
            concentracion_input = gr.Number(
                value=2000000,
                label="Concentraci贸n Inicial",
                precision=0
            )
            unidad_input = gr.Textbox(
                value="UFC",
                label="Unidad de Medida",
                placeholder="UFC, OD, etc..."
            )
            filas_slider = gr.Slider(
                minimum=1,
                maximum=20,
                value=7,
                step=1,
                label="N煤mero de filas"
            )
            decimales_slider = gr.Slider(
                minimum=0,
                maximum=5,
                value=0,
                step=1,
                label="N煤mero de Decimales"
            )

        with gr.Row():
            calcular_btn = gr.Button("馃攧 Calcular", variant="primary")
            limpiar_btn = gr.Button("馃棏 Limpiar Datos", variant="secondary")
            ajustar_decimales_btn = gr.Button("馃洜 Ajustar Decimales", variant="secondary")

        with gr.Row():
            ejemplo_ufc_btn = gr.Button("馃搵 Cargar Ejemplo UFC", variant="secondary")
            ejemplo_od_btn = gr.Button("馃搵 Cargar Ejemplo OD", variant="secondary")
            sinteticos_btn = gr.Button("馃И Generar Datos Sint茅ticos", variant="secondary")

        tabla_output = gr.DataFrame(
            row_count=(1, "dynamic"),
            col_count=(5, "fixed"),
            wrap=True,
            label="Tabla de Datos",
            interactive=True,
            datatype=["number", "number", "number", "number", "number"],
            type="pandas",
        )

    with gr.Tab("馃搳 An谩lisis y Reporte"):
        estado_output = gr.Textbox(label="Estado", interactive=False)
        graficos_output = gr.Plot(label="Gr谩ficos de An谩lisis")
        informe_output = gr.Markdown(elem_id="informe_output")

        with gr.Row():
            copiar_btn = gr.Button("馃搵 Copiar Informe", variant="secondary")
            exportar_word_btn = gr.Button("馃捑 Exportar Informe Word", variant="primary")
            exportar_latex_btn = gr.Button("馃捑 Exportar Informe LaTeX", variant="primary")

            exportar_word_file = gr.File(label="Informe en Word")
            exportar_latex_file = gr.File(label="Informe en LaTeX")

    # Eventos
    input_components = [tabla_output]
    output_components = [estado_output, graficos_output, informe_output]

    # Evento al presionar el bot贸n Calcular
    calcular_btn.click(
        fn=actualizar_analisis,
        inputs=tabla_output,
        outputs=output_components
    )

    # Evento para limpiar datos
    limpiar_btn.click(
        fn=limpiar_datos,
        inputs=[],
        outputs=[concentracion_input, unidad_input, filas_slider, tabla_output, estado_output, graficos_output, informe_output]
    )

    # Eventos de los botones de ejemplo
    ejemplo_ufc_btn.click(
        fn=cargar_ejemplo_ufc,
        outputs=[concentracion_input, unidad_input, filas_slider, tabla_output]
    )

    ejemplo_od_btn.click(
        fn=cargar_ejemplo_od,
        outputs=[concentracion_input, unidad_input, filas_slider, tabla_output]
    )

    # Evento para generar datos sint茅ticos
    sinteticos_btn.click(
        fn=generar_datos_sinteticos_evento,
        inputs=tabla_output,
        outputs=tabla_output
    )

    # Evento al presionar el bot贸n Ajustar Decimales
    ajustar_decimales_btn.click(
        fn=ajustar_decimales_evento,
        inputs=[tabla_output, decimales_slider],
        outputs=tabla_output
    )

    # Actualizar tabla al cambiar los par谩metros (sin borrar "Concentraci贸n Real")
    concentracion_input.change(
        fn=actualizar_tabla_evento,
        inputs=[tabla_output, filas_slider, concentracion_input, unidad_input],
        outputs=tabla_output
    )

    unidad_input.change(
        fn=actualizar_tabla_evento,
        inputs=[tabla_output, filas_slider, concentracion_input, unidad_input],
        outputs=tabla_output
    )

    filas_slider.change(
        fn=actualizar_tabla_evento,
        inputs=[tabla_output, filas_slider, concentracion_input, unidad_input],
        outputs=tabla_output
    )

    # No agregamos un evento para decimales_slider.change, para evitar borrar la columna "Concentraci贸n Real"

    # Evento de copiar informe utilizando JavaScript
    copiar_btn.click(
        None,
        [],
        [],
        js="""
        function() {
            const informeElement = document.querySelector('#informe_output');
            const range = document.createRange();
            range.selectNode(informeElement);
            window.getSelection().removeAllRanges();
            window.getSelection().addRange(range);
            document.execCommand('copy');
            window.getSelection().removeAllRanges();
            alert('Informe copiado al portapapeles');
        }
        """
    )

    # Eventos de exportar informes
    exportar_word_btn.click(
        fn=exportar_word,
        inputs=[tabla_output, informe_output],
        outputs=exportar_word_file
    )

    exportar_latex_btn.click(
        fn=exportar_latex,
        inputs=[tabla_output, informe_output],
        outputs=exportar_latex_file
    )

    # Inicializar la interfaz con el ejemplo base
    def iniciar_con_ejemplo():
        df = generar_tabla(7, 2000000, "UFC")
        valores_reales = [2000000, 1600000, 1200000, 800000, 400000, 200000, 100000]
        df[f"Concentraci贸n Real (UFC)"] = valores_reales
        estado, fig, informe = actualizar_analisis(df)
        return (
            2000000,
            "UFC",
            7,
            df,
            estado,
            fig,
            informe
        )

    interfaz.load(
        fn=iniciar_con_ejemplo,
        outputs=[concentracion_input, unidad_input, filas_slider, tabla_output, estado_output, graficos_output, informe_output]
    )

# Lanzar la interfaz
if __name__ == "__main__":
    interfaz.launch()