Desde la economía urbana, la infraestructura vial es un ejemplo clásico de bien común. A medida que más vehículos ingresan a la red, el costo marginal de usarla aumenta para todos los usuarios, lo que genera externalidades negativas como congestión vial, pérdida de productividad y tiempo y contaminación. La evidencia, además, muestra que ampliar la oferta vial no resuelve por sí sola el problema, al reducir el costo de circular en vehículo particular, induce nueva demanda que vuelve a saturar la red. Este fenómeno, conocido en economía como demanda inducida, explica por qué construir más vías rara vez descongestiona las ciudades de forma sostenida.
Frente a esta limitación, optimizar la infraestructura vial existente se vuelve una prioridad para los planificadores urbanos. En ese contexto, las plataformas de navegación colaborativa como Waze funcionan como una red de sensores ciudadanos recogiendo grandes volúmenes de datos de movilidad en tiempo real que pueden servir de insumo para repensar cómo se mueve la ciudad.
Este documento replica el análisis de los patrones espaciales y temporales de los eventos de movilidad reportados en Waze durante el día 26. Se aplican técnicas de estadística espacial en R :con paquetes como spatstat y leaflet, para visualizar y evaluar la concentración de peligros, congestión, accidentes y cierres viales. A partir de mapas de calor y análisis de patrones de puntos, se busca identificar los puntos críticos de la ciudad y aportar evidencias empiricas para orientar futuras intervenciones viales.
Para garantizar que el análisis sea estructurado y replicable, se adaptó el estándar CRISP-DM (Cross-Industry Standard Process for Data Mining) al contexto del análisis geoespacial de movilidad. Las fases se ejecutaron de la siguiente manera:
1. Comprensión del Negocio: Se enmarcó el problema bajo la necesidad de identificar las zonas más criticas en terminos de eventos tipo accidentes, congestión vehicular, peligros en la via, cierres viales para optimizar el uso de la infraestructura vial existente.
2 y 3. Comprensión y Preparación de los Datos:
Se extrajeron los reportes de Waze correspondientes al día 26. Mediante
la librería lubridate, se extrajeron las componentes
temporales (hora y día). Simultáneamente, se estandarizaron las
coordenadas geográficas (latitud y longitud), ajustando los decimales
mediante potencias de 10 para su correcta proyección espacial. Las
categorías de los eventos fueron recodificadas al español para
estandarizar el análisis.
4. Modelado Espacial: En lugar de modelos
predictivos tradicionales, el modelado se enfocó en el análisis de
Patrones de Puntos Espaciales (Spatial Point Patterns). Utilizando las
librerías spatstat y terra, se generaron
ventanas de observación espacial (objetos owin).
5 y 6. Evaluación y Despliegue: Se validaron los
clústeres espaciales superponiéndolos en cartografía interactiva de alto
contraste mediante la librería leaflet. El resultado final
se despliega a través de este reporte dinámico, sincronizando las capas
con leafsync para permitir la comparación simultánea de los
cuatro tipos de eventos (peligro, congestión, accidentes y cierres) por
parte de los tomadores de decisiones.
Para comprender la estructura de la información extraída de la plataforma Waze, se presenta el siguiente diccionario de datos con las variables principales utilizadas en el analisis.
| Variable | Tipo de Dato | Descripción |
|---|---|---|
creation_Date |
Fecha/Hora | Fecha y hora exacta de creación del reporte por parte del usuario. |
fecha |
Fecha | Columna transformada al formato estandarizado
YYYY-MM-DD. |
hora |
Numérico | Franja horaria extraída del reporte original, en formato 24 horas (0-23). |
tipo_evento / type |
Categórico | Clasificación estandarizada del incidente
(ACCIDENTE, PELIGRO, CONGESTIÓN,
VÍA CERRADA). |
location_x |
Numérico | Coordenada longitudinal cruda (transformada a la
variable long durante el análisis). |
location_y |
Numérico | Coordenada latitudinal cruda (transformada a la
variable lat durante el análisis). |
A continuación, se realiza la carga del archivo de waze y las librerias necesarias para el procesamiento inicial de las variables temporales y categóricas:
library(readxl) # readxl: Para la carga de datos desde archivos Excel.
library(dplyr) # dplyr: Para la manipulación y transformación de datos.
library(lubridate) # lubridate: Para el manejo y procesamiento de fechas y tiempos.
library(leaflet) # leaflet: Para la creación de mapas interactivos.
library(sf) # sf: Para trabajar con datos geoespaciales.
library(mapview) # mapview: Para la visualización interactiva de mapas.
library(spatstat) # spatstat: Para el análisis de patrones espaciales y análisis de densidad.
library(terra) # terra: Para el manejo de datos raster y análisis espacial.
library(leaflet.extras) # leaflet.extras: Para añadir funciones adicionales a los mapas de leaflet
library(ggplot2) # ggplot2: Para la creación de gráficos y visualizaciones.
library(leafsync) # leafsync: Para la sincronización de múltiples mapas interactivos.
Trama_Waze <- read_excel("Trama Waze - Copy (2).xlsx")
Trama_Waze$fecha = as.Date(Trama_Waze$creation_Date, format ="%Y-%m-%d %H:%M")
Trama_Waze$tipo_evento <- recode(Trama_Waze$type,
"ACCIDENT" = "ACCIDENTE",
"HAZARD" = "PELIGRO",
"JAM" = "CONGESTIÓN",
"ROAD_CLOSED" = "VÍA CERRADA")
En esta fase exploratoria, evaluamos el volumen de reportes para identificar los eventos y la prevalencia de cada tipo de incidente.
fecha_hora <- ymd_hms(Trama_Waze$creation_Date)
Trama_Waze$hora <- hour(fecha_hora)
dia <- day(fecha_hora)
hora_factorizado <- factor(Trama_Waze$hora, levels = 0:23)
etiquetas_ampm <- c("12 AM", paste0(1:11, " AM"), "12 PM", paste0(1:11, " PM"))
media_eventos <- nrow(Trama_Waze) / 24
g_hora <- ggplot(data = Trama_Waze, aes(x = hora_factorizado)) +
geom_bar(fill = "#003366", color = "black") + # Azul oscuro
# Agregar la línea horizontal para la media
geom_hline(yintercept = media_eventos, color = "#FFC300", linetype = "dashed", size = 1.2) +
labs(
title = " Ilustración 1. Frecuencia de Eventos por Hora del Día",
subtitle = paste("Línea punteada indica el promedio de eventos por hora:", round(media_eventos, 1)),
x = "Franja Horaria",
y = "Volumen de Reportes (Conteo)"
) +
theme_minimal() +
scale_x_discrete(drop = FALSE, labels = etiquetas_ampm) +
theme(
axis.text.x = element_text(angle = 45, hjust = 1, face = "bold"),
plot.title = element_text(face = "bold", size = 14),
plot.subtitle = element_text(color = "darkgray", face = "italic")
)
g_hora
La Ilustración 1 presenta la ocurrencia de los reportes de movilidad por franja horaria. El pico máximo de eventos se registra a las 5:00 p.m., franja que concentra el 10.1% de los incidentes totales (514 reportes), más del doble de la media de 211 eventos. Esta alta incidencia se mantiene hasta las 8:00 p.m., con un 8.99% de los reportes totales.
Resulta importante destacar un problema en la información: la ausencia de registros entre las 8:00 y las 9:59 a.m. Esto es extraño, dado que esa franja corresponde a un pico de demanda por desplazamientos laborales y académicos. En consecuencia, dicho vacío debe tenerse en cuenta con cautela al interpretar los resultad.
frecuencia_eventos <- Trama_Waze %>%
group_by(tipo_evento) %>%
summarise(Frecuencia = n()) %>%
arrange(desc(Frecuencia))
g_tipo <- ggplot(frecuencia_eventos, aes(x = reorder(tipo_evento, -Frecuencia), y = Frecuencia, fill = tipo_evento)) +
geom_bar(stat = "identity", color = "black", linewidth = 0.5) +
theme_minimal() +
labs(
title = "Ilustración 2. Distribución de Afectaciones en la Red Vial",
x = "Tipo de Evento",
y = "Frecuencia Total"
) +
theme(
axis.text.x = element_text(angle = 15, hjust = 1, face = "bold", size = 11),
plot.title = element_text(face = "bold", size = 14),
legend.position = "none"
) +
scale_fill_brewer(palette = "Blues", direction = -1)
g_tipo
La Ilustración 2 presenta la distribución de ocurrencias en la red vial del municipio de Cajica - Cundinamarca, donde la Congestión lidera el apartado con 3,205 eventos, representando aproximadamente el 63.2% del total Le siguen los Cierres de Vías con 1,021 reportes (20.1%). En tercer lugar se ubican los incidentes de Peligro con 719 casos (14.2%). Finalmente, se registraron 125 Accidentes (2.5%).
Para focalizar el análisis espacial, la base de datos se filtra aislando exclusivamente los reportes de : PELIGRO correspondientes al día 26.
Una vez depurada la muestra se emplea la librería
leaflet para proyectar solo el evento del analisis. La
visualización utiliza un algoritmo de agrupación, lo que permite a los
tomadores de decisiones identificar de manera rápida la zona con mayor
densidad de peligros viales.
pos_peligro <- which(Trama_Waze$tipo_evento == "PELIGRO" & dia == 26)
peligro26 <- Trama_Waze[pos_peligro,]
peligro26$lat <- peligro26$location_y / 10^(nchar(peligro26$location_y) - 1)
peligro26$long <- peligro26$location_x / 10^(nchar(peligro26$location_x) - 3)
peligro26 <- peligro26[peligro26$lat > 4 & peligro26$lat < 5, ]
m26_peligro <- leaflet(peligro26) %>%
addTiles() %>%
addCircleMarkers(
lng = ~long,
lat = ~lat,
clusterOptions = markerClusterOptions(),
label = ~hora
) %>%
addControl(html = "<h3>Mapa de Peligros en la Vía</h3>", position = "topleft")
m26_peligro
La gestión de la red vial requiere identificar las aglomeraciones espaciales o hotspots. En esta sección se identifica la densidad del evento peligro mediante la extensión leaflet.extras y se presenta en un mapa de calor.
La generación de este mapa de calor permite visibilizar la intensidad del evento en el espacio. Al mapear las áreas de alta concentración (zonas rojas), se hacen evidentes los corredores y calles donde el riesgo se acumula. Esta identificación constituye una herramienta clave para la política pública, ya que faculta a las autoridades locales para focalizar los recursos financieros y humanos en aquellas vías donde la probabilidad de algun siniestro es mayor
library(leaflet)
library(dplyr)
library(leaflet.extras)
peligro26 <- peligro26 %>%
filter(lat > 4 & lat < 5, long > -75 & long < -73)
leaflet(peligro26) %>%
addProviderTiles("OpenStreetMap") %>%
addHeatmap(
lng = ~long, lat = ~lat, # Especificar las columnas de longitud y latitud
intensity = ~hora, # Intensidad opcional basada en la hora (o cualquier otra variable)
blur = 20, # Nivel de desenfoque del mapa de calor
max = 0.08, # Ajustar el valor máximo para la intensidad
radius = 15 # Radio de cada punto en el mapa de calor
) %>%
addLegend("bottomright",
title = "Mapa de Calor de Riesgos",
colors = c("blue", "green", "yellow", "red"),
labels = c("Bajo", "Moderado", "Alto", "Muy Alto"))
Para la identificación de hotspots, los eventos de peligro se modelan como un patrón de puntos espaciales. Mediante el paquete spatstat se define una ventana de observación (owin) que delimita el espacio de estudio. Sobre este patrón de puntos espaciales se aplican dos enfoques. El primero es la prueba de cuadrantes, que divide el espacio en una grilla y contrastar el conteo de eventos entre celdas con su valor esperado. El segundo es la función K de Ripley, que permite contrastar la hipótesis de aleatoriedad espacial completa (CSR).
library(spatstat)
zona_estudio <- owin(xrange = c(-74.04331, -73.9929), yrange = c(4.885736, 4.948562))
peligro_ventana <- peligro26 %>%
filter(long >= zona_estudio$xrange[1] & long <= zona_estudio$xrange[2],
lat >= zona_estudio$yrange[1] & lat <= zona_estudio$yrange[2])
patron_peligro <- ppp(x = peligro_ventana$long, y = peligro_ventana$lat, window = zona_estudio)
par(mfrow = c(1, 2))
plot(quadratcount(patron_peligro), main = "Test de Cuadrantes: Peligros")
points(patron_peligro, col = "#F1C40F", pch = 16, cex = 0.6)
plot(Kest(patron_peligro), main = "Función K: Aglomeración de Peligros")
par(mfrow = c(1, 1))
test_csr_peligro <- quadrat.test(patron_peligro)
print(test_csr_peligro)
##
## Chi-squared test of CSR using quadrat counts
##
## data: patron_peligro
## X2 = 459.27, df = 24, p-value < 2.2e-16
## alternative hypothesis: two.sided
##
## Quadrats: 5 by 5 grid of tiles
Interpretación del Análisis de Patrones Espaciales Evento : PELIGRO
Las imágenes presentan la evaluación del comportamiento de los eventos de peligro mediante dos gráficas. En el panel izquierdo, el test de cuadrantes evidencia una fuerte asimetría espacial sobre la grilla de 5×5: mientras la gran mayoría de las áreas registran cero incidencias, celdas específicas operan como focos críticos con valores de conteo de 34, 28 y 25 reportes. Este comportamiento se valida en el panel derecho mediante la estimación de la función K, donde la curva empírica observada se ubica sistemáticamente por encima de la curva teórica, lo que indica un alto grado de aglomeración de los eventos de peligro.
Para complementar la inspección visual, se realizó un test Chi-cuadrado de Aleatoriedad Espacial Completa (CSR) sobre los conteos de la grilla. El contraste arrojó un estadístico de 459.27 con 24 grados de libertad y un p-valor < 2.2e-16. Dado que este valor es inferior al umbral de significancia de 0.05, se rechaza la hipótesis nula de aleatoriedad.
En esta sección, se evalúa la distribución espacial de los eventos tipificados como VÍA CERRADA durante el día 26.
La visualización interactiva inicial permite identificar geográficamente los corredores cerrados.
# Cargar librería necesaria
library(leaflet)
# 1. Filtrar eventos VÍA CERRADA del día 26
pos_cierres <- which(Trama_Waze$tipo_evento == "VÍA CERRADA" & dia == 26)
via_cerrada_26 <- Trama_Waze[pos_cierres,]
# 2. Ajustar las coordenadas de latitud y longitud
via_cerrada_26$lat <- via_cerrada_26$location_y / 10^(nchar(via_cerrada_26$location_y) - 1)
via_cerrada_26$long <- via_cerrada_26$location_x / 10^(nchar(via_cerrada_26$location_x) - 3)
# 3. Filtrar eventos dentro del marco geográfico adecuado
via_cerrada_26 <- via_cerrada_26[via_cerrada_26$lat > 4 & via_cerrada_26$lat < 5,]
# 4. Crear el mapa interactivo
m26_via_cerrada <- leaflet(via_cerrada_26) %>%
addTiles() %>%
addCircleMarkers(
lng = ~long,
lat = ~lat,
clusterOptions = markerClusterOptions(),
label = ~paste("Cierre a las:", hora, "h"),
color = "#E74C3C", # Rojo para indicar restricción/cierre
fillOpacity = 0.7
) %>%
addControl(html = "<h3>Mapa de Cierre de Vías</h3>", position = "topleft")
# Mostrar el mapa interactivo
m26_via_cerrada
Para clasificar si los cierres viales responden a un patron de concentración o a una distribución homogénea. Se modelaron los cierres como un patrón de puntos espaciales (ppp) se presenta el Test de Cuadrantes y la Función K-Estimación.
library(spatstat)
zona_estudio <- owin(xrange = c(-74.04331, -73.9929), yrange = c(4.885736, 4.948562))
cierres_ventana <- via_cerrada_26 %>%
filter(long >= zona_estudio$xrange[1] & long <= zona_estudio$xrange[2],
lat >= zona_estudio$yrange[1] & lat <= zona_estudio$yrange[2])
patron_via_cerrada <- ppp(x = cierres_ventana$long, y = cierres_ventana$lat, window = zona_estudio)
par(mfrow = c(1, 2))
plot(quadratcount(patron_via_cerrada), main = "Test de Cuadrantes: Cierres")
points(patron_via_cerrada, col = "#E74C3C", pch = 16, cex = 0.6)
plot(Kest(patron_via_cerrada), main = "Función K: Aglomeración de Cierres")
par(mfrow = c(1, 1)) # Restaurar la pantalla
test_csr_cierres <- quadrat.test(patron_via_cerrada)
print(test_csr_cierres)
##
## Chi-squared test of CSR using quadrat counts
##
## data: patron_via_cerrada
## X2 = 14752, df = 24, p-value < 2.2e-16
## alternative hypothesis: two.sided
##
## Quadrats: 5 by 5 grid of tiles
La evaluación espacial revela una concentración del evento cierre vial. En el panel izquierdo, el Test de Cuadrantes presenta una alta asimetria, la inmensa mayoría del municipio de estudio registra cero cierres, mientras que una única celda concentra un pico de 636 eventos.La Función K-Estimación se mantiene muy por encima de la curva teórica (Proceso de Poisson), lo que indica que las interrupciones se agrupan en una zona en particular
Al validar estos patrones visuales mediante el Test Chi-cuadrado de Pearson, se obtiene un estadístico de 14752 con 24 grados de libertad y un p-valor < 2.2e-16. Al contrastar este p-valor con el umbral del 0.05, se concluyeque los cierres de vías presentan un patrón de aglomeración.
# Crear un patrón de puntos espaciales utilizando los datos correctos (via_cerrada_26)
zona <- owin(xrange = c(-74.04331, -73.9929), yrange = c(4.885736, 4.948562))
patron_via_cerrada <- ppp(x = via_cerrada_26$long, y = via_cerrada_26$lat, window = zona)
# Calcular la densidad espacial
im1 <- density(patron_via_cerrada, sigma = 0.01) # Ajusta sigma según sea necesario
# Convertir la densidad a un objeto raster usando terra
mapa_via_cerrada <- rast(im1)
# Convertir el raster a data.frame para leaflet
df_via_cerrada <- as.data.frame(mapa_via_cerrada, xy = TRUE)
colnames(df_via_cerrada) <- c("long", "lat", "intensity")
# Normalizar los valores de intensidad entre 0 y 1
df_via_cerrada$intensity <- (df_via_cerrada$intensity - min(df_via_cerrada$intensity)) /
(max(df_via_cerrada$intensity) - min(df_via_cerrada$intensity))
# Crear un mapa interactivo usando leaflet
leaflet(df_via_cerrada) %>%
addProviderTiles("OpenStreetMap") %>% # Añadir la capa base
addHeatmap(
lng = ~long, lat = ~lat, # Coordenadas de longitud y latitud
intensity = ~intensity, # Intensidad normalizada
blur = 20, # Nivel de desenfoque
max = 1, # Valor máximo de la intensidad normalizada
radius = 15 # Radio para reflejar la densidad
) %>%
addLegend("bottomright", # Añadir la leyenda
title = "Mapa de Calor de Cierres de Vías",
colors = c("blue", "green", "yellow", "red"),
labels = c("Bajo", "Moderado", "Alto", "Muy Alto"))
Finalmente, el mapa de calor de cierres viales presenta los resultados de las pruebas efectuadas. A diferencia del evento (PELIGRO) que presenta una dispersión más difusa en el territorio, esta visualización revela la existencia de un único y masivo hotspot.
En esta sección, se evalúa la distribución espacial de los accidentes
ocurridos el día 26 utilizando las librerías spatstat y
terra .El proposito de esta evaluación es determinar si las
colisiones se distribuyen de forma aleatoria (Proceso de Poisson) o si
exhiben patrones de aglomeración.
Identificar de manera eficiente hotspots de siniestralidad vial es indispenable para la formulación de políticas públicas de mitigación de dicho evento.
# Filtrar eventos de accidentes del día 26
pos <- which(Trama_Waze$tipo_evento == "ACCIDENTE" & dia == 26)
accidente_26 <- Trama_Waze[pos,]
# Ajustar las coordenadas de latitud y longitud
accidente_26$lat <- accidente_26$location_y / 10^(nchar(accidente_26$location_y) - 1)
accidente_26$long <- accidente_26$location_x / 10^(nchar(accidente_26$location_x) - 3)
# Filtrar eventos dentro del rango geográfico adecuado
accidente_26 <- accidente_26[accidente_26$lat > 4 & accidente_26$lat < 5,]
# Cargar librerías necesarias
library(leaflet)
# Crear el mapa interactivo
m26_accidente <- leaflet(accidente_26) %>%
addTiles() %>%
addCircleMarkers(lng = ~long, lat = ~lat,
clusterOptions = markerClusterOptions(),
label = ~hora) %>%
addControl(html = "<h3>Mapa de Accidentes</h3>", position = "topleft")
# Mostrar el mapa interactivo
m26_accidente
# Cargar las librerías necesarias
library(spatstat)
# 1. Definir la zona de interés estandarizada (ventana de observación)
zona <- owin(xrange = c(-74.04331, -73.9929), yrange = c(4.885736, 4.948562))
# 2. Filtrar los puntos de accidentes que caen en la ventana de estudio
acc_ventana <- accidente_26 %>%
filter(long >= zona$xrange[1] & long <= zona$xrange[2],
lat >= zona$yrange[1] & lat <= zona$yrange[2])
# 3. Crear el patrón de puntos espaciales (objeto ppp)
patron_accidente <- ppp(x = acc_ventana$long, y = acc_ventana$lat, window = zona)
# 4. Gráfico combinado: Test de Cuadrantes y Función K-Estimación
par(mfrow = c(1, 2)) # Dividir la ventana gráfica en 2 paneles
# Panel izquierdo: Graficar el test de cuadrantes
plot(quadratcount(patron_accidente), main = "Cuadrantes: Accidentes")
points(patron_accidente, col = "#8E44AD", pch = 16, cex = 0.8) # Puntos púrpura
# Panel derecho: Calcular y graficar la función K-estimación
plot(Kest(patron_accidente), main = "Función K: Aglomeración")
par(mfrow = c(1, 1)) # Restaurar la ventana gráfica a 1 panel
# 5. Ejecutar el Test Chi-cuadrado para contrastar la aleatoriedad
test_csr_accidente <- quadrat.test(patron_accidente)
print(test_csr_accidente)
##
## Chi-squared test of CSR using quadrat counts
##
## data: patron_accidente
## X2 = 769.56, df = 24, p-value < 2.2e-16
## alternative hypothesis: two.sided
##
## Quadrats: 5 by 5 grid of tiles
El análisis de la siniestralidad devela un comportamiento asimetrico en la distribución de los accidentes. En el panel izquierdo, el Test de Cuadrantes presenta una altisima concentración, mientras la totalidad de la grilla territorial permanece sin incidentes, una única celda (ubicada en el sector inferior izquierdo) absorbe un pico crítico de 34 accidentes, acompañada marginalmente por otra celda con 2 eventos. Esta aglomeración se corrobora en el panel derecho mediante la Función K-Estimación, donde la curva empírica por elcina de la curva de aleatoriedad.
Tras ejecutar el Test Chi-cuadrado de Aleatoriedad Espacial Completa (CSR), se obtiene un estadístico de 769.56 con 24 grados de libertad y un p-valor < 2.2e-16. Se rechaza la hipótesis nula. Lo que indica estadísticamente que los accidentes de tránsito se aglomeran en un punto en particular.
# Cargar las librerías necesarias
library(terra)
library(leaflet)
library(spatstat)
# Asegurarse de que el objeto patron_accidente esté correctamente definido
# Usar las coordenadas correctas de los accidentes
zona <- owin(xrange = c(-74.04331, -73.9929), yrange = c(4.885736, 4.948562))
patron_accidente <- ppp(x = accidente_26$long, y = accidente_26$lat, window = zona)
# Calcular la densidad espacial
im1 <- density(patron_accidente)
# Convertir la densidad a un objeto raster usando terra
mapa_accidente <- rast(im1)
# Convertir el raster a data.frame para leaflet
df_accidente <- as.data.frame(mapa_accidente, xy = TRUE)
colnames(df_accidente) <- c("long", "lat", "intensity")
# Normalizar los valores de intensidad entre 0 y 1
df_accidente$intensity <- (df_accidente$intensity - min(df_accidente$intensity)) /
(max(df_accidente$intensity) - min(df_accidente$intensity))
# Crear un mapa interactivo usando leaflet
leaflet(df_accidente) %>%
addProviderTiles("OpenStreetMap") %>% # Añadir la capa base
addHeatmap(
lng = ~long, lat = ~lat, # Coordenadas de longitud y latitud
intensity = ~intensity, # Intensidad normalizada
blur = 15, # Nivel de desenfoque
max = 0.5, # Valor máximo de la intensidad normalizada
radius = 10 # Ajustar el radio de los puntos
) %>%
addLegend("bottomright", # Añadir la leyenda para interpretar el mapa de calor
title = "Mapa de Calor de Accidentes",
colors = c("blue", "green", "yellow", "red"),
labels = c("Bajo", "Moderado", "Alto", "Muy Alto"))
# Filtrar eventos de congestión del día 26
pos <- which(Trama_Waze$tipo_evento == "CONGESTIÓN" & dia == 26)
congestion_26 <- Trama_Waze[pos,]
# Ajustar las coordenadas de latitud y longitud
congestion_26$lat <- congestion_26$location_y / 10^(nchar(congestion_26$location_y) - 1)
congestion_26$long <- congestion_26$location_x / 10^(nchar(congestion_26$location_x) - 3)
# Filtrar eventos dentro del rango geográfico adecuado
congestion_26 <- congestion_26[congestion_26$lat > 4 & congestion_26$lat < 5,]
# Cargar las librerías necesarias
library(leaflet)
# Crear el mapa interactivo
m26_congestion <- leaflet(congestion_26) %>%
addTiles() %>%
addCircleMarkers(lng = ~long, lat = ~lat,
clusterOptions = markerClusterOptions(),
label = ~hora) %>%
addControl(html = "<h3>Mapa de Congestión</h3>", position = "topleft")
# Mostrar el mapa interactivo
m26_congestion
La congestión representa la falla de mercado más prevalente en la economía de los bienes publicos. La congestión es una externalidad generada por el exceso de demanda sobre la capacidad instalada.
En esta sección, se modela la distribución espacial de los incidentes tipificados como CONGESTIÓN durante el día 26. Identificando si el evento obedece a un patrón sistémico (clústeres) o si se distribuye homogéneamente, resulta indispensable para que las autoridades puedan estructurar medidas para su mitigación.
# Cargar las librerías necesarias
library(spatstat)
library(dplyr)
# 1. Filtrar eventos CONGESTIÓN del día 26 y ajustar coordenadas
pos_cong <- which(Trama_Waze$tipo_evento == "CONGESTIÓN" & dia == 26)
congestion_26 <- Trama_Waze[pos_cong,]
congestion_26$lat <- congestion_26$location_y / 10^(nchar(congestion_26$location_y) - 1)
congestion_26$long <- congestion_26$location_x / 10^(nchar(congestion_26$location_x) - 3)
# 2. Definir la zona de interés estandarizada (ventana de observación)
zona <- owin(xrange = c(-74.04331, -73.9929), yrange = c(4.885736, 4.948562))
# 3. Filtrar los puntos de congestión que caen en la ventana de estudio
cong_ventana <- congestion_26 %>%
filter(long >= zona$xrange[1] & long <= zona$xrange[2],
lat >= zona$yrange[1] & lat <= zona$yrange[2])
# 4. Crear el patrón de puntos espaciales (objeto ppp)
patron_congestion <- ppp(x = cong_ventana$long, y = cong_ventana$lat, window = zona)
# 5. Gráfico combinado: Test de Cuadrantes y Función K-Estimación
par(mfrow = c(1, 2)) # Dividir la ventana gráfica en 2 paneles
# Panel izquierdo: Graficar el test de cuadrantes
plot(quadratcount(patron_congestion), main = "Cuadrantes: Congestión")
points(patron_congestion, col = "#D35400", pch = 16, cex = 0.5) # Naranja oscuro para contraste
# Panel derecho: Calcular y graficar la función K-estimación
plot(Kest(patron_congestion), main = "Función K: Aglomeración")
par(mfrow = c(1, 1))
# 6. Ejecutar el Test Chi-cuadrado para contrastar la aleatoriedad
test_csr_congestion <- quadrat.test(patron_congestion)
print(test_csr_congestion)
##
## Chi-squared test of CSR using quadrat counts
##
## data: patron_congestion
## X2 = 2612.9, df = 24, p-value < 2.2e-16
## alternative hypothesis: two.sided
##
## Quadrats: 5 by 5 grid of tiles
El análisis geoestadístico de la congestión revela una estructura diferente a la de los accidentes o cierres viales. Visualmente, el Test de Cuadrantes expone una distribución espacial más amplia, pero igualmente asimetrica. En lugar de un único grupo en un apartado, la congestión vehicular conforma una red de cuadrantes criticas con eventos de 223, 201, 120 y 94 reportes por celda, mientras otras celdas presentan valores nulos o minimos. Esta estructura se confirma en el panel derecho con la Función K-Estimación, donde la curva empírica se situa por encima de la curva teórica de aleatoriedad.
Tras ejecutar el Test Chi-cuadrado de aleatoriedad espacial completa (CSR), se obtiene un estadístico de 2612.9 con 24 grados de libertad y un p-valor < 2.2e-16. Al evaluar este resultado frente al umbral de significancia del 0.05, se rechaza la hipótesis nula. Esto confirma que los eventos de congestión presentan un patrón de aglomeración.
# Cargar las librerías necesarias
library(leaflet)
library(terra)
# Definir el patrón de puntos
zona <- owin(xrange = c(-74.04331, -73.9929), yrange = c(4.885736, 4.948562))
patron_congestion <- ppp(x = congestion_26$long, y = congestion_26$lat, window = zona)
# Calcular la densidad espacial del patrón de puntos
im1 <- density(patron_congestion)
# Convertir la densidad en un raster utilizando terra
mapa_congestion <- rast(im1)
# Convertir el objeto raster a un data.frame para usarlo en leaflet
df_congestion <- as.data.frame(mapa_congestion, xy = TRUE)
colnames(df_congestion) <- c("long", "lat", "intensity")
# Normalizar los valores de intensidad entre 0 y 1
df_congestion$intensity <- (df_congestion$intensity - min(df_congestion$intensity)) /
(max(df_congestion$intensity) - min(df_congestion$intensity))
# Crear un mapa interactivo usando leaflet
leaflet(df_congestion) %>%
addProviderTiles("OpenStreetMap") %>% # Añadir la capa base
addHeatmap(
lng = ~long, lat = ~lat, # Coordenadas de longitud y latitud
intensity = ~intensity, # Intensidad normalizada
blur = 35, # Incrementar el desenfoque para suavizar el mapa
max = max(df_congestion$intensity) * 2, # Ajustar el valor máximo de intensidad
radius = 25 # Aumentar el radio para que se vea más suave
) %>%
addLegend("bottomright", # Añadir la leyenda para interpretar el mapa de calor
title = "Mapa de Calor de Congestión",
colors = c("blue", "green", "yellow", "red"),
labels = c("Bajo", "Moderado", "Alto", "Muy Alto"))
Finalmente, el mapa de calor confirma visualmente la estructura de aglomeración espacial en sectores estratégicos del municipio. Los reportes se concentran linealmente sobre las principales arterias de la red vial de Cajicá, tales como la vía Tabio - Cajicá, la Carrera 6 y la Variante de Cajicá, así como en los nodos viales e intersecciones que conectan con estos corredores de alta demanda.
En esta sección de cierre se implementa, mediante la librería leafsync, una sincronización de los cuatro eventos planteados. El propósito es consolidar en una única visualización las cuatro dimensiones evaluadas: congestión, accidentes, peligros y cierres viales. Esta representación permite identificar, a primera vista, posibles relaciones entre los eventos, si bien establecer la existencia de una correlación requiere un análisis posterior más riguroso.
leafsync::sync(m26_peligro, m26_accidente, m26_congestion, m26_via_cerrada)
El análisis espacial y estadístico de los datos crowdsourcing de Waze para el municipio de Cajicá permite extraer conclusiones fundamentales para la planificación urbana y la gestión de la movilidad:
6.2. Limitaciones Metodológicas:
Para garantizar la rigurosidad del estudio, es imperativo reconocer las limitaciones inherentes al alcance de los datos y las herramientas empleadas, las cuales trazan la hoja de ruta para futuras investigaciones:
Restricción de la Ventana Temporal (Choques Transitorios vs. Estructurales): El análisis se encuentra delimitado a los eventos reportados durante un único día (el día 26). Desde la perspectiva de la modelación de transporte, un corte transversal tan reducido impide aislar efectos estacionales o eventos atípicos momentáneos (como un cierre vial por mantenimiento menor o un accidente) de los verdaderos problemas sistémicos de la red vial donde se producen estos en realidad de forma sistemática.
Efectos del Calendario (Variabilidad Interdiaria): La movilidad urbana exhibe una alta sensibilidad al día de la semana. Los patrones de flujo, origen-destino y congestión de un lunes o un viernes difieren radicalmente de las dinámicas comerciales o turísticas de un sábado o un domingo o un puente cuando es un corredor vial de salida de los bogotanos. Limitar el análisis a una sola jornada impide capturar estas fluctuaciones.
El Supuesto del Espacio Plano : La utilización de patrones de puntos espaciales planos (ppp) y ventanas rectangulares (owin) asume una geografía euclidiana plana y continua. En la práctica, la movilidad está estrictamente confinada a una red vial. Esto introduce un sesgo dado que dos incidentes pueden aparecer geográficamente muy cercanos en línea recta dentro de un cuadrante, pero estar completamente aislados en términos de conectividad.