##Clase 3: descubriendo patrones temporales y espaciales –> lubridate
library(tidyverse)
library(ggmap)
library(lubridate)
today() –> da la fecha en la que estamos now () –> da todos los datos de ahora –> formato posix: para almacenar hora y fecha juntos with_tz() –> para elegir otros husos horarios OlsonNames() –> para buscar otros husos wday() –> qué día de la semana fue tal fecha específica La respuesta es “2”. Sin embargo, esto no me da demasiada información, ya que no tengo porque saber que significa ese “2”. Para averiguarlo podemos pedirle a R que nos agregue una etiqueta con el nombre del día:
wday(“2024-01-01”, label = TRUE)
Ahora si, parecería que “2” es el orden de los días y que significa Monday (inglés) o Lunes (español).
Por último, si queremos que los nombres de los días no estén abreviados y se muestren en Español, podemos agregar 2 parámetros:
abbr=FALSE para evitar abreviaturas.
locale=“es_ES.UTF-8” para pasar al idioma español y ajustar el encoding.
wday(“2024-01-01”, label=TRUE, abbr=FALSE, locale=“es_ES.UTF-8”)
month() –> en qué mes fue o será alguna fecha específica
También podemos hacer operaciones/cálculos con lubridate. Por ejemplo, podríamos preguntarnos qué fecha fue hace 10 días:
today()-10
O qué fecha será dentro de 75 días:
today()+75
Imaginen poder aplicar todas estas funciones sobre un dataset que contenga variables temporales. Esto nos permitiría realizar múltiples operaciones y transformaciones para analizar la evolución de los datos en el tiempo, detectar patrones, comparar períodos y generar insights valiosos a partir de la dimensión temporal.
cdmx_transito <- read.csv("data/hechos-transito-cdmx-2024.csv", stringsAsFactors = TRUE)
dim(cdmx_transito)
## [1] 30650 6
names(cdmx_transito)
## [1] "folio" "fecha_evento" "hora_evento" "tipo_evento" "longitud"
## [6] "latitud"
Las 6 columnas que tiene el dataset corresponden a número de folio, fecha y hora en la que ocurrió el evento, tipo de evento y ubicación exacta (longitud y latitud).
Como verán, ¡Tenemos algunas columnas con información geográfica (coordenadas) y otras con datos temporales (fecha y hora)! Y esto resulta más que suficiente para poder encontrar patrones en los datos.
summary(cdmx_transito)
## folio fecha_evento hora_evento
## C5/20240619/04624: 1 2024-04-19: 134 09:02 : 53
## C2C/20240102/00031: 1 2024-02-02: 118 08:54 : 49
## C2C/20240104/00056: 1 2024-02-03: 114 08:03 : 47
## C2C/20240104/00171: 1 2024-10-10: 114 09:10 : 45
## C2C/20240105/00209: 1 2024-02-10: 112 08:25 : 44
## C2C/20240108/00100: 1 2024-06-29: 111 08:57 : 43
## (Other) :30644 (Other) :29947 (Other):30369
## tipo_evento longitud latitud
## ATROPELLADO : 4822 Min. :-99.35 Min. :19.10
## CAIDA DE CICLISTA: 761 1st Qu.:-99.17 1st Qu.:19.34
## CAIDA DE PASAJERO: 611 Median :-99.14 Median :19.40
## CHOQUE :18109 Mean :-99.14 Mean :19.39
## DERRAPADO : 5866 3rd Qu.:-99.10 3rd Qu.:19.44
## VOLCADURA : 481 Max. :-98.95 Max. :19.58
##
Podemos ver que:
El día con mayor cantidad de registros del año 2024 fue el 19/04/2024, con un total de 134.
La hora en la que más delitos se registraron en el año fue a las 09:02, con un total de 53.
El tipo de evento más registrado durante el período analizado fue “CHOQUE”.
Comencemos a trabajar con los datos temporales y revisemos que formato tiene la variable “fecha_evento”.
str(cdmx_transito$fecha_evento)
## Factor w/ 366 levels "2024-01-01","2024-01-02",..: 1 1 1 1 1 1 1 1 1 1 ...
La variable está en formato factor pero nosotros queremos que esté en date entonces será necesario primero verificar el modo en que está escrita y luego hacer un mutate() y agregarle la función de lubridate correspondiente
cdmx_transito <- cdmx_transito %>%
mutate(fecha_evento=ymd(fecha_evento))
Y volvemos a corroborar la estructura del dataset
str(cdmx_transito$fecha_evento)
## Date[1:30650], format: "2024-01-01" "2024-01-01" "2024-01-01" "2024-01-01" "2024-01-01" ...
cdmx_transito <- cdmx_transito %>%
mutate(mes=month(fecha_evento, label = TRUE, abbr=FALSE, locale="es_ES.UTF-8"),
dia_semana=wday(fecha_evento, label=TRUE, abbr=FALSE, locale="es_ES.UTF-8"))
summary(cdmx_transito)
## folio fecha_evento hora_evento
## C5/20240619/04624: 1 Min. :2024-01-01 09:02 : 53
## C2C/20240102/00031: 1 1st Qu.:2024-03-29 08:54 : 49
## C2C/20240104/00056: 1 Median :2024-06-27 08:03 : 47
## C2C/20240104/00171: 1 Mean :2024-06-30 09:10 : 45
## C2C/20240105/00209: 1 3rd Qu.:2024-10-04 08:25 : 44
## C2C/20240108/00100: 1 Max. :2024-12-31 08:57 : 43
## (Other) :30644 (Other):30369
## tipo_evento longitud latitud mes
## ATROPELLADO : 4822 Min. :-99.35 Min. :19.10 noviembre: 2703
## CAIDA DE CICLISTA: 761 1st Qu.:-99.17 1st Qu.:19.34 octubre : 2652
## CAIDA DE PASAJERO: 611 Median :-99.14 Median :19.40 mayo : 2648
## CHOQUE :18109 Mean :-99.14 Mean :19.39 marzo : 2632
## DERRAPADO : 5866 3rd Qu.:-99.10 3rd Qu.:19.44 febrero : 2631
## VOLCADURA : 481 Max. :-98.95 Max. :19.58 junio : 2586
## (Other) :14798
## dia_semana
## domingo :4442
## lunes :4034
## martes :4289
## miércoles:4131
## jueves :4453
## viernes :4592
## sábado :4709
Observamos que el mes con mayores ocurrencias es novimebre, con 2703, y el día de la semana el sábado con 4709. Veremos la evolución en un gráfico
ggplot() +
geom_bar(data= cdmx_transito, aes(x = mes))
a <- cdmx_transito %>%
group_by(mes, tipo_evento) %>%
summarise(cantidad=n())
Y ahora armamos el nuevo gráfico
ggplot() +
geom_bar(data=cdmx_transito, aes(x = mes, fill = tipo_evento), position="dodge")+
labs(title="Evolución mensual de hechos de tránsito",
subtitle="2024, Ciudad de México",
fill="Tipo",
x="Mes",
y="Cantidad")+
theme_minimal()
En el gráfico se observa que las distintas categorías de hechos de tránsito no presentan sus picos ni sus mínimos en los mismos meses. Cada tipo de incidente muestra su propia estacionalidad. A continuación, se detallan los meses con la mayor cantidad de casos para cada tipo de evento.
cdmx_transito %>%
group_by(tipo_evento, mes) %>%
summarise(cantidad=n()) %>%
filter(cantidad==max(cantidad)) %>%
arrange((desc(cantidad)))
## # A tibble: 6 × 3
## # Groups: tipo_evento [6]
## tipo_evento mes cantidad
## <fct> <ord> <int>
## 1 CHOQUE noviembre 1600
## 2 DERRAPADO diciembre 546
## 3 ATROPELLADO febrero 430
## 4 CAIDA DE CICLISTA marzo 87
## 5 CAIDA DE PASAJERO noviembre 62
## 6 VOLCADURA noviembre 52
Al observar los meses con mayor cantidad de hechos según el tipo de evento, se destacan los choques, con un pico en noviembre que supera los 1.600 casos. Ese mismo mes también concentra el mayor número de caídas de pasajero (62) y volcaduras (52), lo que sugiere una posible combinación de factores estacionales o contextuales que incrementan los siniestros. En cuanto a los atropellos, el valor más alto se da en febrero (430), mientras que las caídas de ciclistas alcanzan su máximo en marzo (87). Finalmente, los derrapes registran su mayor cantidad en diciembre (546).
Mejoremos el gráfico
ggplot() +
geom_bar(data = cdmx_transito, aes(x = mes, fill = str_to_title(tipo_evento)), position = "dodge") +
labs(title = "Evolución mensual de hechos de tránsito",
subtitle = "2024, Ciudad de México",
fill = "Tipo",
x = "Mes",
y = "Cantidad") +
scale_fill_manual(values = c("#f94144", "#f8961e", "#f9c74f", "#43aa8b", "#577590", "#277da1")) +
theme_minimal(base_family = "Tahoma") +
theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust = 1))
cdmx_transito_dia <- cdmx_transito %>%
group_by(fecha_evento) %>%
summarise(cantidad=n())
Revisamos los datos
head(cdmx_transito_dia)
## # A tibble: 6 × 2
## fecha_evento cantidad
## <date> <int>
## 1 2024-01-01 43
## 2 2024-01-02 64
## 3 2024-01-03 71
## 4 2024-01-04 80
## 5 2024-01-05 88
## 6 2024-01-06 88
summary(cdmx_transito_dia)
## fecha_evento cantidad
## Min. :2024-01-01 Min. : 43.00
## 1st Qu.:2024-04-01 1st Qu.: 77.00
## Median :2024-07-01 Median : 83.00
## Mean :2024-07-01 Mean : 83.74
## 3rd Qu.:2024-09-30 3rd Qu.: 91.00
## Max. :2024-12-31 Max. :134.00
En promedio, durante el período analizado se registraron 83 hechos de tránsito por día. Para visualizar cómo evolucionó esta cifra a lo largo del tiempo y detectar posibles picos o caídas, a continuación presentamos un gráfico de líneas con la cantidad diaria de incidentes y el promedio calculado.
ggplot() +
geom_line(data=cdmx_transito_dia, aes(x = fecha_evento, y = cantidad))+
geom_hline(yintercept = 83.74, color = "red")+
labs(title = "Evolución diaria de hechos de tránsito",
subtitle = "2024, Ciudad de México",
x = "Día",
y = "Cantidad") +
theme_minimal(base_family = "Tahoma")
Datos filtrados al mes de diciembre para mejor análisis
ggplot() +
geom_line(data = cdmx_transito_dia %>%
filter(month(fecha_evento)==12), aes(x = fecha_evento, y = cantidad)) +
labs(title = "Evolución diaria de hechos de tránsito",
subtitle = "Diciembre 2024, Ciudad de México",
x = "Día",
y = "Cantidad") +
scale_x_date(date_breaks = "1 day", date_labels = "%d-%m") +
theme_minimal(base_family = "Tahoma") +
theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust = 1))
Ahora vamos con enero
ggplot() +
geom_line(data = cdmx_transito_dia %>%
filter(month(fecha_evento)==01), aes(x = fecha_evento, y = cantidad)) +
labs(title = "Evolución diaria de hechos de tránsito",
subtitle = "Enero 2024, Ciudad de México",
x = "Día",
y = "Cantidad") +
scale_x_date(date_breaks = "1 day", date_labels = "%d-%m") +
theme_minimal(base_family = "Tahoma") +
theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust = 1))
11) Aprovechemos que contamos con la variable categórica “tipo_evento”
para agregar color al gráfico anterior y así visualizar cómo varía cada
tipo de hecho a lo largo del tiempo. Esto puede ayudarnos a identificar
patrones específicos según el tipo de incidente.
Para hacerlo, necesitamos agrupar los datos no solo por día (“fecha_evento”), sino también por “tipo_evento”. En lugar de generar un nuevo objeto intermedio, realizaremos estas operaciones directamente dentro del geom_line() utilizando group_by() y summarise().
ggplot() +
geom_line(data = cdmx_transito %>%
group_by(fecha_evento, tipo_evento) %>%
summarise(cantidad=n()),
aes(x = fecha_evento, y = cantidad, color = str_to_title(tipo_evento)))+
labs(title = "Evolución diaria de hechos de tránsito por tipología",
subtitle = "2024, Ciudad de México",
x = "Día",
y = "Cantidad",
color = "Tipo") +
scale_color_manual(values = c("#f94144", "#f8961e", "#f9c74f", "#43aa8b", "#577590", "#277da1")) +
theme_minimal(base_family = "Tahoma")
Ajustes estéticos
ggplot() +
geom_line(data = cdmx_transito %>%
group_by(fecha_evento, tipo_evento) %>%
summarise(cantidad=n()),
aes(x = fecha_evento, y = cantidad, color = str_to_title(tipo_evento)))+
labs(title = "Evolución diaria de hechos de tránsito por tipología",
subtitle = "2024, Ciudad de México",
x = "Día",
y = "Cantidad",
color = "Tipo") +
scale_color_manual(values = c("#f94144", "#f8961e", "#f9c74f", "#43aa8b", "#577590", "#277da1")) +
scale_x_date(date_breaks = "1 month", date_labels = "%d-%m")+
theme_minimal(base_family = "Tahoma")+
theme(legend.position="bottom",
legend.justification = "center",
title=element_text(size=10, face = "bold"),
legend.text=element_text(size=7),
axis.title.x = element_text(size = 8),
axis.title.y = element_text(size = 8),
axis.text.y = element_text(size = 7),
axis.text.x = element_text(size = 7, angle = 90),
plot.caption=element_text(face = "italic", colour = "gray35",size=7))
Ahora aprovechemos que ya agregamos el día de la semana (dia_semana) en nuestro dataset original y trabajemos con esta información.
ggplot()+
geom_bar(data = cdmx_transito, aes(x = dia_semana), fill="#778da9")+
theme_minimal()
Para profundizar este análisis, vamos a aprovechar la variable “mes” y
generar un gráfico de barras facetado, que nos permita ver con mayor
detalle cómo se distribuyen los hechos de tránsito según el mes y día de
la semana.
ggplot() +
geom_bar(data = cdmx_transito, aes(x = dia_semana), fill="#778da9")+
labs(title="Cantidad de hechos de tránsito por día de la semana",
subtitle="Ciudad de México, 2024",
fill="Tipología",
x="Día de la semana",
y="Cantidad") +
facet_wrap(~mes, ncol= 4)+
theme_minimal()+
theme(axis.text.x = element_text(size = 7, angle = 90))
Por último, vamos a incorporar la variable “tipo_evento” y colorear las
barras del gráfico para visualizar con mayor nivel de detalle cómo se
distribuyen los distintos tipos de hechos de tránsito según el mes y el
día de la semana. Esta visualización nos permitirá identificar si
ciertos eventos, como atropellos o volcaduras, tienden a concentrarse en
días o meses específicos, y si siguen o no el patrón general
observado.
ggplot(cdmx_transito) +
geom_bar(aes(x = dia_semana, fill=str_to_title(tipo_evento)))+
labs(title="Cantidad de hechos de tránsito por día y tipo",
subtitle="Ciudad de México, 2019-2020",
fill="Tipología",
x="Día de la semana",
y="Cantidad") +
scale_fill_manual(values = c("#f94144", "#f8961e", "#f9c74f", "#43aa8b", "#577590", "#277da1")) +
facet_wrap(~mes, ncol= 4) +
theme_minimal() +
theme(axis.text.x = element_text(size = 7, angle = 90))
#Análisis espacial
Hasta acá, detectamos varios patrones temporales muy interesantes en nuestros datos, pero todavía nos queda ver una “dimensión” muy relevante a la hora de analizar este tipo de información: la ubicación en el espacio.
Comencemos viendo como se ubican todos los datos en el territorio.
ggplot()+
geom_point(data = cdmx_transito, aes(x= longitud, y= latitud))
Como vimos la clase pasada, alguien ya se cruzó con esta pregunta y
diseñó una librería que nos soluciona el problema y se llama ggmap. Ya
la hemos activado al inicio de la clase, así que el siguiente paso, al
igual que en la clase anterior, es determinar cuál es la “bounding box”
o caja delimitadora de coordenadas de mi dataset. Pero nótese que esta
vez no tengo un dato geoespacial para utlizar st_bbox(), y por
lo tanto aplicaremos la función make_bbox() sobre las
coordenadas de las columnas “longitud” y “latitud” del dataset
original:
cdmx_bbox <- make_bbox(cdmx_transito$longitud, cdmx_transito$latitud)
cdmx_bbox
## left bottom right top
## -99.36838 19.07799 -98.92832 19.60033
Bien, ya tengo los 4 valores que delimitan nuestra bounding box, ahora vamos a usar get_stadiamap() para descargar de internet el “mapa base”. En este caso utilizaremos un mapa de tipo “alidade_smooth”.
register_stadiamaps("96e8da08-2cda-466f-beeb-f3517aa6c989", write = TRUE)
## ℹ Replacing old key (96e8da08) with new key in /home/tormenta/.Renviron
mapa_base <- get_stadiamap(bbox = cdmx_bbox,
maptype = "alidade_smooth",
zoom = 11)
ggmap(mapa_base)
Ahora vamos a ver nuestros datos en este mapa base
ggmap(mapa_base)+
geom_point(data=cdmx_transito, aes(x=longitud, y=latitud, color=tipo_evento))
Vamos a hacer un facetado para que se vea mejor la distribución
ggmap(mapa_base)+
geom_point(data=cdmx_transito, aes(x=longitud, y=latitud, color=tipo_evento))+
facet_wrap(~tipo_evento)
Por suerte tenemos una solución dentro del mismísimo ggplot()
que es lo que llamamos Mapas de Densidad y podemos realizarlos a partir
de geom_bin2d(). Veamos de que se trata.
ggmap(mapa_base) +
geom_bin2d(data = cdmx_transito,
aes(x = longitud, y = latitud))
Vamos a mejorarlo agregando más binds y otra escala de color
ggmap(mapa_base) +
geom_bin2d(data = cdmx_transito,
aes(x = longitud, y = latitud), bins=50)+
scale_fill_viridis_c(direction=-1)
Ahora lo mismo pero facetado por tipo de evento
ggmap(mapa_base) +
geom_bin2d(data = cdmx_transito,
aes(x = longitud, y = latitud), bins=50)+
scale_fill_viridis_c(direction=-1)+
facet_wrap(~tipo_evento)
#Análisis temporal y espacial
Hasta acá vimos por un lado el análisis temporal y por el otro, el análisis espacial. Ahora veamos ambos análisis juntos y busquemos por ejemplo, un patrón espacial según el día de la semana.
ggmap(mapa_base) +
geom_bin2d(data = cdmx_transito,
aes(x = longitud, y = latitud), bins=50, show.legend = FALSE)+
labs(title="Densidad de hechos de tránsito en 2024",
subtitle="Ciudad de México")+
scale_fill_viridis_c(direction=-1)+
facet_wrap(~dia_semana, ncol=4)+
theme_void()
Según el mes
ggmap(mapa_base) +
geom_bin2d(data = cdmx_transito,
aes(x = longitud, y = latitud), bins=50, show.legend=FALSE)+
labs(title="Densidad de hechos de tránsito en 2020",
subtitle="Ciudad de México")+
scale_fill_viridis_c(direction=-1)+
facet_wrap(~mes, ncol=6)+
theme_void()
Otra función muy útil para este tipo de visualizaciones espaciales es stat_density_2d(), que genera curvas de densidad (contornos) a partir de un conjunto de puntos georreferenciados, utilizando un proceso llamado estimación de densidad por núcleo (Kernel Density Estimation, KDE).
Lo que hace, en términos simples, es estimar cuántos puntos hay en cada zona del espacio, no solo contando puntos exactos, sino suavizando esa información en el entorno de cada uno. Imaginemos que cada punto “irradiara” influencia alrededor suyo, y que esas influencias se sumaran en una grilla invisible que cubre todo el mapa. Donde más puntos hay cercanos entre sí, mayor es la densidad estimada.
El resultado son curvas similares a las de un mapa topográfico, pero en este caso representan concentración de eventos, no altura. Las zonas con mayor densidad aparecen más intensas visualmente (o con colores más cálidos, si usás un mapa de calor), y permiten detectar puntos críticos o áreas de riesgo sin necesidad de agrupar manualmente.
ggmap(mapa_base) +
stat_density2d(data = cdmx_transito,
aes(x = longitud, y = latitud, fill = after_stat(level)), geom = "polygon", alpha=0.75)+
labs(title="Densidad de hechos de tránsito",
subtitle="Ciudad de México")+
scale_fill_distiller(palette = "Spectral")+
theme_void()
Ahora desagregada en día y mes
ggmap(mapa_base) +
stat_density2d(data = cdmx_transito,
aes(x = longitud, y = latitud, fill = after_stat(level)), geom = "polygon", alpha=0.75)+
labs(title="Densidad de hechos de tránsito en 2020",
subtitle="Ciudad de México")+
scale_fill_distiller(palette = "Spectral")+
facet_wrap(~dia_semana, ncol=4)+
theme_void()
ggmap(mapa_base) +
stat_density2d(data = cdmx_transito,
aes(x = longitud, y = latitud, fill = after_stat(level)), geom = "polygon", alpha=0.75)+
labs(title="Densidad de hechos de tránsito en 2020",
subtitle="Ciudad de México")+
scale_fill_distiller(palette = "Spectral")+
facet_wrap(~mes, ncol=6)+
theme_void()
Como habrán notado, la información es la misma que la de geom_bin2d() pero visualizada de otra forma.
#Mapas animados
Hasta acá hemos visto una serie de gráficos y mapas donde detectamos algunos patrones temporales a partir de la visualización de variables de tiempo (mes, día de la semana, etc.) con la función facet_wrap().
Sin embargo, esta no es la única forma de incluir variables temporales a nuestros gráficos ya que, por ejemplo cuando las variables tienen muchas opciones posibles, la lectura del gráfico se hace cada vez más compleja.
Para esto sirven las visualizaciones animadas o “GIF” que podemos realizar a partir de 2 librerías llamadas gganimate y gifski.
install.packages("gganimate")
install.packages("gifski")
library(gifski)
library(gganimate)
mapa_animado_2024 <-ggmap(mapa_base) +
stat_density2d(data = cdmx_transito,
aes(x = longitud, y = latitud, fill = after_stat(level)), geom = "polygon", alpha=0.5)+
scale_fill_distiller(palette = "Spectral")+
labs(title="Densidad de hechos de tránsito en 2024",
subtitle = "CDMX | Mes: {closest_state}")+
transition_states(mes)
animate(mapa_animado_2024, renderer = gifski_renderer())
mapa_animado_diciembre <- ggmap(mapa_base) +
stat_density2d(data = cdmx_transito %>% filter(mes=="diciembre"),
aes(x = longitud, y = latitud, fill = after_stat(level)), geom = "polygon", alpha=0.5)+
scale_fill_distiller(palette = "Spectral")+
labs(title="Densidad de hechos de tránsito en diciembre 2024",
subtitle = "CDMX | Dia: {frame_time}")+
transition_time(fecha_evento)
animate(mapa_animado_diciembre, renderer = gifski_renderer())
Por último, ára descargar los gif vamos a usar la función anim_save()
anim_save("mapa_animado_2024.gif", mapa_animado_2024)