hedtorresca commited on
Commit
60f7cb1
·
verified ·
1 Parent(s): 22c1195

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +75 -151
app.py CHANGED
@@ -9,21 +9,16 @@ import folium
9
  from folium.plugins import HeatMap
10
  from shapely.geometry import Point
11
  from sklearn.cluster import DBSCAN
12
- import os
13
  import warnings
14
-
15
  warnings.filterwarnings("ignore")
16
 
17
- # =========================
18
- # 1. CARGA DE DATOS
19
- # =========================
20
  data = pd.read_csv("VasculitisAsociadasA-BDD10jul24_DATA_2025-03-19_1033.csv")
21
  data.columns = data.columns.str.strip().str.lower()
22
  data = data.dropna(subset=['latitud', 'longitud'])
23
  data['geometry'] = data.apply(lambda row: Point(row['longitud'], row['latitud']), axis=1)
24
  data = gpd.GeoDataFrame(data, geometry='geometry', crs='EPSG:4326')
25
 
26
- # Localidades
27
  geo_localidades = gpd.read_file("loca.json")
28
  geo_localidades.columns = geo_localidades.columns.str.lower()
29
  geo_localidades = geo_localidades.rename(columns={"locnombre": "localidad"})
@@ -31,7 +26,7 @@ geo_localidades['localidad'] = geo_localidades['localidad'].str.upper()
31
  data = gpd.sjoin(data.to_crs(geo_localidades.crs), geo_localidades[['localidad', 'geometry']], how='left', predicate='within')
32
  data.drop(columns='index_right', inplace=True)
33
 
34
- # Cargar capas ambientales
35
  def cargar_geojson(filepath):
36
  gdf = gpd.read_file(filepath).to_crs("EPSG:4326")
37
  for col in gdf.columns:
@@ -48,9 +43,7 @@ capas_ambientales = {
48
  "Estaciones Aire": cargar_geojson("estacion_calidad_aire.geojson")
49
  }
50
 
51
- # =========================
52
- # 2. VARIABLES DERIVADAS
53
- # =========================
54
  data = data.assign(
55
  genero_cat=data['genero'].map({0: 'Masculino', 1: 'Femenino'}),
56
  regimen_cat=data['regimen'].map({1: 'Contributivo', 2: 'Subsidiado'}),
@@ -63,9 +56,7 @@ biopsia_cols = [col for col in data.columns if col.startswith('biopsia___')]
63
  data['biopsia_positiva'] = data[biopsia_cols].sum(axis=1).apply(lambda x: 'Sí' if x > 0 else 'No')
64
  data['anca_y_renal'] = np.where((data['ancas'] == 1) & (data['biopsia_positiva'] == 'Sí'), 'Sí', 'No')
65
 
66
- # =========================
67
- # 3. FUNCIONES AUXILIARES
68
- # =========================
69
  def aplicar_filtros(df, genero, edad_rango, localidades, compromiso_renal, antecedentes):
70
  df_filtrado = df.copy()
71
  if genero != "Todos":
@@ -80,165 +71,98 @@ def aplicar_filtros(df, genero, edad_rango, localidades, compromiso_renal, antec
80
  df_filtrado = df_filtrado[df_filtrado[ant] == 1]
81
  return df_filtrado
82
 
83
- def generar_mapa_calor(df, capas):
84
- m = folium.Map(location=[4.65, -74.1], zoom_start=11)
85
- heat_data = df[['latitud', 'longitud']].dropna().values.tolist()
86
- HeatMap(heat_data, radius=12).add_to(m)
87
- for _, row in df.iterrows():
88
- folium.Marker(
89
- location=(row['latitud'], row['longitud']),
90
- popup=f"{row['genero_cat']}<br>Edad: {row['edad']}<br>Estrato: {row['estrato_cat']}<br>Creatinina: {row.get('creatinina', '')}"
91
- ).add_to(m)
92
- for capa in capas:
93
- if capa in capas_ambientales:
94
- gdf = capas_ambientales[capa]
95
- folium.GeoJson(gdf, name=capa, tooltip=folium.GeoJsonTooltip(fields=gdf.columns[:2].tolist())).add_to(m)
96
- folium.LayerControl().add_to(m)
97
- return m._repr_html_()
98
-
99
- def generar_mapa_coropletico(df, capas):
100
- df_grouped = df.groupby('localidad').size().reset_index(name='casos')
101
- geo_local_copy = geo_localidades.merge(df_grouped, on='localidad', how='left').fillna({'casos': 0})
102
- m = folium.Map(location=[4.65, -74.1], zoom_start=11)
103
- folium.Choropleth(
104
- geo_data=geo_local_copy,
105
- name='Casos por localidad',
106
- data=geo_local_copy,
107
- columns=['localidad', 'casos'],
108
- key_on='feature.properties.localidad',
109
- fill_color='YlOrRd',
110
- fill_opacity=0.6,
111
- line_opacity=0.4,
112
- legend_name='Casos'
113
- ).add_to(m)
114
- for _, row in geo_local_copy.iterrows():
115
- if row['casos'] > 0:
116
- folium.Marker(
117
- location=row['geometry'].centroid.coords[0][::-1],
118
- icon=folium.DivIcon(html=f"<div style='font-size:12px;color:black;'>{row['localidad']}<br>{int(row['casos'])} casos</div>")
119
- ).add_to(m)
120
- for capa in capas:
121
- if capa in capas_ambientales:
122
- gdf = capas_ambientales[capa]
123
- folium.GeoJson(gdf, name=capa, tooltip=folium.GeoJsonTooltip(fields=gdf.columns[:2].tolist())).add_to(m)
124
- folium.LayerControl().add_to(m)
125
- return m._repr_html_()
126
-
127
- def generar_clusters(df_filtrado):
128
- coords = df_filtrado[['latitud', 'longitud']].dropna().values
129
- if len(coords) < 2:
130
  return None
131
- coords_rad = np.radians(coords)
132
- db = DBSCAN(eps=0.01, min_samples=3, metric='haversine').fit(coords_rad)
133
- df_filtrado['cluster'] = db.labels_
134
- m = folium.Map(location=[4.65, -74.1], zoom_start=11)
135
- for _, row in df_filtrado.iterrows():
136
- color = 'red' if row['cluster'] != -1 else 'gray'
137
- folium.CircleMarker(location=(row['latitud'], row['longitud']), color=color, radius=4).add_to(m)
138
- return m._repr_html_()
139
-
140
- # =========================
141
- # 4. GRAFICOS UNIVARIADOS Y BIVARIADOS
142
- # =========================
143
- def obtener_columnas_validas(df):
144
- excluidas = ['id', 'documento', 'latitud', 'longitud', 'geometry']
145
- return [col for col in df.columns if col not in excluidas and df[col].nunique() > 1]
146
-
147
- columnas_validas = obtener_columnas_validas(data)
148
-
149
- def tipo_variable(col):
150
- if pd.api.types.is_numeric_dtype(data[col]):
151
- return 'numérica'
152
- else:
153
- return 'categórica'
154
-
155
- def graficar_univariado(col):
156
  plt.figure(figsize=(6, 4))
157
- if tipo_variable(col) == 'numérica':
158
- sns.histplot(data[col], kde=True)
159
  else:
160
- orden = data[col].value_counts().index
161
- sns.countplot(data=data, x=col, order=orden)
162
- plt.xticks(rotation=30)
163
- plt.title(f"Distribución de {col}")
164
- path = f"uni_{col}.png"
165
  plt.tight_layout()
166
  plt.savefig(path)
167
  plt.close()
168
  return path
169
 
170
- def graficar_bivariado(x, y):
 
 
 
 
171
  plt.figure(figsize=(6, 4))
172
- tipo_x = tipo_variable(x)
173
- tipo_y = tipo_variable(y)
174
- if tipo_x == 'numérica' and tipo_y == 'numérica':
175
- sns.scatterplot(data=data, x=x, y=y)
176
- elif tipo_x == 'categórica' and tipo_y == 'numérica':
177
- sns.boxplot(data=data, x=x, y=y)
178
- elif tipo_x == 'numérica' and tipo_y == 'categórica':
179
- sns.boxplot(data=data, x=y, y=x)
180
- elif tipo_x == 'categórica' and tipo_y == 'categórica':
181
- sns.countplot(data=data, x=x, hue=y)
182
- plt.xticks(rotation=30)
183
- plt.title(f"{x} vs {y}")
184
- path = f"bi_{x}_{y}.png"
185
  plt.tight_layout()
186
  plt.savefig(path)
187
  plt.close()
188
  return path
189
 
190
- # =========================
191
- # 5. INTERFAZ
192
- # =========================
193
  def lanzar_app():
194
  with gr.Blocks() as demo:
195
- gr.Markdown("## 🧭 Tablero Interactivo - Vasculitis ANCA con Compromiso Renal")
196
 
197
  with gr.Row():
198
  genero = gr.Dropdown(label="Género", choices=["Todos", "Masculino", "Femenino"], value="Todos")
199
- edad = gr.Slider(label="Edad", minimum=0, maximum=100, value=(20, 80), step=1)
200
- localidades = gr.Dropdown(label="Localidad", choices=sorted(data['localidad'].dropna().unique()), multiselect=True)
201
- compromiso_renal = gr.Dropdown(label="Compromiso Renal", choices=["Todos", "Sí", "No"], value="Todos")
202
- antecedentes = gr.CheckboxGroup(label="Antecedentes", choices=["diabetes", "hta", "epoc", "falla_cardiaca"])
203
- capas = gr.CheckboxGroup(label="Capas Ambientales", choices=list(capas_ambientales.keys()))
204
-
205
- with gr.Row():
206
- btn_kernel = gr.Button("Mapa de Calor")
207
- salida_kernel = gr.HTML()
208
- btn_kernel.click(fn=lambda *args: generar_mapa_calor(aplicar_filtros(data, *args[:5]), args[5]),
209
- inputs=[genero, edad, localidades, compromiso_renal, antecedentes, capas],
210
- outputs=salida_kernel)
211
-
212
- with gr.Row():
213
- btn_coropletico = gr.Button("Mapa Coroplético")
214
- salida_coropletico = gr.HTML()
215
- btn_coropletico.click(fn=lambda *args: generar_mapa_coropletico(aplicar_filtros(data, *args[:5]), args[5]),
216
- inputs=[genero, edad, localidades, compromiso_renal, antecedentes, capas],
217
- outputs=salida_coropletico)
218
-
219
  with gr.Row():
220
- btn_clust = gr.Button("Clúster DBSCAN")
221
- salida_clust = gr.HTML()
222
- btn_clust.click(fn=lambda *args: generar_clusters(aplicar_filtros(data, *args)),
223
- inputs=[genero, edad, localidades, compromiso_renal, antecedentes],
224
- outputs=salida_clust)
225
-
226
- gr.Markdown("### 📊 Gráficos Univariados y Bivariados")
227
-
228
- with gr.Row():
229
- variable_uni = gr.Dropdown(label="Variable Univariada", choices=columnas_validas)
230
- btn_uni = gr.Button("Mostrar Univariado")
231
- salida_uni = gr.Image()
232
- btn_uni.click(fn=graficar_univariado, inputs=variable_uni, outputs=salida_uni)
233
 
234
- with gr.Row():
235
- var_x = gr.Dropdown(label="Variable X", choices=columnas_validas)
236
- var_y = gr.Dropdown(label="Variable Y", choices=columnas_validas)
237
- btn_bi = gr.Button("Mostrar Bivariado")
238
- salida_bi = gr.Image()
239
- btn_bi.click(fn=graficar_bivariado, inputs=[var_x, var_y], outputs=salida_bi)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
240
 
241
  demo.launch()
242
 
243
- if __name__ == "__main__":
244
- lanzar_app()
 
9
  from folium.plugins import HeatMap
10
  from shapely.geometry import Point
11
  from sklearn.cluster import DBSCAN
 
12
  import warnings
 
13
  warnings.filterwarnings("ignore")
14
 
15
+ # Carga de datos
 
 
16
  data = pd.read_csv("VasculitisAsociadasA-BDD10jul24_DATA_2025-03-19_1033.csv")
17
  data.columns = data.columns.str.strip().str.lower()
18
  data = data.dropna(subset=['latitud', 'longitud'])
19
  data['geometry'] = data.apply(lambda row: Point(row['longitud'], row['latitud']), axis=1)
20
  data = gpd.GeoDataFrame(data, geometry='geometry', crs='EPSG:4326')
21
 
 
22
  geo_localidades = gpd.read_file("loca.json")
23
  geo_localidades.columns = geo_localidades.columns.str.lower()
24
  geo_localidades = geo_localidades.rename(columns={"locnombre": "localidad"})
 
26
  data = gpd.sjoin(data.to_crs(geo_localidades.crs), geo_localidades[['localidad', 'geometry']], how='left', predicate='within')
27
  data.drop(columns='index_right', inplace=True)
28
 
29
+ # Capas ambientales
30
  def cargar_geojson(filepath):
31
  gdf = gpd.read_file(filepath).to_crs("EPSG:4326")
32
  for col in gdf.columns:
 
43
  "Estaciones Aire": cargar_geojson("estacion_calidad_aire.geojson")
44
  }
45
 
46
+ # Variables derivadas
 
 
47
  data = data.assign(
48
  genero_cat=data['genero'].map({0: 'Masculino', 1: 'Femenino'}),
49
  regimen_cat=data['regimen'].map({1: 'Contributivo', 2: 'Subsidiado'}),
 
56
  data['biopsia_positiva'] = data[biopsia_cols].sum(axis=1).apply(lambda x: 'Sí' if x > 0 else 'No')
57
  data['anca_y_renal'] = np.where((data['ancas'] == 1) & (data['biopsia_positiva'] == 'Sí'), 'Sí', 'No')
58
 
59
+ # Filtros
 
 
60
  def aplicar_filtros(df, genero, edad_rango, localidades, compromiso_renal, antecedentes):
61
  df_filtrado = df.copy()
62
  if genero != "Todos":
 
71
  df_filtrado = df_filtrado[df_filtrado[ant] == 1]
72
  return df_filtrado
73
 
74
+ # Univariado
75
+ def generar_univariado(variable, genero, edad, localidades, compromiso_renal, antecedentes):
76
+ df_filtrado = aplicar_filtros(data, genero, edad, localidades, compromiso_renal, antecedentes)
77
+ if df_filtrado.empty:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
78
  return None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
  plt.figure(figsize=(6, 4))
80
+ if df_filtrado[variable].dtype == 'object':
81
+ sns.countplot(data=df_filtrado, x=variable)
82
  else:
83
+ sns.histplot(df_filtrado[variable], kde=True)
84
+ plt.title(f"Distribución de {variable}")
85
+ path = f"uni_{variable}.png"
 
 
86
  plt.tight_layout()
87
  plt.savefig(path)
88
  plt.close()
89
  return path
90
 
91
+ # Bivariado
92
+ def generar_bivariado(xvar, yvar, genero, edad, localidades, compromiso_renal, antecedentes):
93
+ df_filtrado = aplicar_filtros(data, genero, edad, localidades, compromiso_renal, antecedentes)
94
+ if df_filtrado.empty:
95
+ return None
96
  plt.figure(figsize=(6, 4))
97
+ if df_filtrado[xvar].dtype == 'object' or df_filtrado[yvar].dtype == 'object':
98
+ sns.countplot(data=df_filtrado, x=xvar, hue=yvar)
99
+ else:
100
+ sns.scatterplot(data=df_filtrado, x=xvar, y=yvar)
101
+ plt.title(f"{xvar} vs {yvar}")
102
+ path = f"bi_{xvar}_{yvar}.png"
 
 
 
 
 
 
 
103
  plt.tight_layout()
104
  plt.savefig(path)
105
  plt.close()
106
  return path
107
 
108
+ # Interfaz con pestañas
 
 
109
  def lanzar_app():
110
  with gr.Blocks() as demo:
111
+ gr.Markdown("## Tablero Interactivo: Vasculitis ANCA")
112
 
113
  with gr.Row():
114
  genero = gr.Dropdown(label="Género", choices=["Todos", "Masculino", "Femenino"], value="Todos")
115
+ edad = gr.Slider(label="Edad", minimum=0, maximum=100, value=(20, 80))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
116
  with gr.Row():
117
+ localidades = gr.Dropdown(label="Localidades", choices=sorted(data['localidad'].dropna().unique()), multiselect=True)
118
+ compromiso_renal = gr.Dropdown(label="Compromiso Renal", choices=["Todos", "Sí", "No"], value="Todos")
119
+ antecedentes = gr.CheckboxGroup(label="Antecedentes", choices=["diabetes", "hta", "epoc", "falla_cardiaca"])
 
 
 
 
 
 
 
 
 
 
120
 
121
+ with gr.Tab("Mapa Coroplético"):
122
+ capas = gr.CheckboxGroup(label="Capas Ambientales", choices=list(capas_ambientales.keys()))
123
+ btn_coro = gr.Button("Generar Mapa")
124
+ mapa_html = gr.HTML()
125
+
126
+ def mostrar_mapa(genero, edad, localidades, compromiso_renal, antecedentes, capas):
127
+ df_f = aplicar_filtros(data, genero, edad, localidades, compromiso_renal, antecedentes)
128
+ return generar_mapa_coropletico(df_f, capas)
129
+
130
+ btn_coro.click(mostrar_mapa, inputs=[genero, edad, localidades, compromiso_renal, antecedentes, capas], outputs=mapa_html)
131
+
132
+ with gr.Tab("Mapa de Calor"):
133
+ btn_heat = gr.Button("Generar Mapa de Calor")
134
+ mapa_heat = gr.HTML()
135
+
136
+ def mostrar_heat(genero, edad, localidades, compromiso_renal, antecedentes):
137
+ df_f = aplicar_filtros(data, genero, edad, localidades, compromiso_renal, antecedentes)
138
+ return generar_mapa_calor(df_f)
139
+
140
+ btn_heat.click(mostrar_heat, inputs=[genero, edad, localidades, compromiso_renal, antecedentes], outputs=mapa_heat)
141
+
142
+ with gr.Tab("Univariado"):
143
+ variable_uni = gr.Dropdown(label="Variable", choices=['edad', 'genero_cat', 'estrato_cat', 'anca_y_renal', 'sindrome'])
144
+ btn_uni = gr.Button("Graficar")
145
+ img_uni = gr.Image()
146
+ btn_uni.click(generar_univariado, inputs=[variable_uni, genero, edad, localidades, compromiso_renal, antecedentes], outputs=img_uni)
147
+
148
+ with gr.Tab("Bivariado"):
149
+ xvar = gr.Dropdown(label="Variable X", choices=['genero_cat', 'estrato_cat', 'edad', 'mpo_cat'])
150
+ yvar = gr.Dropdown(label="Variable Y", choices=['anca_y_renal', 'biopsia_positiva', 'creatinina'])
151
+ btn_bi = gr.Button("Graficar")
152
+ img_bi = gr.Image()
153
+ btn_bi.click(generar_bivariado, inputs=[xvar, yvar, genero, edad, localidades, compromiso_renal, antecedentes], outputs=img_bi)
154
+
155
+ with gr.Tab("Ayuda"):
156
+ gr.Markdown("""
157
+ **Guía de uso:**
158
+ - Seleccione los filtros para género, edad, localidad y antecedentes clínicos.
159
+ - Vaya a cada pestaña para generar los gráficos correspondientes.
160
+ - Mapa de calor: muestra concentración espacial de casos.
161
+ - Mapa coroplético: muestra número de casos por localidad, con opción de superponer factores ambientales.
162
+ - Gráficos univariados: muestra distribución de una variable.
163
+ - Gráficos bivariados: relación entre dos variables relevantes.
164
+ """)
165
 
166
  demo.launch()
167
 
168
+ lanzar_app()