Spaces:
Build error
Build error
Update app.py
Browse files
app.py
CHANGED
|
@@ -11,41 +11,33 @@ import io
|
|
| 11 |
import base64
|
| 12 |
import folium
|
| 13 |
from folium.plugins import HeatMap
|
|
|
|
| 14 |
import os
|
| 15 |
|
| 16 |
# =========================
|
| 17 |
-
# 1. CARGA DE DATOS
|
| 18 |
# =========================
|
| 19 |
data = pd.read_csv("VasculitisAsociadasA-BDD10jul24_DATA_2025-03-19_1033.csv")
|
| 20 |
data.columns = data.columns.str.strip().str.lower()
|
| 21 |
|
| 22 |
-
#
|
| 23 |
-
col_localidad = next((col for col in data.columns if 'localidad' in col), None)
|
| 24 |
-
if col_localidad:
|
| 25 |
-
data.rename(columns={col_localidad: 'localidad'}, inplace=True)
|
| 26 |
-
data['localidad'] = data['localidad'].astype(str).str.upper()
|
| 27 |
-
else:
|
| 28 |
-
data['localidad'] = "SIN DATO"
|
| 29 |
-
|
| 30 |
-
# Carga GeoJSONs ambientales y localidades
|
| 31 |
geo_localidades = gpd.read_file("loca.json")
|
| 32 |
-
geo_localidades.columns = geo_localidades.columns.str.strip()
|
| 33 |
-
if '
|
| 34 |
-
geo_localidades['localidad'] = geo_localidades['
|
| 35 |
else:
|
| 36 |
-
raise ValueError("El GeoJSON de localidades no tiene la columna '
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
}
|
| 46 |
|
| 47 |
# =========================
|
| 48 |
-
# 2.
|
| 49 |
# =========================
|
| 50 |
categorias = {
|
| 51 |
'genero_cat': data['genero'].map({0: 'Masculino', 1: 'Femenino'}),
|
|
@@ -54,96 +46,21 @@ categorias = {
|
|
| 54 |
}
|
| 55 |
data = data.assign(**categorias)
|
| 56 |
|
| 57 |
-
# ANCA positivo
|
| 58 |
data['anca_cat'] = data['ancas'].map({0: 'Negativo', 1: 'Positivo'}).fillna('No definido')
|
| 59 |
data['mpo_cat'] = data['mpo'].map({0: 'Negativo', 1: 'Positivo'}).fillna('No definido')
|
| 60 |
data['pr3_cat'] = data['pr3'].map({0: 'Negativo', 1: 'Positivo'}).fillna('No definido')
|
| 61 |
|
| 62 |
-
# Biopsia
|
| 63 |
biopsia_cols = [col for col in data.columns if col.startswith('biopsia___')]
|
| 64 |
data['biopsia_positiva'] = data[biopsia_cols].sum(axis=1).apply(lambda x: 'Sí' if x > 0 else 'No')
|
| 65 |
data['anca_y_renal'] = np.where((data['ancas'] == 1) & (data['biopsia_positiva'] == 'Sí'), 'Sí', 'No')
|
| 66 |
|
| 67 |
# =========================
|
| 68 |
-
# 3.
|
| 69 |
-
# =========================
|
| 70 |
-
def aplicar_filtros(df, genero, edad, localidad, anca_tipo, antecedentes):
|
| 71 |
-
if genero != "Todos":
|
| 72 |
-
df = df[df['genero_cat'] == genero]
|
| 73 |
-
df = df[(df['edad'] >= edad[0]) & (df['edad'] <= edad[1])]
|
| 74 |
-
if localidad:
|
| 75 |
-
df = df[df['localidad'].isin(localidad)]
|
| 76 |
-
if anca_tipo != "Todos":
|
| 77 |
-
df = df[df['anca_cat'] == anca_tipo]
|
| 78 |
-
for ant in antecedentes:
|
| 79 |
-
if ant in df.columns:
|
| 80 |
-
df = df[df[ant] == 1]
|
| 81 |
-
return df
|
| 82 |
-
|
| 83 |
-
# =========================
|
| 84 |
-
# 4. MAPA COROPLÉTICO CON CAPAS AMBIENTALES
|
| 85 |
-
# =========================
|
| 86 |
-
def generar_mapa_coropletico(df_filtrado):
|
| 87 |
-
conteo = df_filtrado.groupby('localidad').size().reset_index(name='casos')
|
| 88 |
-
geo_merged = geo_localidades.merge(conteo, on='localidad', how='left').fillna(0)
|
| 89 |
-
fig = px.choropleth_mapbox(
|
| 90 |
-
geo_merged,
|
| 91 |
-
geojson=geo_merged.geometry,
|
| 92 |
-
locations=geo_merged.index,
|
| 93 |
-
color='casos',
|
| 94 |
-
hover_name='localidad',
|
| 95 |
-
mapbox_style='carto-positron',
|
| 96 |
-
zoom=10,
|
| 97 |
-
center={"lat": 4.65, "lon": -74.1},
|
| 98 |
-
opacity=0.6
|
| 99 |
-
)
|
| 100 |
-
return fig
|
| 101 |
-
|
| 102 |
-
# =========================
|
| 103 |
-
# 5. ANÁLISIS DESCRIPTIVO
|
| 104 |
# =========================
|
| 105 |
-
|
| 106 |
-
rutas = []
|
| 107 |
-
variables_uni = ['edad', 'genero_cat', 'regimen_cat', 'estrato_cat', 'anca_cat',
|
| 108 |
-
'mpo_cat', 'pr3_cat', 'biopsia_positiva', 'anca_y_renal', 'sindrome',
|
| 109 |
-
'diabetes', 'falla_cardiaca', 'epoc', 'hta']
|
| 110 |
-
for col in variables_uni:
|
| 111 |
-
plt.figure(figsize=(6, 4))
|
| 112 |
-
if data[col].dtype == 'object':
|
| 113 |
-
sns.countplot(data=data, x=col, order=data[col].value_counts().index)
|
| 114 |
-
else:
|
| 115 |
-
sns.histplot(data[col], kde=True)
|
| 116 |
-
plt.title(f"Distribución de {col}")
|
| 117 |
-
plt.xticks(rotation=30)
|
| 118 |
-
plt.tight_layout()
|
| 119 |
-
path = f"uni_{col}.png"
|
| 120 |
-
plt.savefig(path)
|
| 121 |
-
rutas.append(path)
|
| 122 |
-
plt.close()
|
| 123 |
-
return rutas
|
| 124 |
-
|
| 125 |
-
def generar_bivariado():
|
| 126 |
-
relaciones_bi = [
|
| 127 |
-
('anca_cat', 'biopsia_positiva'), ('anca_cat', 'mpo_cat'),
|
| 128 |
-
('anca_cat', 'pr3_cat'), ('estrato_cat', 'biopsia_positiva'),
|
| 129 |
-
('genero_cat', 'anca_cat'), ('regimen_cat', 'anca_cat'),
|
| 130 |
-
('sindrome', 'biopsia_positiva')
|
| 131 |
-
]
|
| 132 |
-
rutas = []
|
| 133 |
-
for x, y in relaciones_bi:
|
| 134 |
-
plt.figure(figsize=(6, 4))
|
| 135 |
-
sns.countplot(data=data, x=x, hue=y)
|
| 136 |
-
plt.title(f"{x} vs {y}")
|
| 137 |
-
plt.xticks(rotation=30)
|
| 138 |
-
plt.tight_layout()
|
| 139 |
-
path = f"bi_{x}_{y}.png"
|
| 140 |
-
plt.savefig(path)
|
| 141 |
-
rutas.append(path)
|
| 142 |
-
plt.close()
|
| 143 |
-
return rutas
|
| 144 |
|
| 145 |
# =========================
|
| 146 |
-
#
|
| 147 |
# =========================
|
| 148 |
def interfaz():
|
| 149 |
with gr.Blocks() as demo:
|
|
@@ -151,22 +68,22 @@ def interfaz():
|
|
| 151 |
|
| 152 |
genero = gr.Dropdown(label="Género", choices=["Todos", "Masculino", "Femenino"], value="Todos")
|
| 153 |
edad = gr.Slider(label="Edad", minimum=0, maximum=100, value=(20, 80), step=1)
|
| 154 |
-
localidades = gr.Dropdown(label="Localidad", choices=sorted(data['localidad'].unique()), multiselect=True)
|
| 155 |
-
|
| 156 |
antecedentes = gr.CheckboxGroup(label="Antecedentes", choices=["diabetes", "hta", "epoc", "falla_cardiaca"])
|
| 157 |
|
| 158 |
boton = gr.Button("Generar Mapa")
|
| 159 |
salida = gr.Plot()
|
| 160 |
|
| 161 |
-
def actualizar(genero, edad, localidades,
|
| 162 |
-
filtrado = aplicar_filtros(data, genero, edad, localidades,
|
| 163 |
if filtrado.empty:
|
| 164 |
fig = go.Figure()
|
| 165 |
fig.update_layout(title="No hay datos para los filtros seleccionados")
|
| 166 |
return fig
|
| 167 |
return generar_mapa_coropletico(filtrado)
|
| 168 |
|
| 169 |
-
boton.click(actualizar, inputs=[genero, edad, localidades,
|
| 170 |
|
| 171 |
with gr.Tab("Gráficos Univariados"):
|
| 172 |
for path in generar_univariado():
|
|
@@ -178,4 +95,4 @@ def interfaz():
|
|
| 178 |
|
| 179 |
demo.launch()
|
| 180 |
|
| 181 |
-
interfaz()
|
|
|
|
| 11 |
import base64
|
| 12 |
import folium
|
| 13 |
from folium.plugins import HeatMap
|
| 14 |
+
from shapely.geometry import Point
|
| 15 |
import os
|
| 16 |
|
| 17 |
# =========================
|
| 18 |
+
# 1. CARGA DE DATOS Y LOCALIDADES
|
| 19 |
# =========================
|
| 20 |
data = pd.read_csv("VasculitisAsociadasA-BDD10jul24_DATA_2025-03-19_1033.csv")
|
| 21 |
data.columns = data.columns.str.strip().str.lower()
|
| 22 |
|
| 23 |
+
# Cargar localidades
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 24 |
geo_localidades = gpd.read_file("loca.json")
|
| 25 |
+
geo_localidades.columns = geo_localidades.columns.str.strip().str.lower()
|
| 26 |
+
if 'locnombre' in geo_localidades.columns:
|
| 27 |
+
geo_localidades['localidad'] = geo_localidades['locnombre'].str.upper()
|
| 28 |
else:
|
| 29 |
+
raise ValueError("El GeoJSON de localidades no tiene la columna 'locnombre')")
|
| 30 |
+
|
| 31 |
+
# Convertir data a GeoDataFrame con lat/lon
|
| 32 |
+
data = data.dropna(subset=['coordenada_residencia-latitud', 'coordenada_residencia-longitud'])
|
| 33 |
+
data['geometry'] = data.apply(lambda row: Point(row['coordenada_residencia-longitud'], row['coordenada_residencia-latitud']), axis=1)
|
| 34 |
+
data = gpd.GeoDataFrame(data, geometry='geometry', crs='EPSG:4326')
|
| 35 |
+
|
| 36 |
+
# Intersección espacial para asignar localidad
|
| 37 |
+
data = gpd.sjoin(data, geo_localidades[['localidad', 'geometry']], how='left', predicate='within')
|
|
|
|
| 38 |
|
| 39 |
# =========================
|
| 40 |
+
# 2. CATEGORÍAS Y VARIABLES DERIVADAS
|
| 41 |
# =========================
|
| 42 |
categorias = {
|
| 43 |
'genero_cat': data['genero'].map({0: 'Masculino', 1: 'Femenino'}),
|
|
|
|
| 46 |
}
|
| 47 |
data = data.assign(**categorias)
|
| 48 |
|
|
|
|
| 49 |
data['anca_cat'] = data['ancas'].map({0: 'Negativo', 1: 'Positivo'}).fillna('No definido')
|
| 50 |
data['mpo_cat'] = data['mpo'].map({0: 'Negativo', 1: 'Positivo'}).fillna('No definido')
|
| 51 |
data['pr3_cat'] = data['pr3'].map({0: 'Negativo', 1: 'Positivo'}).fillna('No definido')
|
| 52 |
|
|
|
|
| 53 |
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 |
+
# 3. FUNCIONES DE FILTRO Y GRÁFICOS (sin cambios)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 59 |
# =========================
|
| 60 |
+
# ... (mantener las funciones: aplicar_filtros, generar_mapa_coropletico, generar_univariado, generar_bivariado)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 61 |
|
| 62 |
# =========================
|
| 63 |
+
# 4. INTERFAZ DE USUARIO (GRADIO)
|
| 64 |
# =========================
|
| 65 |
def interfaz():
|
| 66 |
with gr.Blocks() as demo:
|
|
|
|
| 68 |
|
| 69 |
genero = gr.Dropdown(label="Género", choices=["Todos", "Masculino", "Femenino"], value="Todos")
|
| 70 |
edad = gr.Slider(label="Edad", minimum=0, maximum=100, value=(20, 80), step=1)
|
| 71 |
+
localidades = gr.Dropdown(label="Localidad", choices=sorted(data['localidad'].dropna().unique()), multiselect=True)
|
| 72 |
+
compromiso_renal = gr.Dropdown(label="Compromiso Renal", choices=["Todos", "Sí", "No"], value="Todos")
|
| 73 |
antecedentes = gr.CheckboxGroup(label="Antecedentes", choices=["diabetes", "hta", "epoc", "falla_cardiaca"])
|
| 74 |
|
| 75 |
boton = gr.Button("Generar Mapa")
|
| 76 |
salida = gr.Plot()
|
| 77 |
|
| 78 |
+
def actualizar(genero, edad, localidades, compromiso_renal, antecedentes):
|
| 79 |
+
filtrado = aplicar_filtros(data, genero, edad, localidades, compromiso_renal, antecedentes)
|
| 80 |
if filtrado.empty:
|
| 81 |
fig = go.Figure()
|
| 82 |
fig.update_layout(title="No hay datos para los filtros seleccionados")
|
| 83 |
return fig
|
| 84 |
return generar_mapa_coropletico(filtrado)
|
| 85 |
|
| 86 |
+
boton.click(actualizar, inputs=[genero, edad, localidades, compromiso_renal, antecedentes], outputs=salida)
|
| 87 |
|
| 88 |
with gr.Tab("Gráficos Univariados"):
|
| 89 |
for path in generar_univariado():
|
|
|
|
| 95 |
|
| 96 |
demo.launch()
|
| 97 |
|
| 98 |
+
interfaz()
|