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.1 ──
## ✔ ggplot2 3.3.5     ✔ purrr   0.3.4
## ✔ tibble  3.1.3     ✔ dplyr   1.0.9
## ✔ tidyr   1.1.3     ✔ stringr 1.4.0
## ✔ readr   2.1.2     ✔ forcats 0.5.1
## Warning: package 'readr' was built under R version 4.0.5
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ 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

Sobre el dataset

Vamos a trabajar con datos de uso de bicicletas públicas entre octubre 2021 y noviembre 2021. Los datos originales fueron obtenidos de BADATA.
Sobre los datos originales, se realizó un geoproceso para obtener los nombres del barrio y de la comuna donde se ubica la estación de origen y la estación de llegada. También se realizó un ruteo a partir de datos de OSM para calcular la distancia y la duración estimada.

El dataset puede descargarse desde Github.

bicicletas_raw <- read.csv("https://raw.githubusercontent.com/paulavidela/utdt_cienciadedatos/main/data/bicicletas-GCBA-oct-nov-2021.csv",  header=TRUE, stringsAsFactors=TRUE, encoding = "UTF-8")

En primer lugar vamos a inspeccionar nuestra base de datos:

names(bicicletas_raw)
##  [1] "periodo_recorrido"       "fecha_origen_recorrido" 
##  [3] "nombre_estacion_origen"  "barrio_estacion_origen" 
##  [5] "comuna_estacion_origen"  "fecha_destino_recorrido"
##  [7] "nombre_estacion_destino" "barrio_estacion_destino"
##  [9] "comuna_estacion_destino" "modelo_bicicleta"       
## [11] "duracion_calculada_min"  "duracion_osrm_min"      
## [13] "distancia_osrm_km"

Sobre las variables:
- periodo_recorrido: corresponde al año-mes del inicio del recorrido
- fecha_origen_recorrido: corresponde a la fecha del origen del recorrido
- nombre_estacion_origen: corresponde al nombre de la estación de origen del recorrido
- barrio_estacion_origen: corresponde al barrio de la estación de origen del recorrido
- comuna_estacion_origen: corresponde al comuna de la estación de origen del recorrido
- fecha_destino_recorrido: corresponde a la fecha del destino del recorrido
- nombre_estacion_destino: corresponde al nombre de la estación de destino del recorrido
- barrio_estacion_destino: corresponde al barrio de la estación de destino del recorrido
- comuna_estacion_destino: corresponde al comuna de la estación de destino del recorrido
- modelo_bicicleta: corresponde al modelo de la bicicleta elegida
- duracion_calculada_min: corresponde a la duración del recorrido en minutos (según origen-destino)
- duracion_osrm_min: corresponde a la duración del recorrido calculada por OSRM
- distancia_osrm_km: corresponde a la distancia recorrida según OSRM

head(bicicletas_raw)
##   periodo_recorrido  fecha_origen_recorrido nombre_estacion_origen
## 1           2021-10 2021-10-09 16:03:42 UTC         002 - RETIRO I
## 2           2021-10 2021-10-09 14:53:58 UTC         002 - RETIRO I
## 3           2021-10 2021-10-09 10:30:22 UTC         002 - RETIRO I
## 4           2021-10 2021-10-09 15:00:35 UTC         002 - RETIRO I
## 5           2021-10 2021-10-09 17:47:07 UTC         002 - RETIRO I
## 6           2021-10 2021-10-09 15:09:20 UTC         002 - RETIRO I
##   barrio_estacion_origen comuna_estacion_origen fecha_destino_recorrido
## 1                 RETIRO                      1 2021-10-09 16:42:33 UTC
## 2                 RETIRO                      1 2021-10-09 15:08:53 UTC
## 3                 RETIRO                      1 2021-10-09 11:06:05 UTC
## 4                 RETIRO                      1 2021-10-09 16:36:00 UTC
## 5                 RETIRO                      1 2021-10-09 18:00:17 UTC
## 6                 RETIRO                      1 2021-10-09 16:02:43 UTC
##        nombre_estacion_destino barrio_estacion_destino comuna_estacion_destino
## 1 254 - PLAZA RAFAEL HERNANDEZ                BELGRANO                      13
## 2               032 - CATEDRAL             SAN NICOLAS                       1
## 3          292 - PLAZA BOLIVIA                 PALERMO                      14
## 4          006 - PARQUE LEZAMA               SAN TELMO                       1
## 5          237 - MADERO OFFICE           PUERTO MADERO                       1
## 6                  250 - FLENI                BELGRANO                      13
##   modelo_bicicleta duracion_calculada_min duracion_osrm_min distancia_osrm_km
## 1           ICONIC                     39             36.91              8.88
## 2           ICONIC                     15             10.56              2.01
## 3           ICONIC                     36             28.41              6.86
## 4           ICONIC                     95             20.38              4.36
## 5              FIT                     13              8.50              1.78
## 6           ICONIC                     53             35.91              8.66
summary(bicicletas_raw)
##  periodo_recorrido             fecha_origen_recorrido
##  2021-10:246419    2021-11-26 15:55:38 UTC:     6    
##  2021-11:265178    2021-10-04 14:11:18 UTC:     5    
##                    2021-10-05 18:22:00 UTC:     5    
##                    2021-10-06 15:38:16 UTC:     5    
##                    2021-10-15 20:24:22 UTC:     5    
##                    2021-10-20 20:25:52 UTC:     5    
##                    (Other)                :511566    
##                    nombre_estacion_origen barrio_estacion_origen
##  014 - PACIFICO               :  6894     PALERMO    : 98039    
##  009 - PARQUE LAS HERAS       :  5512     RECOLETA   : 53619    
##  160 - GODOY CRUZ Y LIBERTADOR:  5401     BALVANERA  : 33400    
##  005 - PLAZA ITALIA           :  5337     CABALLITO  : 28047    
##  096 - CARLOS GARDEL          :  5333     SAN NICOLAS: 26794    
##  147 - CONSTITUCIÓN           :  5178     (Other)    :269219    
##  (Other)                      :477942     NA's       :  2479    
##  comuna_estacion_origen            fecha_destino_recorrido
##  Min.   : 1.000         2021-11-17 19:42:12 UTC:     6    
##  1st Qu.: 2.000         2021-11-26 18:57:54 UTC:     6    
##  Median : 6.000         2021-10-05 14:10:45 UTC:     5    
##  Mean   : 7.459         2021-10-05 15:08:14 UTC:     5    
##  3rd Qu.:14.000         2021-10-05 16:24:27 UTC:     5    
##  Max.   :15.000         2021-10-25 20:31:01 UTC:     5    
##  NA's   :2479           (Other)                :511565    
##                   nombre_estacion_destino barrio_estacion_destino
##  014 - PACIFICO               :  6660     PALERMO    : 97560     
##  160 - GODOY CRUZ Y LIBERTADOR:  5548     RECOLETA   : 53610     
##  009 - PARQUE LAS HERAS       :  5533     BALVANERA  : 33526     
##  096 - CARLOS GARDEL          :  5415     CABALLITO  : 27389     
##  147 - CONSTITUCIÓN           :  5262     SAN NICOLAS: 26506     
##  005 - PLAZA ITALIA           :  5089     (Other)    :270507     
##  (Other)                      :478090     NA's       :  2499     
##  comuna_estacion_destino modelo_bicicleta duracion_calculada_min
##  Min.   : 1.000          FIT   :227668    Min.   :   1.00       
##  1st Qu.: 2.000          ICONIC:283929    1st Qu.:  10.00       
##  Median : 6.000                           Median :  16.00       
##  Mean   : 7.431                           Mean   :  20.45       
##  3rd Qu.:14.000                           3rd Qu.:  25.00       
##  Max.   :15.000                           Max.   :7258.00       
##  NA's   :2499                                                   
##  duracion_osrm_min distancia_osrm_km
##  Min.   : 0.00     Min.   : 0.000   
##  1st Qu.: 7.17     1st Qu.: 1.500   
##  Median :11.18     Median : 2.360   
##  Mean   :12.63     Mean   : 2.737   
##  3rd Qu.:16.71     3rd Qu.: 3.660   
##  Max.   :74.71     Max.   :17.280   
## 
str(bicicletas_raw)
## 'data.frame':    511597 obs. of  13 variables:
##  $ periodo_recorrido      : Factor w/ 2 levels "2021-10","2021-11": 1 1 1 1 1 1 1 1 1 1 ...
##  $ fecha_origen_recorrido : Factor w/ 470709 levels "2021-09-30 19:58:09 UTC",..: 58804 58297 56942 58337 59615 58402 58398 59285 56654 57474 ...
##  $ nombre_estacion_origen : Factor w/ 271 levels "001 - FACULTAD DE DERECHO",..: 2 2 2 2 2 2 2 2 2 2 ...
##  $ barrio_estacion_origen : Factor w/ 47 levels "AGRONOMIA","ALMAGRO",..: 29 29 29 29 29 29 29 29 29 29 ...
##  $ comuna_estacion_origen : int  1 1 1 1 1 1 1 1 1 1 ...
##  $ fecha_destino_recorrido: Factor w/ 467136 levels "2021-10-01 00:00:10 UTC",..: 58335 57713 56540 58286 58919 58066 58073 58914 56232 56889 ...
##  $ nombre_estacion_destino: Factor w/ 273 levels "001 - FACULTAD DE DERECHO",..: 192 28 217 6 181 189 189 194 96 2 ...
##  $ barrio_estacion_destino: Factor w/ 47 levels "AGRONOMIA","ALMAGRO",..: 5 32 21 33 27 5 5 5 28 29 ...
##  $ comuna_estacion_destino: int  13 1 14 1 1 13 13 13 2 1 ...
##  $ modelo_bicicleta       : Factor w/ 2 levels "FIT","ICONIC": 2 2 2 2 1 2 1 2 1 2 ...
##  $ duracion_calculada_min : int  39 15 36 95 13 53 54 58 20 7 ...
##  $ duracion_osrm_min      : num  36.9 10.6 28.4 20.4 8.5 ...
##  $ distancia_osrm_km      : num  8.88 2.01 6.86 4.36 1.78 8.66 8.66 9.31 3.33 0 ...

Llama la atención la cantidad de niveles (levels) que tienen las fechas. Deberíamos convertir las fechas a formato fecha.

Observamos que hay dos variables relacionadas a la fecha:
- fecha_origen_recorrido: correspondiente a la fecha y hora en la que se inició el recorrido en bicicleta
- fecha_destino_recorrido: 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 ‘factor’ es decir. Podemos confirmar la clase con la función class()

class(bicicletas_raw$fecha_origen_recorrido)
## [1] "factor"

Como cargamos nuestros datos con el parámetros stringsAsFactor = TRUE, R reconoció esa variable como de tipo caracter y la convirtió a factor. Vamos a reconvertirla a caracter para que pierda los niveles asociados a los factores.

bicicletas <- bicicletas_raw %>%
  mutate(fecha_origen_recorrido = as.character(fecha_origen_recorrido), 
         fecha_destino_recorrido = as.character(fecha_destino_recorrido))

Veamos los resultados

str(bicicletas)
## 'data.frame':    511597 obs. of  13 variables:
##  $ periodo_recorrido      : Factor w/ 2 levels "2021-10","2021-11": 1 1 1 1 1 1 1 1 1 1 ...
##  $ fecha_origen_recorrido : chr  "2021-10-09 16:03:42 UTC" "2021-10-09 14:53:58 UTC" "2021-10-09 10:30:22 UTC" "2021-10-09 15:00:35 UTC" ...
##  $ nombre_estacion_origen : Factor w/ 271 levels "001 - FACULTAD DE DERECHO",..: 2 2 2 2 2 2 2 2 2 2 ...
##  $ barrio_estacion_origen : Factor w/ 47 levels "AGRONOMIA","ALMAGRO",..: 29 29 29 29 29 29 29 29 29 29 ...
##  $ comuna_estacion_origen : int  1 1 1 1 1 1 1 1 1 1 ...
##  $ fecha_destino_recorrido: chr  "2021-10-09 16:42:33 UTC" "2021-10-09 15:08:53 UTC" "2021-10-09 11:06:05 UTC" "2021-10-09 16:36:00 UTC" ...
##  $ nombre_estacion_destino: Factor w/ 273 levels "001 - FACULTAD DE DERECHO",..: 192 28 217 6 181 189 189 194 96 2 ...
##  $ barrio_estacion_destino: Factor w/ 47 levels "AGRONOMIA","ALMAGRO",..: 5 32 21 33 27 5 5 5 28 29 ...
##  $ comuna_estacion_destino: int  13 1 14 1 1 13 13 13 2 1 ...
##  $ modelo_bicicleta       : Factor w/ 2 levels "FIT","ICONIC": 2 2 2 2 1 2 1 2 1 2 ...
##  $ duracion_calculada_min : int  39 15 36 95 13 53 54 58 20 7 ...
##  $ duracion_osrm_min      : num  36.9 10.6 28.4 20.4 8.5 ...
##  $ distancia_osrm_km      : num  8.88 2.01 6.86 4.36 1.78 8.66 8.66 9.31 3.33 0 ...

Primeros pasos en 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-08-15"

¿Cuál es la clase de today()?

class(today())
## [1] "Date"
now()
## [1] "2022-08-15 16:04:19 -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?

Vamos a tomar los primeros 5 registros y vamos a transformar la variable fecha_origen_recorrido. 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.

registros10 <- bicicletas %>% 
  head(10) %>%
  select(fecha_origen_recorrido) %>%
  rename(original = fecha_origen_recorrido)
registros10
##                   original
## 1  2021-10-09 16:03:42 UTC
## 2  2021-10-09 14:53:58 UTC
## 3  2021-10-09 10:30:22 UTC
## 4  2021-10-09 15:00:35 UTC
## 5  2021-10-09 17:47:07 UTC
## 6  2021-10-09 15:09:20 UTC
## 7  2021-10-09 15:09:09 UTC
## 8  2021-10-09 17:01:17 UTC
## 9  2021-10-09 08:18:22 UTC
## 10 2021-10-09 12:28:06 UTC

Nuestro campo tiene fecha y hora. La fecha se encuentra en formato: año(year), mes(month), día(day), hora(hour), minuto(minute), segundo (second). Se utiliza entonces la función ymd_hms().

registros10 <- registros10 %>% 
  mutate(fecha_hora = ymd_hms(original))
registros10
##                   original          fecha_hora
## 1  2021-10-09 16:03:42 UTC 2021-10-09 16:03:42
## 2  2021-10-09 14:53:58 UTC 2021-10-09 14:53:58
## 3  2021-10-09 10:30:22 UTC 2021-10-09 10:30:22
## 4  2021-10-09 15:00:35 UTC 2021-10-09 15:00:35
## 5  2021-10-09 17:47:07 UTC 2021-10-09 17:47:07
## 6  2021-10-09 15:09:20 UTC 2021-10-09 15:09:20
## 7  2021-10-09 15:09:09 UTC 2021-10-09 15:09:09
## 8  2021-10-09 17:01:17 UTC 2021-10-09 17:01:17
## 9  2021-10-09 08:18:22 UTC 2021-10-09 08:18:22
## 10 2021-10-09 12:28:06 UTC 2021-10-09 12:28:06

Veamos la clase de la nueva variable

class(registros10$fecha_hora)
## [1] "POSIXct" "POSIXt"

Podemos ver que la clase de la variable fecha_hora es de POSIXct (fecha - hora) .

Como nuestra variable fecha_hora tiene una clase adecuada, podemos ingresar a los componetes ‘año’, ‘mes’ y ‘día’ con las funciones year(), month(), day()

registros10 <- registros10 %>% 
  mutate(anio = year(fecha_hora), 
         mes = month(fecha_hora), 
         dia = day(fecha_hora))
registros10
##                   original          fecha_hora anio mes dia
## 1  2021-10-09 16:03:42 UTC 2021-10-09 16:03:42 2021  10   9
## 2  2021-10-09 14:53:58 UTC 2021-10-09 14:53:58 2021  10   9
## 3  2021-10-09 10:30:22 UTC 2021-10-09 10:30:22 2021  10   9
## 4  2021-10-09 15:00:35 UTC 2021-10-09 15:00:35 2021  10   9
## 5  2021-10-09 17:47:07 UTC 2021-10-09 17:47:07 2021  10   9
## 6  2021-10-09 15:09:20 UTC 2021-10-09 15:09:20 2021  10   9
## 7  2021-10-09 15:09:09 UTC 2021-10-09 15:09:09 2021  10   9
## 8  2021-10-09 17:01:17 UTC 2021-10-09 17:01:17 2021  10   9
## 9  2021-10-09 08:18:22 UTC 2021-10-09 08:18:22 2021  10   9
## 10 2021-10-09 12:28:06 UTC 2021-10-09 12:28:06 2021  10   9

Vamos a generar una nueva variable de fecha, en formato día/mes/año.

registros10 <- registros10 %>%
  mutate(solo_fecha= paste0(dia, "/", mes, "/", anio))
registros10
##                   original          fecha_hora anio mes dia solo_fecha
## 1  2021-10-09 16:03:42 UTC 2021-10-09 16:03:42 2021  10   9  9/10/2021
## 2  2021-10-09 14:53:58 UTC 2021-10-09 14:53:58 2021  10   9  9/10/2021
## 3  2021-10-09 10:30:22 UTC 2021-10-09 10:30:22 2021  10   9  9/10/2021
## 4  2021-10-09 15:00:35 UTC 2021-10-09 15:00:35 2021  10   9  9/10/2021
## 5  2021-10-09 17:47:07 UTC 2021-10-09 17:47:07 2021  10   9  9/10/2021
## 6  2021-10-09 15:09:20 UTC 2021-10-09 15:09:20 2021  10   9  9/10/2021
## 7  2021-10-09 15:09:09 UTC 2021-10-09 15:09:09 2021  10   9  9/10/2021
## 8  2021-10-09 17:01:17 UTC 2021-10-09 17:01:17 2021  10   9  9/10/2021
## 9  2021-10-09 08:18:22 UTC 2021-10-09 08:18:22 2021  10   9  9/10/2021
## 10 2021-10-09 12:28:06 UTC 2021-10-09 12:28:06 2021  10   9  9/10/2021

El campo solo_fecha está en formato día(day), mes(month), año(year). Se utiliza entonces la función dmy().

registros10 <- registros10 %>%
  mutate(fecha = dmy(solo_fecha))
registros10
##                   original          fecha_hora anio mes dia solo_fecha
## 1  2021-10-09 16:03:42 UTC 2021-10-09 16:03:42 2021  10   9  9/10/2021
## 2  2021-10-09 14:53:58 UTC 2021-10-09 14:53:58 2021  10   9  9/10/2021
## 3  2021-10-09 10:30:22 UTC 2021-10-09 10:30:22 2021  10   9  9/10/2021
## 4  2021-10-09 15:00:35 UTC 2021-10-09 15:00:35 2021  10   9  9/10/2021
## 5  2021-10-09 17:47:07 UTC 2021-10-09 17:47:07 2021  10   9  9/10/2021
## 6  2021-10-09 15:09:20 UTC 2021-10-09 15:09:20 2021  10   9  9/10/2021
## 7  2021-10-09 15:09:09 UTC 2021-10-09 15:09:09 2021  10   9  9/10/2021
## 8  2021-10-09 17:01:17 UTC 2021-10-09 17:01:17 2021  10   9  9/10/2021
## 9  2021-10-09 08:18:22 UTC 2021-10-09 08:18:22 2021  10   9  9/10/2021
## 10 2021-10-09 12:28:06 UTC 2021-10-09 12:28:06 2021  10   9  9/10/2021
##         fecha
## 1  2021-10-09
## 2  2021-10-09
## 3  2021-10-09
## 4  2021-10-09
## 5  2021-10-09
## 6  2021-10-09
## 7  2021-10-09
## 8  2021-10-09
## 9  2021-10-09
## 10 2021-10-09

Se observa que la variable fecha es de tipo ‘date’ y que está en formato año-mes-día. Este es el formato por defecto para las fechas en R.
Habiendo explorado algunas funciones iniciales en lubridate, ahora vamos a analizar los patrones temporales en nuestro dataset.

Patrones en movilidad en bicicleta en Buenos Aires

Para preprocesar nuestro dataset, vamos a transformas las variables fecha_origen_recorrido y fecha_destino_recorrido a formato ‘fecha-hora’. Además vamos a seleccionar nuestras variables de interés.

bicicletas_ok <- bicicletas %>%
  mutate(fecha_hora_origen = ymd_hms(fecha_origen_recorrido), 
         fecha_hora_destino = ymd_hms(fecha_destino_recorrido)) %>% 
  select(nombre_estacion_origen, barrio_estacion_origen, fecha_hora_origen, nombre_estacion_destino, barrio_estacion_destino, fecha_hora_destino)
head(bicicletas_ok, 10)
##    nombre_estacion_origen barrio_estacion_origen   fecha_hora_origen
## 1          002 - RETIRO I                 RETIRO 2021-10-09 16:03:42
## 2          002 - RETIRO I                 RETIRO 2021-10-09 14:53:58
## 3          002 - RETIRO I                 RETIRO 2021-10-09 10:30:22
## 4          002 - RETIRO I                 RETIRO 2021-10-09 15:00:35
## 5          002 - RETIRO I                 RETIRO 2021-10-09 17:47:07
## 6          002 - RETIRO I                 RETIRO 2021-10-09 15:09:20
## 7          002 - RETIRO I                 RETIRO 2021-10-09 15:09:09
## 8          002 - RETIRO I                 RETIRO 2021-10-09 17:01:17
## 9          002 - RETIRO I                 RETIRO 2021-10-09 08:18:22
## 10         002 - RETIRO I                 RETIRO 2021-10-09 12:28:06
##         nombre_estacion_destino barrio_estacion_destino  fecha_hora_destino
## 1  254 - PLAZA RAFAEL HERNANDEZ                BELGRANO 2021-10-09 16:42:33
## 2                032 - CATEDRAL             SAN NICOLAS 2021-10-09 15:08:53
## 3           292 - PLAZA BOLIVIA                 PALERMO 2021-10-09 11:06:05
## 4           006 - PARQUE LEZAMA               SAN TELMO 2021-10-09 16:36:00
## 5           237 - MADERO OFFICE           PUERTO MADERO 2021-10-09 18:00:17
## 6                   250 - FLENI                BELGRANO 2021-10-09 16:02:43
## 7                   250 - FLENI                BELGRANO 2021-10-09 16:03:21
## 8           256 - PLAZA NORUEGA                BELGRANO 2021-10-09 17:59:34
## 9         116 - HOSPITAL ALEMÁN                RECOLETA 2021-10-09 08:38:52
## 10               002 - RETIRO I                  RETIRO 2021-10-09 12:35:20

En primer lugar, queremos calcular los viajes por día según fecha de inicio. Con la función round_date() podemos redondear la fecha.

viajes_diarios <- bicicletas_ok %>% 
  mutate(dia = round_date(fecha_hora_origen, "day")) %>% 
  group_by(dia) %>% 
  summarise(cantidad =n())
head(viajes_diarios, 10)
## # A tibble: 10 × 2
##    dia                 cantidad
##    <dttm>                 <int>
##  1 2021-10-01 00:00:00     3020
##  2 2021-10-02 00:00:00     8492
##  3 2021-10-03 00:00:00     1832
##  4 2021-10-04 00:00:00     5055
##  5 2021-10-05 00:00:00    10717
##  6 2021-10-06 00:00:00    11368
##  7 2021-10-07 00:00:00    10091
##  8 2021-10-08 00:00:00     7884
##  9 2021-10-09 00:00:00     3689
## 10 2021-10-10 00:00:00     4468

Veamos la variación en un gráfico

viajes_diarios %>% 
  ggplot() + 
  geom_line(aes(x=dia, y = cantidad), color = "darkslateblue") + 
  labs(title = "Viajes por día", 
       subtitle = "Buenos Aires Octubre - Noviembre 2021", 
       caption = "Fuente: BADATA", 
       x = "", 
       y = "") + 
  scale_y_continuous(breaks = seq(0, 15000, 2000)) + 
  theme_minimal()

Detectamos un patrón en el comportamiento: se observan picos de descenso en el uso de bicicletas de manera frecuente.
Podríamos evaluar si este patrón varía según el barrio de origen y destino. Para eso vamos a evaluar cuales son los 3 orígenes destino más frecuentes.

od_frecuentes <- bicicletas_ok %>%
  group_by(barrio_estacion_origen, barrio_estacion_destino ) %>%
  summarise(cantidad =n()) %>%
  arrange(desc(cantidad)) 
## `summarise()` has grouped output by 'barrio_estacion_origen'. You can override
## using the `.groups` argument.
head(od_frecuentes, 3)
## # A tibble: 3 × 3
## # Groups:   barrio_estacion_origen [2]
##   barrio_estacion_origen barrio_estacion_destino cantidad
##   <fct>                  <fct>                      <int>
## 1 PALERMO                PALERMO                    47352
## 2 PALERMO                RECOLETA                   15524
## 3 RECOLETA               PALERMO                    14973

Vamos a filtrar esos viajes

bicicletas_filtro <- bicicletas_ok %>%
  filter((barrio_estacion_origen == "PALERMO" & barrio_estacion_destino == "PALERMO" ) |
           (barrio_estacion_origen == "PALERMO" & barrio_estacion_destino == "RECOLETA" ) |
           (barrio_estacion_origen == "RECOLETA" & barrio_estacion_destino == "PALERMO" )) 

Vamos a generar nuevamente el dataset diario

viajes_diarios_od <- bicicletas_filtro %>% 
  mutate(dia = round_date(fecha_hora_origen, "day"), 
         od = paste0(barrio_estacion_origen, " - ", barrio_estacion_destino)) %>%
  group_by(dia, od) %>% 
  summarise(cantidad =n())
## `summarise()` has grouped output by 'dia'. You can override using the `.groups`
## argument.
viajes_diarios_od %>% 
  ggplot() + 
  geom_line(aes(x=dia, y = cantidad, color = od)) + 
  labs(title = "Viajes por día", 
       subtitle = "Buenos Aires Octubre - Noviembre 2021", 
       caption = "Fuente: BADATA", 
       x = "", 
       y = "", 
       color = "Origen-Destino") + 
  theme_minimal() + 
  theme(legend.position = "bottom")

Observamos un comportamiento muy similar para los viajes Palermo-Recoleta y Recoleta-Palermo. ¿Se tratan de viajes de ida y vuelta? Vamos a evaluar la segunda semana de noviembre. Podemos utilizar la función week() para encontrar la semana.

week(ymd("2021-11-07"))
## [1] 45
recorridos_por_hora <-  bicicletas_ok %>% 
  filter(((barrio_estacion_origen == "PALERMO" & barrio_estacion_destino == "RECOLETA" ) |
           (barrio_estacion_origen == "RECOLETA" & barrio_estacion_destino == "PALERMO" )) &
           week(fecha_hora_origen) == 45) %>%
  mutate(dia_hora = round_date(fecha_hora_origen, "hour"), 
         od = paste0(barrio_estacion_origen, " - ", barrio_estacion_destino)) %>%
  group_by(dia_hora, od) %>% 
  summarise(cantidad =n())
## `summarise()` has grouped output by 'dia_hora'. You can override using the
## `.groups` argument.

Veamos los resultados en un gráfico

recorridos_por_hora %>% 
  ggplot() + 
  geom_line(aes(x=dia_hora, y = cantidad, color = od)) + 
  labs(title = "Viajes por día hora", 
       subtitle = "Buenos Aires Octubre - Noviembre 2021", 
       caption = "Fuente: BADATA", 
       x = "", 
       y = "", 
       color = "Origen-Destino") + 
  theme_minimal()

Vemos que los picos no son contrarios, sino que ocurren ambos a la tarde.

¿Los recorridos estarán relacionados con los días de la semana? La función wday() del paquete de lubridate permite calcular el día de la semana.

wday(today())
## [1] 2

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] Monday
## 7 Levels: Sunday < Monday < Tuesday < Wednesday < Thursday < ... < Saturday

Por defecto, los parámetros se encuentran de la siguiente manera: label = FALSE y abbr =TRUE
Además, y en caso que sea necesario, es posible configurar el idioma con el parámetro locale. Por ejemplo: locale = "Spanish" para Windows o locale = "es_ES.UTF-8" para IOS. También podemos ajustar el parámetro week_start para determinar el inicio de la semana. Por defecto, es week_start = 0 , es decir primero domingo.

Vamos a calcular el día de la semana en la que se iniciaron los viajes. Además vamos a incoporar una variable para que evalúe si se trata de un día laborable o si es fin de semana o feriado. Para crear esta variable, vamos a usar la función case_when() para definir de acuerdo a los casos. Nuestro dataset cuenta con los siguientes feriados:

feriados <- c(ymd("2021-10-08"), ymd("2021-10-11"), ymd("2021-11-20"), ymd("2021-11-22"))
viajes_diarios <- viajes_diarios %>%
  mutate( 
    dia = ymd(substr(dia,1,10)), 
    dia_sem = wday(dia, label = TRUE, abbr = FALSE, week_start = 1, locale = "es_ES.UTF-8"), 
    laborable = case_when(
      dia %in% feriados ~ "No laborable", 
      dia_sem %in% c("sábado", "domingo") ~ "No laborable",
      TRUE ~ "Laborable"
         ))
head(viajes_diarios, 10)
## # A tibble: 10 × 4
##    dia        cantidad dia_sem   laborable   
##    <date>        <int> <ord>     <chr>       
##  1 2021-10-01     3020 viernes   Laborable   
##  2 2021-10-02     8492 sábado    No laborable
##  3 2021-10-03     1832 domingo   No laborable
##  4 2021-10-04     5055 lunes     Laborable   
##  5 2021-10-05    10717 martes    Laborable   
##  6 2021-10-06    11368 miércoles Laborable   
##  7 2021-10-07    10091 jueves    Laborable   
##  8 2021-10-08     7884 viernes   No laborable
##  9 2021-10-09     3689 sábado    No laborable
## 10 2021-10-10     4468 domingo   No laborable
viajes_diarios %>% 
  ggplot() + 
  geom_col(aes(x=dia, y = cantidad, fill = laborable) )+ 
  labs(title = "Viajes por día", 
       subtitle = "Buenos Aires Octubre - Noviembre 2021", 
       caption = "Fuente: BADATA", 
       x = "", 
       y = "", 
       fill = "Tipo de jornada") + 
  scale_y_continuous(breaks = seq(0, 15000, 2500)) + 
  scale_fill_brewer(palette = 7, type = "qual") + 
  theme_minimal() + 
  theme(legend.position = "bottom")

Se observa un menor uso de las bicicletas públicas en días no laborables. ¿Las bicicletas se utilizan como movilidad de última milla en viajes laborales? Veamos la cantidad de recorridos según horario de inicio del recorrido.

bicicletas_ok <- bicicletas_ok %>% 
  mutate(hora_inicio = hour(fecha_hora_origen), 
         dia = ymd(substr(fecha_hora_origen,1,10)), 
         dia_sem = wday(dia, label = TRUE, abbr = FALSE, week_start = 1, locale = "es_ES.UTF-8"), 
         laborable = case_when(
           dia %in% feriados ~ "No laborable", 
           dia_sem %in% c("sábado", "domingo") ~ "No laborable",
           TRUE ~ "Laborable"
         ))
bicicletas_ok %>% 
  ggplot() + 
  geom_bar(aes(x=hora_inicio)) + 
  labs(title = "Recorridos en bicicleta según hora de origen", 
       subtitle = "Buenos Aires Octubre - Noviembre 2021", 
       caption = "Fuente: BADATA", 
       fill = "",
       x = "Hora", 
       y = "Cantidad (en miles)") + 
  scale_y_continuous(breaks =seq(0,100000, 10000), labels = seq(0, 100, 10)) + 
  scale_x_continuous(breaks = seq(0,23,2)) + 
  theme_minimal() + 
  facet_wrap(~laborable)

Finalmente, vamos a calcular la duración del recorrido, y evaluar si se modifica según día de la semana. Para eso, vamos a realizar una resta entre la hora de finalización y la hora de origen.

bicicletas_ok <- bicicletas_ok %>%
  mutate(duracion_min = round(fecha_hora_destino - fecha_hora_origen, digits = 1))

¿Cómo se distribuye la duración de recorridos?

summary(as.numeric(bicicletas_ok$duracion_min))
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##    1.00   10.20   16.40   20.45   25.50 7258.40

Hay algunos recorridos que superan las 10hs. Incluso el máximo llega a 7258.4 minutos. Para gráficar, se filtran los recorridos de más de 10hs (600 min)

bicicletas_ok %>% 
  filter(duracion_min <= 600) %>%
  ggplot() + 
  geom_histogram(aes(x=duracion_min)) + 
  labs(title = "Distribución de la duración de los recorridos", 
       subtitle = "Buenos Aires Octubre - Noviembre 2021", 
       caption = "Fuente: BADATA", 
       fill = "",
       x = "Hora", 
       y = "Cantidad (en miles)") + 
  scale_x_continuous(breaks =seq(0,600, 100)) + 
  scale_y_continuous(breaks = seq(0,600000,50000), labels = seq(0,600,50)) + 
  theme_minimal() + 
  facet_wrap(~laborable)
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

Vemos que la mayoría de los viajes tienen una duración de menos de 2hs (120min). ¿Varía según día de la semana?

bicicletas_ok %>%
  filter(duracion_min <= 120) %>% 
  ggplot() + 
  geom_boxplot(aes(x = dia_sem, 
                   y=duracion_min, 
                   fill = as.character(dia_sem))) + 
  labs(title = "Duración de los recorridos", 
       y = "Duración (min)", 
       x = "Día de inicio") + 
  theme_minimal() + 
  scale_y_continuous(breaks = seq(0,130,20)) + 
  scale_fill_brewer(palette = 8, type = "qual") + 
  theme(legend.position = "none")

Podemos observar que el valor medio de los viajes es muy similar para los días de semana, y el comportamiento difiere levemente los fin de semana.

¡Con el paquete ‘lubridate’ podemos trabajar sencillamente con las variables de tipo fecha!