hedtorresca commited on
Commit
0ff3e0c
·
verified ·
1 Parent(s): 61063ac

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +118 -97
app.py CHANGED
@@ -1,25 +1,25 @@
 
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 seaborn as sns
7
  import matplotlib.pyplot as plt
 
8
  import folium
9
  from folium.plugins import HeatMap
10
  from shapely.geometry import Point
 
11
  import warnings
12
  warnings.filterwarnings("ignore")
13
 
14
- # ====================
15
- # CARGA DE DATOS
16
- # ====================
17
  data = pd.read_csv("VasculitisAsociadasA-BDD10jul24_DATA_2025-03-19_1033.csv")
18
  data.columns = data.columns.str.strip().str.lower()
19
  data = data.dropna(subset=['latitud', 'longitud'])
20
  data['geometry'] = data.apply(lambda row: Point(row['longitud'], row['latitud']), axis=1)
21
  data = gpd.GeoDataFrame(data, geometry='geometry', crs='EPSG:4326')
22
 
 
23
  geo_localidades = gpd.read_file("loca.json")
24
  geo_localidades.columns = geo_localidades.columns.str.lower()
25
  geo_localidades = geo_localidades.rename(columns={"locnombre": "localidad"})
@@ -28,8 +28,8 @@ data = gpd.sjoin(data.to_crs(geo_localidades.crs), geo_localidades[['localidad',
28
  data.drop(columns='index_right', inplace=True)
29
 
30
  # Capas ambientales
31
- def cargar_geojson(path):
32
- gdf = gpd.read_file(path).to_crs("EPSG:4326")
33
  for col in gdf.columns:
34
  if pd.api.types.is_datetime64_any_dtype(gdf[col]):
35
  gdf[col] = gdf[col].astype(str)
@@ -45,8 +45,11 @@ capas_ambientales = {
45
  }
46
 
47
  # Variables derivadas
48
- data['genero_cat'] = data['genero'].map({0: 'Masculino', 1: 'Femenino'})
49
- data['estrato_cat'] = data['estrato'].fillna('Sin dato')
 
 
 
50
  data['anca_cat'] = 'Positivo'
51
  data['mpo_cat'] = data['mpo'].map({0: 'Negativo', 1: 'Positivo'}).fillna('No definido')
52
  data['pr3_cat'] = data['pr3'].map({0: 'Negativo', 1: 'Positivo'}).fillna('No definido')
@@ -54,134 +57,152 @@ biopsia_cols = [col for col in data.columns if col.startswith('biopsia___')]
54
  data['biopsia_positiva'] = data[biopsia_cols].sum(axis=1).apply(lambda x: 'Sí' if x > 0 else 'No')
55
  data['anca_y_renal'] = np.where((data['ancas'] == 1) & (data['biopsia_positiva'] == 'Sí'), 'Sí', 'No')
56
 
57
- # ====================
58
- # FUNCIONES AUXILIARES
59
- # ====================
60
- def aplicar_filtros(df, genero, edad, localidades, renal, antecedentes):
61
- df = df.copy()
62
  if genero != "Todos":
63
- df = df[df['genero_cat'] == genero]
64
- if isinstance(edad, tuple):
65
- df = df[(df['edad'] >= edad[0]) & (df['edad'] <= edad[1])]
66
  if localidades:
67
- df = df[df['localidad'].isin(localidades)]
68
- if renal != "Todos":
69
- df = df[df['biopsia_positiva'] == renal]
70
- for a in antecedentes:
71
- if a in df.columns:
72
- df = df[df[a] == 1]
73
- return df
74
-
75
- def generar_mapa_kernel(df):
76
- m = folium.Map(location=[4.65, -74.1], zoom_start=11)
77
- if not df.empty:
78
- HeatMap(df[['latitud', 'longitud']].dropna().values.tolist(), radius=12).add_to(m)
79
- return m._repr_html_()
80
-
81
  def generar_mapa_coropletico(df, capas):
82
  df_grouped = df.groupby('localidad').size().reset_index(name='casos')
83
  geo_local_copy = geo_localidades.merge(df_grouped, on='localidad', how='left').fillna({'casos': 0})
84
  m = folium.Map(location=[4.65, -74.1], zoom_start=11)
85
  folium.Choropleth(
86
  geo_data=geo_local_copy,
 
87
  data=geo_local_copy,
88
  columns=['localidad', 'casos'],
89
  key_on='feature.properties.localidad',
90
  fill_color='YlOrRd',
91
- fill_opacity=0.6,
92
- line_opacity=0.4,
93
- legend_name='Casos'
94
  ).add_to(m)
 
 
 
 
 
 
 
 
95
  for _, row in df.iterrows():
96
  folium.CircleMarker(
97
  location=(row['latitud'], row['longitud']),
98
  radius=4,
99
- popup=f"Edad: {row['edad']}, Género: {row['genero_cat']}, ANCA: {row['anca_cat']}",
100
- color='black', fill=True, fill_opacity=0.5
101
  ).add_to(m)
 
102
  for capa in capas:
103
- gdf = capas_ambientales.get(capa)
104
- if gdf is not None:
105
- folium.GeoJson(
106
- gdf,
107
- name=capa,
108
- tooltip=folium.GeoJsonTooltip(fields=gdf.columns[:2].tolist())
109
- ).add_to(m)
110
  folium.LayerControl().add_to(m)
111
  return m._repr_html_()
112
 
113
- def graficos_univariados(df, var):
114
- fig, ax = plt.subplots(figsize=(6, 4))
115
- if df[var].dtype == 'object' or df[var].nunique() < 10:
116
- sns.countplot(data=df, x=var, ax=ax)
 
 
 
 
 
 
 
 
 
 
 
 
 
117
  else:
118
- sns.histplot(df[var], kde=True, ax=ax)
119
- plt.xticks(rotation=45)
120
- plt.tight_layout()
121
  path = f"uni_{var}.png"
 
122
  plt.savefig(path)
123
  plt.close()
124
  return path
125
 
126
- def graficos_bivariados(df, var1, var2):
127
- fig, ax = plt.subplots(figsize=(6, 4))
128
- if df[var1].dtype == 'object' and df[var2].dtype == 'object':
129
- sns.countplot(data=df, x=var1, hue=var2, ax=ax)
130
- elif df[var1].dtype != 'object' and df[var2].dtype == 'object':
131
- sns.boxplot(data=df, x=var2, y=var1, ax=ax)
132
- elif df[var1].dtype == 'object' and df[var2].dtype != 'object':
133
- sns.boxplot(data=df, x=var1, y=var2, ax=ax)
134
  else:
135
- sns.scatterplot(data=df, x=var1, y=var2, ax=ax)
136
- plt.xticks(rotation=45)
 
137
  plt.tight_layout()
138
- path = f"bi_{var1}_{var2}.png"
139
  plt.savefig(path)
140
  plt.close()
141
  return path
142
 
143
- # ====================
144
- # INTERFAZ
145
- # ====================
146
  def lanzar_app():
147
  with gr.Blocks() as demo:
148
- gr.Markdown("# Tablero Interactivo de Vasculitis ANCA con Análisis Espacial")
 
 
 
 
 
 
 
 
149
 
150
  with gr.Tab("Mapa Coroplético"):
151
- genero = gr.Dropdown(["Todos", "Masculino", "Femenino"], label="Género")
152
- edad = gr.Slider(0, 100, value=(20, 80), label="Edad")
153
- localidades = gr.Dropdown(sorted(data['localidad'].dropna().unique()), multiselect=True, label="Localidades")
154
- compromiso = gr.Dropdown(["Todos", "Sí", "No"], label="Compromiso Renal")
155
- antecedentes = gr.CheckboxGroup(["diabetes", "hta", "epoc", "falla_cardiaca"], label="Antecedentes")
156
- capas = gr.CheckboxGroup(list(capas_ambientales.keys()), label="Capas Ambientales")
157
- boton = gr.Button("Generar Mapa")
158
- salida = gr.HTML()
159
- boton.click(lambda g, e, l, c, a, cap: generar_mapa_coropletico(
160
- aplicar_filtros(data, g, e, l, c, a), cap
161
- ), inputs=[genero, edad, localidades, compromiso, antecedentes, capas], outputs=salida)
162
 
163
  with gr.Tab("Mapa de Calor"):
164
- boton2 = gr.Button("Generar Mapa")
165
- salida2 = gr.HTML()
166
- boton2.click(lambda g, e, l, c, a: generar_mapa_kernel(
167
- aplicar_filtros(data, g, e, l, c, a)
168
- ), inputs=[genero, edad, localidades, compromiso, antecedentes], outputs=salida2)
169
-
170
- with gr.Tab("Gráficos Univariados"):
171
- var_uni = gr.Dropdown(label="Variable", choices=[
172
- "edad", "genero_cat", "estrato_cat", "anca_cat", "biopsia_positiva", "sindrome", "proteinuria", "creatinina"
173
- ])
174
- btn_uni = gr.Button("Generar gráfico")
175
- out_uni = gr.Image()
176
- btn_uni.click(lambda v: graficos_univariados(data, v), inputs=var_uni, outputs=out_uni)
177
-
178
- with gr.Tab("Gráficos Bivariados"):
179
- var1 = gr.Dropdown(label="Variable 1", choices=data.columns.tolist())
180
- var2 = gr.Dropdown(label="Variable 2", choices=data.columns.tolist())
181
- btn_bi = gr.Button("Generar gráfico")
182
- out_bi = gr.Image()
183
- btn_bi.click(lambda x, y: graficos_bivariados(data, x, y), inputs=[var1, var2], outputs=out_bi)
 
 
 
 
 
 
184
 
185
  demo.launch()
186
 
187
- lanzar_app()
 
1
+ # app.py
2
  import gradio as gr
3
  import pandas as pd
4
  import numpy as np
5
  import geopandas as gpd
 
 
6
  import matplotlib.pyplot as plt
7
+ import seaborn as sns
8
  import folium
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
+ # Localidades
23
  geo_localidades = gpd.read_file("loca.json")
24
  geo_localidades.columns = geo_localidades.columns.str.lower()
25
  geo_localidades = geo_localidades.rename(columns={"locnombre": "localidad"})
 
28
  data.drop(columns='index_right', inplace=True)
29
 
30
  # Capas ambientales
31
+ def cargar_geojson(filepath):
32
+ gdf = gpd.read_file(filepath).to_crs("EPSG:4326")
33
  for col in gdf.columns:
34
  if pd.api.types.is_datetime64_any_dtype(gdf[col]):
35
  gdf[col] = gdf[col].astype(str)
 
45
  }
46
 
47
  # Variables derivadas
48
+ data = data.assign(
49
+ genero_cat=data['genero'].map({0: 'Masculino', 1: 'Femenino'}),
50
+ regimen_cat=data['regimen'].map({1: 'Contributivo', 2: 'Subsidiado'}),
51
+ estrato_cat=data['estrato'].fillna('Sin estrato')
52
+ )
53
  data['anca_cat'] = 'Positivo'
54
  data['mpo_cat'] = data['mpo'].map({0: 'Negativo', 1: 'Positivo'}).fillna('No definido')
55
  data['pr3_cat'] = data['pr3'].map({0: 'Negativo', 1: 'Positivo'}).fillna('No definido')
 
57
  data['biopsia_positiva'] = data[biopsia_cols].sum(axis=1).apply(lambda x: 'Sí' if x > 0 else 'No')
58
  data['anca_y_renal'] = np.where((data['ancas'] == 1) & (data['biopsia_positiva'] == 'Sí'), 'Sí', 'No')
59
 
60
+ # Filtros
61
+ def aplicar_filtros(df, genero, edad_rango, localidades, compromiso_renal, antecedentes):
62
+ df_filtrado = df.copy()
 
 
63
  if genero != "Todos":
64
+ df_filtrado = df_filtrado[df_filtrado['genero_cat'] == genero]
65
+ df_filtrado = df_filtrado[(df_filtrado['edad'] >= edad_rango[0]) & (df_filtrado['edad'] <= edad_rango[1])]
 
66
  if localidades:
67
+ df_filtrado = df_filtrado[df_filtrado['localidad'].isin(localidades)]
68
+ if compromiso_renal != "Todos":
69
+ df_filtrado = df_filtrado[df_filtrado['biopsia_positiva'] == compromiso_renal]
70
+ for ant in antecedentes:
71
+ if ant in df_filtrado.columns:
72
+ df_filtrado = df_filtrado[df_filtrado[ant] == 1]
73
+ return df_filtrado
74
+
75
+ # Mapa coroplético
 
 
 
 
 
76
  def generar_mapa_coropletico(df, capas):
77
  df_grouped = df.groupby('localidad').size().reset_index(name='casos')
78
  geo_local_copy = geo_localidades.merge(df_grouped, on='localidad', how='left').fillna({'casos': 0})
79
  m = folium.Map(location=[4.65, -74.1], zoom_start=11)
80
  folium.Choropleth(
81
  geo_data=geo_local_copy,
82
+ name='Casos por localidad',
83
  data=geo_local_copy,
84
  columns=['localidad', 'casos'],
85
  key_on='feature.properties.localidad',
86
  fill_color='YlOrRd',
87
+ fill_opacity=0.7,
88
+ line_opacity=0.3,
89
+ legend_name='Número de casos'
90
  ).add_to(m)
91
+
92
+ for _, row in geo_local_copy.iterrows():
93
+ folium.Marker(
94
+ location=row['geometry'].centroid.coords[0][::-1],
95
+ popup=f"{row['localidad']}: {int(row['casos'])} casos",
96
+ icon=folium.Icon(color='blue', icon='info-sign')
97
+ ).add_to(m)
98
+
99
  for _, row in df.iterrows():
100
  folium.CircleMarker(
101
  location=(row['latitud'], row['longitud']),
102
  radius=4,
103
+ popup=f"Edad: {row['edad']}, Género: {row['genero_cat']}, Estrato: {row['estrato_cat']}, Creatinina: {row.get('creatinina', '')}",
104
+ color='black', fill=True, fill_opacity=0.6
105
  ).add_to(m)
106
+
107
  for capa in capas:
108
+ if capa in capas_ambientales:
109
+ gdf = capas_ambientales[capa]
110
+ folium.GeoJson(gdf, name=capa, tooltip=folium.GeoJsonTooltip(fields=gdf.columns[:2].tolist())).add_to(m)
111
+
 
 
 
112
  folium.LayerControl().add_to(m)
113
  return m._repr_html_()
114
 
115
+ # Mapa calor
116
+ def generar_mapa_calor(df):
117
+ m = folium.Map(location=[4.65, -74.1], zoom_start=11)
118
+ if df.empty:
119
+ return m._repr_html_()
120
+ heat_data = df[['latitud', 'longitud']].dropna().values.tolist()
121
+ HeatMap(heat_data, radius=14).add_to(m)
122
+ return m._repr_html_()
123
+
124
+ # Gráfico univariado
125
+ def grafico_univariado(var, genero, edad, localidades, compromiso_renal, antecedentes):
126
+ df = aplicar_filtros(data, genero, edad, localidades, compromiso_renal, antecedentes)
127
+ if df.empty:
128
+ return None
129
+ plt.figure(figsize=(6, 4))
130
+ if df[var].dtype == 'object':
131
+ sns.countplot(data=df, x=var)
132
  else:
133
+ sns.histplot(df[var], kde=True)
134
+ plt.title(f"Distribución de {var}")
 
135
  path = f"uni_{var}.png"
136
+ plt.tight_layout()
137
  plt.savefig(path)
138
  plt.close()
139
  return path
140
 
141
+ # Gráfico bivariado
142
+ def grafico_bivariado(x, y, genero, edad, localidades, compromiso_renal, antecedentes):
143
+ df = aplicar_filtros(data, genero, edad, localidades, compromiso_renal, antecedentes)
144
+ if df.empty:
145
+ return None
146
+ plt.figure(figsize=(6, 4))
147
+ if df[x].dtype == 'object' or df[y].dtype == 'object':
148
+ sns.countplot(data=df, x=x, hue=y)
149
  else:
150
+ sns.scatterplot(data=df, x=x, y=y)
151
+ plt.title(f"{x} vs {y}")
152
+ path = f"bi_{x}_{y}.png"
153
  plt.tight_layout()
 
154
  plt.savefig(path)
155
  plt.close()
156
  return path
157
 
158
+ # Interfaz
 
 
159
  def lanzar_app():
160
  with gr.Blocks() as demo:
161
+ gr.Markdown("## 🧬 Tablero Geoespacial Interactivo: Vasculitis ANCA con Compromiso Renal")
162
+
163
+ with gr.Row():
164
+ genero = gr.Dropdown(label="Género", choices=["Todos", "Masculino", "Femenino"], value="Todos")
165
+ edad = gr.Slider(label="Edad", minimum=0, maximum=100, value=(20, 80))
166
+ with gr.Row():
167
+ localidades = gr.Dropdown(label="Localidades", choices=sorted(data['localidad'].dropna().unique()), multiselect=True)
168
+ compromiso = gr.Dropdown(label="Compromiso Renal", choices=["Todos", "Sí", "No"], value="Todos")
169
+ antecedentes = gr.CheckboxGroup(label="Antecedentes", choices=["diabetes", "hta", "epoc", "falla_cardiaca"])
170
 
171
  with gr.Tab("Mapa Coroplético"):
172
+ capas = gr.CheckboxGroup(label="Capas Ambientales", choices=list(capas_ambientales.keys()))
173
+ btn = gr.Button("Mostrar")
174
+ mapa = gr.HTML()
175
+ btn.click(fn=lambda g, e, l, c, a, cap: generar_mapa_coropletico(aplicar_filtros(data, g, e, l, c, a), cap),
176
+ inputs=[genero, edad, localidades, compromiso, antecedentes, capas], outputs=mapa)
 
 
 
 
 
 
177
 
178
  with gr.Tab("Mapa de Calor"):
179
+ btn2 = gr.Button("Mostrar")
180
+ salida = gr.HTML()
181
+ btn2.click(fn=lambda g, e, l, c, a: generar_mapa_calor(aplicar_filtros(data, g, e, l, c, a)),
182
+ inputs=[genero, edad, localidades, compromiso, antecedentes], outputs=salida)
183
+
184
+ with gr.Tab("Univariado"):
185
+ var = gr.Dropdown(label="Variable", choices=['edad', 'genero_cat', 'estrato_cat', 'anca_y_renal', 'sindrome'])
186
+ btn3 = gr.Button("Graficar")
187
+ img = gr.Image()
188
+ btn3.click(grafico_univariado, inputs=[var, genero, edad, localidades, compromiso, antecedentes], outputs=img)
189
+
190
+ with gr.Tab("Bivariado"):
191
+ x = gr.Dropdown(label="X", choices=['genero_cat', 'estrato_cat', 'edad', 'mpo_cat'])
192
+ y = gr.Dropdown(label="Y", choices=['anca_y_renal', 'biopsia_positiva', 'creatinina'])
193
+ btn4 = gr.Button("Graficar")
194
+ img2 = gr.Image()
195
+ btn4.click(grafico_bivariado, inputs=[x, y, genero, edad, localidades, compromiso, antecedentes], outputs=img2)
196
+
197
+ with gr.Tab("Ayuda"):
198
+ gr.Markdown("""
199
+ **Instrucciones de uso:**
200
+ - Aplica filtros por género, edad, localidad y antecedentes.
201
+ - Visualiza mapas con o sin capas ambientales.
202
+ - Compara variables clínicas por localidad.
203
+ - Todos los análisis son descriptivos, sin inferencia causal directa.
204
+ """)
205
 
206
  demo.launch()
207
 
208
+ lanzar_app()