import folium
from folium.plugins import HeatMap ,HeatMapWithTime
import pandas as pdUn heatmap o mapa de calor es una visualización que utiliza una escala de colores para representar la intensidad o concentración de un fenómeno sobre una superficie. Su objetivo es revelar patrones espaciales de forma rápida e intuitiva.
A diferencia de los mapas coropléticos, que colorean regiones administrativas (provincias, cantones, países), el heatmap utiliza puntos geográficos individuales (latitud y longitud) y genera zonas más claras u oscuras según la densidad de datos (hotspots).
Buenas prácticas y errores comunes
- Ajustar radio y blur para una visualización adecuada.
- Usar colores progresivos y claros.
- Incluir leyenda si es posible.
- Normalizar datos si los puntos tienen distinto peso o importancia.
- Usar pocos datos → mapa sin patrones reales.
- Interpretar el heatmap como un valor exacto por región.
- No ajustar los parámetros → manchas confusas o invisibles.
- Colores mal seleccionados que dificultan la lectura.
Dataset
Quito Morpho-Climatic Events Database (1900–2020)
Zapata, C., Cupueran, M. I., Sevilla, E., Jiménez, E., Espinoza, T., & Taipe, R. (2024).
Quito Morpho-Climatic Events Database 1900–2020 Dataset. Harvard Dataverse.
https://doi.org/10.7910/DVN/7VXOQK
df = pd.read_csv("Eventos_Morfo_UIO.csv", encoding='latin-1')df.head()| AÑO | MES | DIA | DPA_PROVIN | DPA_DESPRO | DPA_CANTON | DPA_DESCAN | DPA_PARR_1 | DPA_DESP_1 | longitud | ... | Descripcio | SECTOR / B | OBSERVACIO | GRAVEDAD | Intensidad | Notas | # Muertos | #Heridos | #Desaparec | #Damnifica | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 1900 | 3 | 21 | P17 | PICHINCHA | C1701 | QUITO | PR170113 | ITCHIMBIA | -78.50222 | ... | NaN | La Alameda | Daños en varias caa, caidas de redes telefonicas | 3. molestia en el tráfico, daños ligeros | 1 | NaN | 0 | 0 | 0 | 0 |
| 1 | 1900 | 5 | 19 | P17 | PICHINCHA | C1701 | QUITO | PR170103 | CENTRO HISTORICO | -78.51142 | ... | NaN | Quito | El agua lluvia arraza gran cantidad de materia... | 3. molestia en el tráfico, daños ligeros | 1 | NaN | 0 | 0 | 0 | 0 |
| 2 | 1902 | 3 | 19 | P17 | PICHINCHA | C1701 | QUITO | PR170103 | CENTRO HISTORICO | -78.51588 | ... | NaN | Casco Colonia | los meses de enero a mayo en que hay lluvias f... | 3. molestia en el tráfico, daños ligeros | 1 | NaN | 0 | 0 | 0 | 0 |
| 3 | 1904 | 4 | 4 | P17 | PICHINCHA | C1701 | QUITO | PR170103 | CENTRO HISTORICO | -78.50526 | ... | lluvia y desaseo de vecinos | La Marin | la carrera León intersección con la Oriental s... | 2. daños materiales comentados por el periodis... | 2 | NaN | 0 | 0 | 0 | 0 |
| 4 | 1904 | 4 | 15 | P17 | PICHINCHA | C1701 | QUITO | PR170125 | PUENGASI | -78.49193 | ... | NaN | s. Chiryacu - Luluncoto | El carretero antiguio a los Chillos( act. Ana ... | 3. molestia en el tráfico, daños ligeros | 1 | NaN | 0 | 0 | 0 | 0 |
5 rows × 30 columns
df.columnsIndex(['AÑO', 'MES', 'DIA', 'DPA_PROVIN', 'DPA_DESPRO', 'DPA_CANTON',
'DPA_DESCAN', 'DPA_PARR_1', 'DPA_DESP_1', 'longitud', 'latitud', 'X',
'Y', 'No.', 'Codigo en', 'Fuente Sec', 'Fuente Pri', 'EVENTO',
'SUBCATEGOR', 'CAUSA/DISP', 'Descripcio', 'SECTOR / B', 'OBSERVACIO',
'GRAVEDAD', 'Intensidad', 'Notas', '# Muertos', '#Heridos',
'#Desaparec', '#Damnifica'],
dtype='object')
dfmap=df[["latitud","longitud","Intensidad","GRAVEDAD","EVENTO"]]dfmap.head()| latitud | longitud | Intensidad | GRAVEDAD | EVENTO | |
|---|---|---|---|---|---|
| 0 | -0.21665 | -78.50222 | 1 | 3. molestia en el tráfico, daños ligeros | Inundacion |
| 1 | -0.22154 | -78.51142 | 1 | 3. molestia en el tráfico, daños ligeros | Inundacion |
| 2 | -0.22003 | -78.51588 | 1 | 3. molestia en el tráfico, daños ligeros | Inundacion |
| 3 | -0.21969 | -78.50526 | 2 | 2. daños materiales comentados por el periodis... | Hundimiento |
| 4 | -0.24189 | -78.49193 | 1 | 3. molestia en el tráfico, daños ligeros | Movimiento en Masa |
dfmap.EVENTO.unique()array(['Inundacion', 'Hundimiento', 'Movimiento en Masa', 'Aluvión'],
dtype=object)
dfmap=dfmap[dfmap["EVENTO"]=="Inundacion"]
dfmap = dfmap[["latitud","longitud","Intensidad"]]# Mapa base centrado en Ecuador
m = folium.Map(
[-0.19899731681836336, -78.4428000494774], # PUNTO INICIAL
zoom_start=10)
# Añadir capa de calor
HeatMap(
dfmap,
radius=15, # Influencia de cada punto
blur=15, # Suavizado
#max_zoom=12 # Precisión según el nivel de zoom
).add_to(m)
mdfmap=df[df["EVENTO"]=="Inundacion"]
dfmap = dfmap[["latitud","longitud","EVENTO"]]
# puntos exactos en el mapa "latitud","longitud"
for i, row in dfmap.iterrows():
folium.CircleMarker(
location=[row['latitud'], row['longitud']],
radius=2, # Tamaño del punto
color='blue', # Borde del punto
fill=True,
fill_opacity=0.8,
popup=f"Lat:{row['EVENTO']}"
).add_to(m)
mLinea de tiempo
dfmap=df[df["EVENTO"]=="Inundacion"]
dfmap = dfmap[["latitud","longitud","EVENTO",'AÑO', 'MES', 'DIA']]
dfmap = dfmap.rename(columns=lambda x: x.strip())
# Crear la columna FECHA como texto: 'YYYY-MM-DD'
dfmap['FECHA'] = (
dfmap['AÑO'].astype(int).astype(str) + '-' +
dfmap['MES'].astype(int).astype(str).str.zfill(2) + '-' +
dfmap['DIA'].astype(int).astype(str).str.zfill(2)
)
dfmap = dfmap.dropna(subset=['FECHA'])
dfmap.head()| latitud | longitud | EVENTO | AÑO | MES | DIA | FECHA | |
|---|---|---|---|---|---|---|---|
| 0 | -0.21665 | -78.50222 | Inundacion | 1900 | 3 | 21 | 1900-03-21 |
| 1 | -0.22154 | -78.51142 | Inundacion | 1900 | 5 | 19 | 1900-05-19 |
| 2 | -0.22003 | -78.51588 | Inundacion | 1902 | 3 | 19 | 1902-03-19 |
| 9 | -0.22329 | -78.51295 | Inundacion | 1909 | 3 | 7 | 1909-03-07 |
| 12 | -0.22048 | -78.51566 | Inundacion | 1911 | 1 | 26 | 1911-01-26 |
# Lista de fechas
dfmap['FECHA'] = pd.to_datetime(dfmap['FECHA'], errors='coerce')
# 3️⃣ Eliminar fechas inválidas
dfmap = dfmap.dropna(subset=['FECHA'])
# 4️⃣ Ahora sí, crear time_index correctamente
time_index = sorted(dfmap['FECHA'].dt.strftime('%Y-%m-%d').unique())
# Lista
data = []
for fecha in time_index:
sub = dfmap[dfmap['FECHA'] == fecha]
puntos = sub[['latitud', 'longitud']].values.tolist()
data.append(puntos)# puntos exactos en el mapa "latitud","longitud"
# Mapa base centrado en Ecuador
m = folium.Map(
[-0.19899731681836336, -78.4428000494774], # PUNTO INICIAL
zoom_start=10)
HeatMapWithTime(
data=data,
index=time_index,
auto_play=True,
radius=20,
max_opacity=0.6
).add_to(m)
m