Los campos de fechas suelen ser formatos difíciles de trabajar y procesar. ¿Cómo conocer la fecha mínima de un dataset? ¿Cómo calcular la diferencia entre dos fechas? Si bien existen funciones en Rbase para el manejo de fechas, el paquete lubridate cuenta con muchas funcionalidades para el manejo de fechas. Con este paquete, además de poder operar sobre las fechas, podemos también conocer los días de las semanas y los meses.
En primer lugar vamos a cargar las librerías. Recuerden instalarlas previamente con install.packages():
# install.packages("tidyverse")
library(tidyverse)
## ── Attaching packages ─────────────────────────────────────── tidyverse 1.3.0 ──
## ✓ ggplot2 3.3.5 ✓ purrr 0.3.4
## ✓ tibble 3.1.3 ✓ dplyr 1.0.7
## ✓ tidyr 1.1.3 ✓ stringr 1.4.0
## ✓ readr 1.3.1 ✓ forcats 0.5.0
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## x dplyr::filter() masks stats::filter()
## x dplyr::lag() masks stats::lag()
# install.packages("lubridate")
library(lubridate)
##
## Attaching package: 'lubridate'
## The following objects are masked from 'package:base':
##
## date, intersect, setdiff, union
Vamos a trabajar con datos de uso de bicicletas públicas. Los datos originales fueron obtenidos de BADATA. Este dataset contiene más de 2.600.000 de registros representando cada viaje realizado durante 2021 mediante el sistema público de bicicletas de la Ciudad Autónoma de Buenos Aires.
A partir de la información geoespacial de los barrios de la ciudad, se le asignó a cada viaje el barrio de origen y el barrio de destino. Luego se agrupó el dataset por par origen-destino, realizando un conteo de cantidad de recorridos. Por último, se seleccionaron los pares origen-destino con más de 20 viajes diarios realizados entre el 1 de julio 2021 y el 30 de noviembre 2021 (Diciembre no estaba completo en la base).
# carga de dataset
viajes_od <- read.csv( "https://raw.githubusercontent.com/paulavidela/utdt_cienciadedatos/main/data/viajes_od_bicis_2021.csv")
En primer lugar vamos a inspeccionar nuestra base de datos:
head(viajes_od)
## barrio_origen fecha_hora_origen barrio_destino fecha_hora_destino cantidad
## 1 ALMAGRO 2021-07-01 00 ALMAGRO 2021-07-01 00 1
## 2 ALMAGRO 2021-07-01 00 RECOLETA 2021-07-01 00 1
## 3 ALMAGRO 2021-07-01 05 PALERMO 2021-07-01 05 1
## 4 ALMAGRO 2021-07-01 06 ALMAGRO 2021-07-01 06 1
## 5 ALMAGRO 2021-07-01 06 BALVANERA 2021-07-01 06 1
## 6 ALMAGRO 2021-07-01 06 CABALLITO 2021-07-01 06 2
El dataset contiene:
- barrio de inicio del recorrido: barrio_origen
- fecha y hora de inicio del recorrido: fecha_hora_origen
- barrio de finalización del recorrido: barrio_destino
- fecha y hora de finalización del recorrido: fecha_hora_destino
- cantidad de viajes realizados para ese par origen-destino en ese horario: cantidad
str(viajes_od)
## 'data.frame': 236351 obs. of 5 variables:
## $ barrio_origen : chr "ALMAGRO" "ALMAGRO" "ALMAGRO" "ALMAGRO" ...
## $ fecha_hora_origen : chr "2021-07-01 00" "2021-07-01 00" "2021-07-01 05" "2021-07-01 06" ...
## $ barrio_destino : chr "ALMAGRO" "RECOLETA" "PALERMO" "ALMAGRO" ...
## $ fecha_hora_destino: chr "2021-07-01 00" "2021-07-01 00" "2021-07-01 05" "2021-07-01 06" ...
## $ cantidad : int 1 1 1 1 1 2 1 1 2 3 ...
Observamos que hay dos variables relacionadas a la fecha:
- fecha_hora_origen: correspondiente a la fecha y hora en la que se inició el recorrido en bicicleta
- fecha_hora_destino: correspondiente a la fecha y hora en la que se finalizó el recorrido en bicicleta
Como podemos ver en el resultado de la función str(), las dos variables son ‘chr’ es decir, son de tipo caracter. Podemos confirmar la clase con la función class()
class(viajes_od$fecha_hora_destino)
## [1] "character"
R reconoció esa variable como de tipo caracter. Podemos transformar estas variables con funciones de Lubridate.
Lubridate tiene funciones que permiten calcular la fecha y hora actual:
- today(): nos indica la fecha de hoy
- now(): nos indica la fecha y hora de ahora
today()
## [1] "2022-05-29"
¿Cuál es la clase de today()?
class(today())
## [1] "Date"
now()
## [1] "2022-05-29 17:58:13 -03"
¿Cuál es la clase de now()?
class(now())
## [1] "POSIXct" "POSIXt"
Existen tres tipos de formato de fechas:
- fecha (tipo ‘Date’)
- hora (tipo ‘Period’)
- fecha-hora (tipo ‘POSIXt’)
¿Cómo se transforman las variables?
Para realizar los procesamientos de forma más rápida, vamos a seleccionar un dataset reducido con el OD más frecuente. Para eso, vamos a calcular la cantidad de viajes totales para cada par origen-destino . ¿Cuál creen que puede ser el OD más frecuente?
matriz_od <- viajes_od %>%
group_by(barrio_origen, barrio_destino) %>%
summarise(cantidad = sum(cantidad)) %>%
arrange(desc(cantidad))
## `summarise()` has grouped output by 'barrio_origen'. You can override using the `.groups` argument.
head(matriz_od, 10)
## # A tibble: 10 × 3
## # Groups: barrio_origen [5]
## barrio_origen barrio_destino cantidad
## <chr> <chr> <int>
## 1 PALERMO PALERMO 105186
## 2 PALERMO RECOLETA 36491
## 3 RECOLETA PALERMO 35536
## 4 RECOLETA RECOLETA 34788
## 5 BELGRANO BELGRANO 18010
## 6 CABALLITO CABALLITO 17266
## 7 BALVANERA BALVANERA 17223
## 8 BELGRANO PALERMO 13522
## 9 PALERMO BELGRANO 13132
## 10 BALVANERA RECOLETA 12468
El OD Palermo-Palermo es el OD más frecuente. Filtremos solo estos viajes
od_palermo <- viajes_od %>%
filter(barrio_origen == "PALERMO" & barrio_destino == "PALERMO")
Veamos el dataset resultante
head(od_palermo)
## barrio_origen fecha_hora_origen barrio_destino fecha_hora_destino cantidad
## 1 PALERMO 2021-07-01 00 PALERMO 2021-07-01 00 7
## 2 PALERMO 2021-07-01 00 PALERMO 2021-07-01 01 2
## 3 PALERMO 2021-07-01 01 PALERMO 2021-07-01 01 1
## 4 PALERMO 2021-07-01 05 PALERMO 2021-07-01 05 2
## 5 PALERMO 2021-07-01 06 PALERMO 2021-07-01 06 6
## 6 PALERMO 2021-07-01 06 PALERMO 2021-07-01 07 1
Ahora si, vamos a transformar la variable fecha_hora_origen a formato fecha. Para esto pueden utilizar multiples funciones de lubridate dependiendo de como estén escritos los campos. Vamos a mostrar algunas de ellas. Pueden ver otras maneras aqui.
Nuestro campo tiene fecha y hora. La fecha se encuentra en formato: año(year), mes(month), día(day) y hora(hour). Se utiliza entonces la función ymd_h().
od_palermo <- od_palermo %>%
mutate(fh_inicio = ymd_h(fecha_hora_origen),
fh_final = ymd_h(fecha_hora_destino))
Veamos los resultados
head(od_palermo)
## barrio_origen fecha_hora_origen barrio_destino fecha_hora_destino cantidad
## 1 PALERMO 2021-07-01 00 PALERMO 2021-07-01 00 7
## 2 PALERMO 2021-07-01 00 PALERMO 2021-07-01 01 2
## 3 PALERMO 2021-07-01 01 PALERMO 2021-07-01 01 1
## 4 PALERMO 2021-07-01 05 PALERMO 2021-07-01 05 2
## 5 PALERMO 2021-07-01 06 PALERMO 2021-07-01 06 6
## 6 PALERMO 2021-07-01 06 PALERMO 2021-07-01 07 1
## fh_inicio fh_final
## 1 2021-07-01 00:00:00 2021-07-01 00:00:00
## 2 2021-07-01 00:00:00 2021-07-01 01:00:00
## 3 2021-07-01 01:00:00 2021-07-01 01:00:00
## 4 2021-07-01 05:00:00 2021-07-01 05:00:00
## 5 2021-07-01 06:00:00 2021-07-01 06:00:00
## 6 2021-07-01 06:00:00 2021-07-01 07:00:00
Observamos que R ajustó el formato de nuestra variable.
Ahora la función summary nos va a brindar más información.
summary(od_palermo$fh_inicio)
## Min. 1st Qu. Median
## "2021-07-01 00:00:00" "2021-08-11 15:15:00" "2021-09-19 16:00:00"
## Mean 3rd Qu. Max.
## "2021-09-18 02:47:34" "2021-10-26 10:00:00" "2021-11-30 23:00:00"
Veamos la clase de la nueva variable
class(od_palermo$fh_inicio)
## [1] "POSIXct" "POSIXt"
Podemos ver que la clase de la variable fh_inicio es de POSIXct (fecha - hora) . Al tratarse de fechas, podríamos realizar operaciones. Por ejemplo, podríamos calcular la diferencia entre la fecha-hora de inicio, y la fecha-hora de finalización.
od_palermo <- od_palermo %>%
mutate(duracion = fh_final - fh_inicio)
Veamos un resumen
head(od_palermo)
## barrio_origen fecha_hora_origen barrio_destino fecha_hora_destino cantidad
## 1 PALERMO 2021-07-01 00 PALERMO 2021-07-01 00 7
## 2 PALERMO 2021-07-01 00 PALERMO 2021-07-01 01 2
## 3 PALERMO 2021-07-01 01 PALERMO 2021-07-01 01 1
## 4 PALERMO 2021-07-01 05 PALERMO 2021-07-01 05 2
## 5 PALERMO 2021-07-01 06 PALERMO 2021-07-01 06 6
## 6 PALERMO 2021-07-01 06 PALERMO 2021-07-01 07 1
## fh_inicio fh_final duracion
## 1 2021-07-01 00:00:00 2021-07-01 00:00:00 0 secs
## 2 2021-07-01 00:00:00 2021-07-01 01:00:00 3600 secs
## 3 2021-07-01 01:00:00 2021-07-01 01:00:00 0 secs
## 4 2021-07-01 05:00:00 2021-07-01 05:00:00 0 secs
## 5 2021-07-01 06:00:00 2021-07-01 06:00:00 0 secs
## 6 2021-07-01 06:00:00 2021-07-01 07:00:00 3600 secs
La variable duracion es de tipo time. Podríamos transformarla a numérica para ver un resumen.
summary(as.numeric(od_palermo$duracion))
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0 0 3600 2626 3600 104400
Nuestro dataset tiene unicamente fecha y hora, por eso la diferencia no tiene valores menores a 3600 segundos (1hora). Hay muchos recorridos que se realizaron dentro de la misma hora.
Lubridate nos permite también calcular valores específicos en relación a la fecha. Como nuestra variable fh_inicio tiene una clase adecuada, podemos ingresar a los componetes ‘año’, ‘mes’ y ‘día’ con las funciones year(), month(), day()
od_palermo<- od_palermo %>%
mutate(anio = year(fh_inicio),
mes = month(fh_inicio),
dia = day(fh_inicio),
hora = hour(fh_inicio))
Veamos el resultado
head(od_palermo)
## barrio_origen fecha_hora_origen barrio_destino fecha_hora_destino cantidad
## 1 PALERMO 2021-07-01 00 PALERMO 2021-07-01 00 7
## 2 PALERMO 2021-07-01 00 PALERMO 2021-07-01 01 2
## 3 PALERMO 2021-07-01 01 PALERMO 2021-07-01 01 1
## 4 PALERMO 2021-07-01 05 PALERMO 2021-07-01 05 2
## 5 PALERMO 2021-07-01 06 PALERMO 2021-07-01 06 6
## 6 PALERMO 2021-07-01 06 PALERMO 2021-07-01 07 1
## fh_inicio fh_final duracion anio mes dia hora
## 1 2021-07-01 00:00:00 2021-07-01 00:00:00 0 secs 2021 7 1 0
## 2 2021-07-01 00:00:00 2021-07-01 01:00:00 3600 secs 2021 7 1 0
## 3 2021-07-01 01:00:00 2021-07-01 01:00:00 0 secs 2021 7 1 1
## 4 2021-07-01 05:00:00 2021-07-01 05:00:00 0 secs 2021 7 1 5
## 5 2021-07-01 06:00:00 2021-07-01 06:00:00 0 secs 2021 7 1 6
## 6 2021-07-01 06:00:00 2021-07-01 07:00:00 3600 secs 2021 7 1 6
Podemos incluso ajustar parámetros para las funciones. Por ejemplo, nos interesa el nombre del mes y no el número. Por eso vamos a activar la etiqueta de forma completa (no abreviada). También es posible configurar el idioma con el parámetro locale. Por ejemplo: locale = "Spanish" para Windows o locale = "es_ES.UTF-8" para IOS.
od_palermo <- od_palermo %>%
mutate(mes = month(fh_inicio, label = TRUE, abbr = FALSE, locale = "es_ES.UTF-8"))
Veamos ahora el resultado
head(od_palermo)
## barrio_origen fecha_hora_origen barrio_destino fecha_hora_destino cantidad
## 1 PALERMO 2021-07-01 00 PALERMO 2021-07-01 00 7
## 2 PALERMO 2021-07-01 00 PALERMO 2021-07-01 01 2
## 3 PALERMO 2021-07-01 01 PALERMO 2021-07-01 01 1
## 4 PALERMO 2021-07-01 05 PALERMO 2021-07-01 05 2
## 5 PALERMO 2021-07-01 06 PALERMO 2021-07-01 06 6
## 6 PALERMO 2021-07-01 06 PALERMO 2021-07-01 07 1
## fh_inicio fh_final duracion anio mes dia hora
## 1 2021-07-01 00:00:00 2021-07-01 00:00:00 0 secs 2021 julio 1 0
## 2 2021-07-01 00:00:00 2021-07-01 01:00:00 3600 secs 2021 julio 1 0
## 3 2021-07-01 01:00:00 2021-07-01 01:00:00 0 secs 2021 julio 1 1
## 4 2021-07-01 05:00:00 2021-07-01 05:00:00 0 secs 2021 julio 1 5
## 5 2021-07-01 06:00:00 2021-07-01 06:00:00 0 secs 2021 julio 1 6
## 6 2021-07-01 06:00:00 2021-07-01 07:00:00 3600 secs 2021 julio 1 6
Vamos a realizar un gráfico de cantidad de viajes por mes
ggplot(od_palermo) +
geom_bar(aes(x = mes, weight = cantidad, fill = mes)) +
labs(title = "Cantidad de Viajes PALERMO-PALERMO",
subtitle = "Desde julio 2021 a noviembre 2021") +
theme_minimal() +
scale_fill_brewer(palette = 8, type = "qual") +
theme(legend.position = "none",
axis.title = element_blank())
Nos interesa conocer la evolución de la cantidad de viajes por día y hora para una semana en particular. Vamos a tomar la semana 27 (aprox principios de julio) Vamos a utilizar la función week() para calcular la semana
od_palermo <- od_palermo %>%
mutate(semana = week(fh_inicio))
ggplot(od_palermo %>%
filter(semana == 27)) +
geom_line(aes(x=fh_inicio, y = cantidad), color = "darkslateblue")+
labs(title = "Cantidad de Viajes PALERMO-PALERMO",
subtitle = "Año 2021 - Semana 27") +
theme_minimal() +
theme(legend.position = "none",
axis.title = element_blank())
Habiendo explorado algunas funciones iniciales en lubridate, ahora vamos a analizar los patrones temporales en nuestro dataset.
Para preprocesar nuestro dataset, vamos a transformas la variable fecha_hora_origen a formato fecha-hora. Además vamos a calcular solo el día (para luego agrupar por registros diarios), el día de la semana y la hora.
La función round_date() nos permite redondear el día, y la función wday() nos permite calcular el día de la semana.
viajes_od_final <- viajes_od %>%
mutate(fecha_hora_o = ymd_h(fecha_hora_origen),
fecha_o = round_date(fecha_hora_o, "day"),
hora_o = hour(fecha_hora_o))
head(viajes_od_final, 10)
## barrio_origen fecha_hora_origen barrio_destino fecha_hora_destino cantidad
## 1 ALMAGRO 2021-07-01 00 ALMAGRO 2021-07-01 00 1
## 2 ALMAGRO 2021-07-01 00 RECOLETA 2021-07-01 00 1
## 3 ALMAGRO 2021-07-01 05 PALERMO 2021-07-01 05 1
## 4 ALMAGRO 2021-07-01 06 ALMAGRO 2021-07-01 06 1
## 5 ALMAGRO 2021-07-01 06 BALVANERA 2021-07-01 06 1
## 6 ALMAGRO 2021-07-01 06 CABALLITO 2021-07-01 06 2
## 7 ALMAGRO 2021-07-01 06 PALERMO 2021-07-01 06 1
## 8 ALMAGRO 2021-07-01 06 PALERMO 2021-07-01 07 1
## 9 ALMAGRO 2021-07-01 07 ALMAGRO 2021-07-01 08 2
## 10 ALMAGRO 2021-07-01 07 BALVANERA 2021-07-01 07 3
## fecha_hora_o fecha_o hora_o
## 1 2021-07-01 00:00:00 2021-07-01 0
## 2 2021-07-01 00:00:00 2021-07-01 0
## 3 2021-07-01 05:00:00 2021-07-01 5
## 4 2021-07-01 06:00:00 2021-07-01 6
## 5 2021-07-01 06:00:00 2021-07-01 6
## 6 2021-07-01 06:00:00 2021-07-01 6
## 7 2021-07-01 06:00:00 2021-07-01 6
## 8 2021-07-01 06:00:00 2021-07-01 6
## 9 2021-07-01 07:00:00 2021-07-01 7
## 10 2021-07-01 07:00:00 2021-07-01 7
Vamos a calcular la cantidad de registros por día para cada par origen-destino.
viajes_diarios <- viajes_od_final %>%
group_by(fecha_o, barrio_origen, barrio_destino) %>%
summarise(cantidad = sum(cantidad))
## `summarise()` has grouped output by 'fecha_o', 'barrio_origen'. You can override using the `.groups` argument.
De acuerdo a nuestro dataset de pares OD, los 3 más frecuentes son:
- Palermo - Palermo
- Palermo - Recoleta
- Recoleta - Palermo
Veamos como varía la cantidad de viajes diarios para esos destinos
viajes_diarios_od_freq <- viajes_diarios %>%
filter(
(barrio_origen == "PALERMO" & barrio_destino == "PALERMO") |
(barrio_origen == "PALERMO" & barrio_destino == "RECOLETA") |
(barrio_origen == "RECOLETA" & barrio_destino == "PALERMO")) %>%
mutate(etiqueta = paste0(barrio_origen, " a ", barrio_destino))
Veamos la variación en un gráfico
viajes_diarios_od_freq %>%
ggplot() +
geom_line(aes(x=fecha_o, y = cantidad, color = etiqueta)) +
labs(title = "Viajes por día",
subtitle = "Para OD más frecuentes entre Julio y Noviembre 2021",
caption = "Fuente: BADATA",
x = "",
y = "",
color = "") +
scale_color_brewer(type = "qual", palette = 2) +
scale_y_continuous(breaks = seq(0, 10000, 2000)) +
theme_minimal() +
theme(legend.position = "bottom")
El comportamiento diario de los viajes Palermo-Recoleta y Recoleta-Palermo son muy similares. ¿Se tratará de viajes de ida y de vuelta? En este caso deberíamos detectar un comportamiento distinto a nivel horario.
viajes_hora_od_freq <- viajes_od_final %>%
group_by(hora_o, barrio_origen, barrio_destino) %>%
summarise(cantidad = sum(cantidad)) %>%
filter(
(barrio_origen == "PALERMO" & barrio_destino == "PALERMO") |
(barrio_origen == "PALERMO" & barrio_destino == "RECOLETA") |
(barrio_origen == "RECOLETA" & barrio_destino == "PALERMO")) %>%
mutate(etiqueta = paste0(barrio_origen, " a ", barrio_destino))
## `summarise()` has grouped output by 'hora_o', 'barrio_origen'. You can override using the `.groups` argument.
Veamos el comportamiento
viajes_hora_od_freq %>%
ggplot() +
geom_line(aes(x=hora_o, y = cantidad, color = etiqueta)) +
labs(title = "Viajes por hora",
subtitle = "Para OD más frecuentes entre Julio y Noviembre 2021",
caption = "Fuente: BADATA",
x = "",
y = "",
color = "") +
scale_color_brewer(type = "qual", palette = 2) +
scale_y_continuous(breaks = seq(0, 10000, 2000)) +
theme_minimal() +
theme(legend.position = "bottom")
El comportamiento es nuevamente similar. Para los tres pares OD, los viajes a la tarde son más frecuentes.
En el gráfico de viajes por día, detectamos un patrón en el comportamiento: se observan picos de descenso en el uso de bicicletas de manera frecuente. ¿Está relacionado con el día de la semana?
La función wday() del paquete de lubridate permite calcular el día de la semana.
wday(today())
## [1] 1
El día de la semana de formato numérico es poco claro. Podemos ajustar los parámetros label (para mostrar el día en texto) y abbr (texto completo o abreviado)
wday(today(), label = TRUE, abbr = FALSE)
## [1] Sunday
## 7 Levels: Sunday < Monday < Tuesday < Wednesday < Thursday < ... < Saturday
Por defecto, los parámetros se encuentran de la siguiente manera: label = FALSE y abbr =TRUE. También se puede configurar el idioma. Por último, podemos ajustar el parámetro week_start para determinar el inicio de la semana. Por defecto, es week_start = 0 , es decir primero domingo.
viajes_dia_semana <- viajes_diarios %>%
group_by(fecha_o) %>%
summarise(cantidad = sum(cantidad)) %>%
mutate(dia_semana_o = wday(fecha_o,
label = TRUE,
abbr = FALSE,
locale = "es_ES.UTF-8",
week_start = 1))
Veamos nuestro nuevo dataset
head(viajes_dia_semana)
## # A tibble: 6 × 3
## fecha_o cantidad dia_semana_o
## <dttm> <int> <ord>
## 1 2021-07-01 00:00:00 1108 jueves
## 2 2021-07-02 00:00:00 4286 viernes
## 3 2021-07-03 00:00:00 3729 sábado
## 4 2021-07-04 00:00:00 1814 domingo
## 5 2021-07-05 00:00:00 2782 lunes
## 6 2021-07-06 00:00:00 4823 martes
Veamos como varía la cantidad de viajes según día de la semana.
ggplot(viajes_dia_semana) +
geom_boxplot(aes(x = dia_semana_o,
y=cantidad,
fill = as.character(dia_semana_o))) +
labs(title = "Cantidad de viajes",
y = "",
x = "Día de la semana de inicio") +
theme_minimal() +
scale_y_continuous(breaks = seq(0,10000,1000)) +
scale_fill_brewer(palette = 8, type = "qual") +
theme(legend.position = "none")
Podemos observar que el valor medio de los viajes es muy similar desde martes a viernes, disminuyendo considerablemente los domingos.
¡Con el paquete ‘lubridate’ podemos trabajar sencillamente con las variables de tipo fecha!