hedtorresca commited on
Commit
32bd549
·
verified ·
1 Parent(s): 6e83594

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +129 -132
app.py CHANGED
@@ -1,145 +1,142 @@
1
  import gradio as gr
2
  import pandas as pd
3
  import numpy as np
4
- import matplotlib.pyplot as plt
5
- import seaborn as sns
6
  import geopandas as gpd
 
 
7
  from scipy.stats import chi2_contingency
8
- from sklearn.neighbors import KernelDensity
 
 
 
9
  import folium
10
  from folium.plugins import HeatMap
11
 
12
- # ===========================
13
- # 1. CARGA Y PREPROCESAMIENTO
14
- # ===========================
15
- df = pd.read_csv("VasculitisAsociadasA-BDD10jul24_DATA_2025-03-19_1033.csv")
16
-
17
- # Derivar variables categóricas útiles
 
 
 
 
 
 
 
 
 
18
  categorias = {
19
- 'genero_cat': df['genero'].map({0: 'Masculino', 1: 'Femenino'}),
20
- 'regimen_cat': df['regimen'].map({1: 'Contributivo', 2: 'Subsidiado'}),
21
- 'estrato_cat': df['estrato'].map({0: 'Bajo', 1: 'Bajo', 2: 'Bajo', 3: 'Medio', 4: 'Medio', 5: 'Alto', 6: 'Alto'})
22
  }
23
- df = df.assign(**categorias)
24
-
25
- # ANCA positivo
26
- df['anca_cat'] = df['ancas'].map({0: 'Negativo', 1: 'Positivo'}).fillna('No definido')
27
- df['mpo_cat'] = df['mpo'].map({0: 'Negativo', 1: 'Positivo'}).fillna('No definido')
28
- df['pr3_cat'] = df['pr3'].map({0: 'Negativo', 1: 'Positivo'}).fillna('No definido')
29
-
30
- # Compromiso renal por biopsia
31
- biopsia_cols = [col for col in df.columns if col.startswith('biopsia___')]
32
- df['biopsia_positiva'] = df[biopsia_cols].sum(axis=1).apply(lambda x: 'Sí' if x > 0 else 'No')
33
-
34
- # Caso confirmado: ANCA+ y biopsia positiva
35
- df['anca_y_renal'] = np.where((df['ancas'] == 1) & (df['biopsia_positiva'] == 'Sí'), 'Sí', 'No')
36
-
37
- # ===========================
38
- # 2. FUNCIONES DE GRAFICACIÓN Y MAPEO
39
- # ===========================
40
-
41
- def generar_univariado():
42
- rutas = []
43
- variables_uni = ['edad', 'genero_cat', 'regimen_cat', 'estrato_cat', 'anca_cat',
44
- 'mpo_cat', 'pr3_cat', 'biopsia_positiva', 'anca_y_renal', 'sindrome',
45
- 'diabetes', 'falla_cardiaca', 'epoc', 'hta']
46
- for col in variables_uni:
47
- plt.figure(figsize=(6, 4))
48
- if df[col].dtype == 'object':
49
- sns.countplot(data=df, x=col, order=df[col].value_counts().index)
50
- else:
51
- sns.histplot(df[col], kde=True)
52
- plt.title(f"Distribución de {col}")
53
- plt.xticks(rotation=30)
54
- plt.tight_layout()
55
- path = f"uni_{col}.png"
56
- plt.savefig(path)
57
- rutas.append(path)
58
- plt.close()
59
- return rutas
60
-
61
- def generar_bivariado():
62
- relaciones_bi = [
63
- ('anca_cat', 'biopsia_positiva'), ('anca_cat', 'mpo_cat'),
64
- ('anca_cat', 'pr3_cat'), ('estrato_cat', 'biopsia_positiva'),
65
- ('genero_cat', 'anca_cat'), ('regimen_cat', 'anca_cat'),
66
- ('sindrome', 'biopsia_positiva')
67
- ]
68
- rutas = []
69
- for x, y in relaciones_bi:
70
- plt.figure(figsize=(6, 4))
71
- sns.countplot(data=df, x=x, hue=y)
72
- plt.title(f"{x} vs {y}")
73
- plt.xticks(rotation=30)
74
- plt.tight_layout()
75
- path = f"bi_{x}_{y}.png"
76
- plt.savefig(path)
77
- rutas.append(path)
78
- plt.close()
79
- return rutas
80
-
81
- def generar_chi2():
82
- relaciones_bi = [
83
- ('anca_cat', 'biopsia_positiva'), ('anca_cat', 'mpo_cat'),
84
- ('anca_cat', 'pr3_cat'), ('estrato_cat', 'biopsia_positiva'),
85
- ('genero_cat', 'anca_cat'), ('regimen_cat', 'anca_cat'),
86
- ('sindrome', 'biopsia_positiva')
87
- ]
88
- resultados = []
89
- for var1, var2 in relaciones_bi:
90
- tabla = pd.crosstab(df[var1], df[var2])
91
- chi, p, dof, _ = chi2_contingency(tabla)
92
- resultados.append((f"{var1} vs {var2}", tabla, chi, p, dof))
93
- return resultados
94
-
95
- def generar_mapa_coropletico():
96
- gdf = gpd.read_file("https://datosabiertos.bogota.gov.co/dataset/856cb657-8ca3-4ee8-857f-37211173b1f8/resource/497b8756-0927-4aee-8da9-ca4e32ca3a8a/download/loca.json")
97
- df_loc = df.copy()
98
- df_loc['localidad'] = df_loc['localidad'].astype(str)
99
- counts = df_loc['localidad'].value_counts().reset_index()
100
- counts.columns = ['localidad', 'casos']
101
- gdf['localidad'] = gdf['NOMBRE'].str.upper()
102
- merged = gdf.merge(counts, how='left', left_on='localidad', right_on='localidad').fillna(0)
103
- m = merged.explore(column='casos', cmap='OrRd', legend=True)
104
- m.save("coropletico.html")
105
- with open("coropletico.html", "r", encoding="utf-8") as f:
106
- return f.read()
107
-
108
- def generar_mapa_kernel():
109
- if 'latitud' in df.columns and 'longitud' in df.columns:
110
- puntos = df[['latitud', 'longitud']].dropna()
111
- m = folium.Map(location=[4.65, -74.1], zoom_start=11)
112
- HeatMap(puntos.values.tolist(), radius=12).add_to(m)
113
- m.save("heatmap.html")
114
- with open("heatmap.html", "r", encoding="utf-8") as f:
115
- return f.read()
116
- else:
117
- return "No hay coordenadas geográficas disponibles."
118
-
119
- # ===========================
120
- # 3. INTERFAZ AUTOMÁTICA PROFESIONAL
121
- # ===========================
122
  with gr.Blocks() as demo:
123
- gr.Markdown("# Tablero Clínico Profesional - Vasculitis ANCA")
124
-
125
- with gr.Tab("Gráficos Univariados"):
126
- for path in generar_univariado():
127
- gr.Image(value=path)
128
-
129
- with gr.Tab("Gráficos Bivariados"):
130
- for path in generar_bivariado():
131
- gr.Image(value=path)
132
-
133
- with gr.Tab("Tablas Chi-cuadrado"):
134
- for nombre, tabla, chi, p, dof in generar_chi2():
135
- gr.Markdown(f"### {nombre}")
136
- gr.Markdown(f"**Chi²** = {chi:.2f} | **p-valor** = {p:.4f} | **gl** = {dof}")
137
- gr.DataFrame(value=tabla.reset_index())
138
-
139
- with gr.Tab("Mapa Coroplético por Localidad"):
140
- gr.HTML(value=generar_mapa_coropletico())
141
-
142
- with gr.Tab("Mapa de Calor (Kernel Density)"):
143
- gr.HTML(value=generar_mapa_kernel())
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
144
 
145
  demo.launch()
 
1
  import gradio as gr
2
  import pandas as pd
3
  import numpy as np
 
 
4
  import geopandas as gpd
5
+ import plotly.express as px
6
+ import plotly.graph_objects as go
7
  from scipy.stats import chi2_contingency
8
+ import matplotlib.pyplot as plt
9
+ import seaborn as sns
10
+ import io
11
+ import base64
12
  import folium
13
  from folium.plugins import HeatMap
14
 
15
+ # =========================
16
+ # 1. CARGA DE DATOS
17
+ # =========================
18
+ data = pd.read_csv("VasculitisAsociadasA-BDD10jul24_DATA_2025-03-19_1033.csv")
19
+ geo_localidades = gpd.read_file("loca.json")
20
+ calidad_aire = gpd.read_file("pm25_prom_anual_2023.geojson")
21
+ ozono = gpd.read_file("ozono_prom_anual_2022.geojson")
22
+ temperatura = gpd.read_file("temp_anualprom_2023.geojson")
23
+ precipitacion = gpd.read_file("precip_anualacum_2023.geojson")
24
+ viento = gpd.read_file("vel_viento_0_23h_anual_2023.geojson")
25
+ estaciones = gpd.read_file("estacion_calidad_aire.geojson")
26
+
27
+ # =========================
28
+ # 2. PROCESAMIENTO
29
+ # =========================
30
  categorias = {
31
+ 'genero_cat': data['genero'].map({0: 'Masculino', 1: 'Femenino'}),
32
+ 'regimen_cat': data['regimen'].map({1: 'Contributivo', 2: 'Subsidiado'}),
33
+ 'estrato_cat': data['estrato'].map({0: 'Bajo', 1: 'Bajo', 2: 'Bajo', 3: 'Medio', 4: 'Medio', 5: 'Alto', 6: 'Alto'})
34
  }
35
+ data = data.assign(**categorias)
36
+
37
+ data['anca_cat'] = data['ancas'].map({0: 'Negativo', 1: 'Positivo'}).fillna('No definido')
38
+ data['mpo_cat'] = data['mpo'].map({0: 'Negativo', 1: 'Positivo'}).fillna('No definido')
39
+ data['pr3_cat'] = data['pr3'].map({0: 'Negativo', 1: 'Positivo'}).fillna('No definido')
40
+
41
+ tipo_biopsia = [col for col in data.columns if col.startswith('biopsia___')]
42
+ data['biopsia_positiva'] = data[tipo_biopsia].sum(axis=1).apply(lambda x: 'Sí' if x > 0 else 'No')
43
+ data['anca_y_renal'] = np.where((data['ancas'] == 1) & (data['biopsia_positiva'] == ''), 'Sí', 'No')
44
+
45
+ # =========================
46
+ # 3. FUNCIONES DE FILTRADO
47
+ # =========================
48
+ def aplicar_filtros(df, genero, edad, localidad, anca_tipo, antecedentes):
49
+ if genero != "Todos":
50
+ df = df[df['genero_cat'] == genero]
51
+ df = df[(df['edad'] >= edad[0]) & (df['edad'] <= edad[1])]
52
+ if localidad:
53
+ df = df[df['localidad'].isin(localidad)]
54
+ if anca_tipo != "Todos":
55
+ df = df[df['anca_cat'] == anca_tipo]
56
+ for ant in antecedentes:
57
+ if ant in df.columns:
58
+ df = df[df[ant] == 1]
59
+ return df
60
+
61
+ # =========================
62
+ # 4. NUEVOS COMPONENTES UI
63
+ # =========================
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
64
  with gr.Blocks() as demo:
65
+ gr.Markdown("# Tablero de Análisis de Vasculitis ANCA en Bogotá")
66
+
67
+ with gr.Accordion("Filtros de Subgrupo", open=False):
68
+ genero = gr.Radio(["Todos", "Masculino", "Femenino"], label="Género")
69
+ edad = gr.Slider(0, 100, step=1, value=(0, 100), label="Edad")
70
+ localidades_opciones = sorted(data['localidad'].dropna().unique().tolist())
71
+ localidad = gr.Dropdown(localidades_opciones, label="Localidad", multiselect=True)
72
+ anca = gr.Radio(["Todos", "Positivo", "Negativo"], label="ANCA")
73
+ antecedentes = gr.CheckboxGroup(choices=['diabetes', 'falla_cardiaca', 'epoc', 'hta'], label="Antecedentes Clínicos")
74
+ btn_aplicar = gr.Button("Aplicar Filtros")
75
+
76
+ resultados_univariados = gr.Gallery(label="Distribuciones Univariadas")
77
+ resultados_bivariados = gr.Gallery(label="Relaciones Bivariadas")
78
+ resultados_chi = gr.DataFrame()
79
+ html_mapa = gr.HTML()
80
+ mapa_plot = gr.Plot()
81
+
82
+ def actualizar_tablero(genero, edad, localidad, anca, antecedentes):
83
+ df_filtrado = aplicar_filtros(data, genero, edad, localidad, anca, antecedentes)
84
+ if df_filtrado.empty:
85
+ return [[], [], pd.DataFrame(), "<h4>No hay datos para los filtros seleccionados</h4>", go.Figure()]
86
+
87
+ imgs_uni = []
88
+ for col in ['edad', 'genero_cat', 'regimen_cat', 'estrato_cat', 'anca_cat', 'mpo_cat', 'pr3_cat', 'biopsia_positiva', 'anca_y_renal']:
89
+ plt.figure(figsize=(5, 4))
90
+ if df_filtrado[col].dtype == 'object':
91
+ sns.countplot(data=df_filtrado, x=col, order=df_filtrado[col].value_counts().index)
92
+ else:
93
+ sns.histplot(df_filtrado[col], kde=True)
94
+ plt.xticks(rotation=30)
95
+ plt.tight_layout()
96
+ path = f"plot_uni_{col}.png"
97
+ plt.savefig(path)
98
+ imgs_uni.append(path)
99
+ plt.close()
100
+
101
+ imgs_bi = []
102
+ for x, y in [('anca_cat', 'biopsia_positiva'), ('estrato_cat', 'anca_cat')]:
103
+ plt.figure(figsize=(6, 4))
104
+ sns.countplot(data=df_filtrado, x=x, hue=y)
105
+ plt.tight_layout()
106
+ path = f"plot_bi_{x}_{y}.png"
107
+ plt.savefig(path)
108
+ imgs_bi.append(path)
109
+ plt.close()
110
+
111
+ tabla = pd.crosstab(df_filtrado['genero_cat'], df_filtrado['anca_cat'])
112
+ chi2, p, dof, _ = chi2_contingency(tabla)
113
+
114
+ resumen = df_filtrado.groupby("localidad")['ancas'].count().reset_index(name='casos')
115
+ geo_localidades['localidad'] = geo_localidades['NOMBRE'].str.upper()
116
+ merged = geo_localidades.merge(resumen, left_on='localidad', right_on='localidad', how='left').fillna(0)
117
+ m = folium.Map(location=[4.65, -74.1], zoom_start=11)
118
+ folium.Choropleth(
119
+ geo_data=merged,
120
+ data=merged,
121
+ columns=['localidad', 'casos'],
122
+ key_on='feature.properties.localidad',
123
+ fill_color='YlOrRd',
124
+ fill_opacity=0.7,
125
+ line_opacity=0.2,
126
+ legend_name='Casos de Vasculitis'
127
+ ).add_to(m)
128
+ path_map = "folium_map.html"
129
+ m.save(path_map)
130
+ with open(path_map, 'r', encoding='utf-8') as f:
131
+ html_out = f.read()
132
+
133
+ fig = px.density_mapbox(df_filtrado.dropna(subset=['latitud', 'longitud']), lat='latitud', lon='longitud',
134
+ z=[1]*len(df_filtrado), radius=10,
135
+ center=dict(lat=4.65, lon=-74.1), zoom=10, mapbox_style="carto-positron")
136
+
137
+ return [imgs_uni, imgs_bi, tabla.reset_index(), html_out, fig]
138
+
139
+ btn_aplicar.click(actualizar_tablero, inputs=[genero, edad, localidad, anca, antecedentes],
140
+ outputs=[resultados_univariados, resultados_bivariados, resultados_chi, html_mapa, mapa_plot])
141
 
142
  demo.launch()