Si bien cuando vemos fechas las podemos entender fácilmente, estos campos suelen ser formatos difíciles de trabajar en R. Sin embargo, el paquete lubridate nos permite poder manipularlas mejor. 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)
# install.packages("lubridate")
library(lubridate)

Vamos a trabajar con datos de Blue Bike, empresa prestataria de los servicios de bicicletas públicas en la ciudad de Boston, Massacusetts, USA. Se agregaron los datos de Enero a Abril 20201.  

El dataset puede descargarse desde aqui.
También puede obtenerse desde una query a data.world:
bikes2021 <- read.csv(“https://query.data.world/s/lgh5y23j2ym4zvkhqfi4z75wqtxwnp”, header=TRUE)

bikes2021 <- read.csv("bikes.csv", header = TRUE)

En primer lugar vamos a inspeccionar nuestra base de datos:

head(bikes2021)
##   tripduration                starttime                 stoptime
## 1          914 2021-01-01 00:00:04.5900 2021-01-01 00:15:19.1680
## 2         1085 2021-01-01 00:00:21.8030 2021-01-01 00:18:27.4640
## 3          946 2021-01-01 00:00:26.0090 2021-01-01 00:16:12.0900
## 4          355 2021-01-01 00:00:30.9210 2021-01-01 00:06:26.6000
## 5          511 2021-01-01 00:01:11.2270 2021-01-01 00:09:43.1950
## 6         1004 2021-01-01 00:01:52.3100 2021-01-01 00:18:36.4180
##   start.station.id                                          start.station.name
## 1               91            One Kendall Square at Hampshire St / Portland St
## 2              370                                  Dartmouth St at Newbury St
## 3               46 Christian Science Plaza - Massachusetts Ave at Westland Ave
## 4              178                             MIT Pacific St at Purrington St
## 5              386                     Sennott Park Broadway at Norfolk Street
## 6              105           Lower Cambridgeport at Magazine St / Riverside Rd
##   start.station.latitude start.station.longitude end.station.id
## 1               42.36628               -71.09169            370
## 2               42.35096               -71.07783            169
## 3               42.34367               -71.08582             21
## 4               42.35957               -71.10129            107
## 5               42.36861               -71.09930            413
## 6               42.35722               -71.11387             55
##                          end.station.name end.station.latitude
## 1              Dartmouth St at Newbury St             42.35096
## 2 Edwards Playground - Main St at Eden St             42.37897
## 3  Prudential Center - 101 Huntington Ave             42.34652
## 4                      Ames St at Main St             42.36250
## 5 Kennedy-Longfellow School 158 Spring St             42.36955
## 6        Boylston St at Massachusetts Ave             42.34741
##   end.station.longitude bikeid   usertype postal.code
## 1             -71.07783   5316   Customer       02139
## 2             -71.06861   4917 Subscriber       02116
## 3             -71.08066   2881   Customer       02115
## 4             -71.08822   4792 Subscriber       02139
## 5             -71.08579   6062 Subscriber       02139
## 6             -71.08678   4371 Subscriber       02139

Existen tres tipos de formato de fechas:
- fecha
- hora
- fecha-hora

En nuestro dataset las variables starttime y stoptime están en formato fecha-hora. Sin embargo, R aún no las reconoce:

class(bikes2021$starttime)
## [1] "character"

Por el momento es una variable tipo caracter.  

Veamos algunas funciones de fechas del paquete lubridate:
- today() : nos indica la fecha de hoy
- now(): nos indica la fecha y hora de ahora

today()
## [1] "2021-07-27"
class(today())
## [1] "Date"

Vemos que la clase es fecha.

now()
## [1] "2021-07-27 20:10:25 -03"
class(now())
## [1] "POSIXct" "POSIXt"

POSIXct es una clase de datos de fecha y hora.
 

Vamos a transformar nuestra variable starttime en formato fecha-hora. 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 aquí
Para facilitar la lectura, vamos a tomar una muestra de 10 recorridos con la función sample_n() y vamos a seleccionar solo el campo starttime que vamos a renombrar como ‘original’

set.seed(1)
bikes_sample <- bikes2021 %>% 
  sample_n(10)  %>% 
  select(starttime) %>% 
  rename(original = starttime)
bikes_sample
##                    original
## 1  2021-01-12 11:19:41.1070
## 2  2021-04-30 02:01:48.3390
## 3  2021-02-26 14:06:25.0460
## 4  2021-04-27 11:49:19.8170
## 5  2021-04-10 21:18:16.3310
## 6  2021-03-04 22:29:25.0680
## 7  2021-01-12 16:34:11.8430
## 8  2021-04-05 21:06:16.7970
## 9  2021-04-20 23:35:25.9130
## 10 2021-02-23 23:12:51.4980

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().

bikes_sample <- bikes_sample %>%  
  mutate(fecha_hora = ymd_hms(original))
bikes_sample
##                    original          fecha_hora
## 1  2021-01-12 11:19:41.1070 2021-01-12 11:19:41
## 2  2021-04-30 02:01:48.3390 2021-04-30 02:01:48
## 3  2021-02-26 14:06:25.0460 2021-02-26 14:06:25
## 4  2021-04-27 11:49:19.8170 2021-04-27 11:49:19
## 5  2021-04-10 21:18:16.3310 2021-04-10 21:18:16
## 6  2021-03-04 22:29:25.0680 2021-03-04 22:29:25
## 7  2021-01-12 16:34:11.8430 2021-01-12 16:34:11
## 8  2021-04-05 21:06:16.7970 2021-04-05 21:06:16
## 9  2021-04-20 23:35:25.9130 2021-04-20 23:35:25
## 10 2021-02-23 23:12:51.4980 2021-02-23 23:12:51
class(bikes_sample$fecha_hora)
## [1] "POSIXct" "POSIXt"

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

Vamos a crear ahora un nuevo campo que solo contenga la fecha con la función substr()

bikes_sample <- bikes_sample %>% 
  mutate(solo_fecha = substr(original, 1, 10))
bikes_sample
##                    original          fecha_hora solo_fecha
## 1  2021-01-12 11:19:41.1070 2021-01-12 11:19:41 2021-01-12
## 2  2021-04-30 02:01:48.3390 2021-04-30 02:01:48 2021-04-30
## 3  2021-02-26 14:06:25.0460 2021-02-26 14:06:25 2021-02-26
## 4  2021-04-27 11:49:19.8170 2021-04-27 11:49:19 2021-04-27
## 5  2021-04-10 21:18:16.3310 2021-04-10 21:18:16 2021-04-10
## 6  2021-03-04 22:29:25.0680 2021-03-04 22:29:25 2021-03-04
## 7  2021-01-12 16:34:11.8430 2021-01-12 16:34:11 2021-01-12
## 8  2021-04-05 21:06:16.7970 2021-04-05 21:06:16 2021-04-05
## 9  2021-04-20 23:35:25.9130 2021-04-20 23:35:25 2021-04-20
## 10 2021-02-23 23:12:51.4980 2021-02-23 23:12:51 2021-02-23

El nuevo campo que creamos está escrito en formato: año(year), mes(month), día(day).
Se utiliza entonces la función ymd().

bikes_sample <- bikes_sample %>%  
  mutate(fecha = ymd(solo_fecha))
bikes_sample
##                    original          fecha_hora solo_fecha      fecha
## 1  2021-01-12 11:19:41.1070 2021-01-12 11:19:41 2021-01-12 2021-01-12
## 2  2021-04-30 02:01:48.3390 2021-04-30 02:01:48 2021-04-30 2021-04-30
## 3  2021-02-26 14:06:25.0460 2021-02-26 14:06:25 2021-02-26 2021-02-26
## 4  2021-04-27 11:49:19.8170 2021-04-27 11:49:19 2021-04-27 2021-04-27
## 5  2021-04-10 21:18:16.3310 2021-04-10 21:18:16 2021-04-10 2021-04-10
## 6  2021-03-04 22:29:25.0680 2021-03-04 22:29:25 2021-03-04 2021-03-04
## 7  2021-01-12 16:34:11.8430 2021-01-12 16:34:11 2021-01-12 2021-01-12
## 8  2021-04-05 21:06:16.7970 2021-04-05 21:06:16 2021-04-05 2021-04-05
## 9  2021-04-20 23:35:25.9130 2021-04-20 23:35:25 2021-04-20 2021-04-20
## 10 2021-02-23 23:12:51.4980 2021-02-23 23:12:51 2021-02-23 2021-02-23

Por último, vamos a crear un último formato de fecha:

bikes_sample <- bikes_sample %>% 
  mutate(solo_fecha2 = 
           paste0(substr(solo_fecha,9,10), "/", substr(solo_fecha, 6,7), "/", substr(solo_fecha,1,4)))
bikes_sample
##                    original          fecha_hora solo_fecha      fecha
## 1  2021-01-12 11:19:41.1070 2021-01-12 11:19:41 2021-01-12 2021-01-12
## 2  2021-04-30 02:01:48.3390 2021-04-30 02:01:48 2021-04-30 2021-04-30
## 3  2021-02-26 14:06:25.0460 2021-02-26 14:06:25 2021-02-26 2021-02-26
## 4  2021-04-27 11:49:19.8170 2021-04-27 11:49:19 2021-04-27 2021-04-27
## 5  2021-04-10 21:18:16.3310 2021-04-10 21:18:16 2021-04-10 2021-04-10
## 6  2021-03-04 22:29:25.0680 2021-03-04 22:29:25 2021-03-04 2021-03-04
## 7  2021-01-12 16:34:11.8430 2021-01-12 16:34:11 2021-01-12 2021-01-12
## 8  2021-04-05 21:06:16.7970 2021-04-05 21:06:16 2021-04-05 2021-04-05
## 9  2021-04-20 23:35:25.9130 2021-04-20 23:35:25 2021-04-20 2021-04-20
## 10 2021-02-23 23:12:51.4980 2021-02-23 23:12:51 2021-02-23 2021-02-23
##    solo_fecha2
## 1   12/01/2021
## 2   30/04/2021
## 3   26/02/2021
## 4   27/04/2021
## 5   10/04/2021
## 6   04/03/2021
## 7   12/01/2021
## 8   05/04/2021
## 9   20/04/2021
## 10  23/02/2021

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

bikes_sample <- bikes_sample %>%  
  mutate(fecha2 = dmy(solo_fecha2))
bikes_sample
##                    original          fecha_hora solo_fecha      fecha
## 1  2021-01-12 11:19:41.1070 2021-01-12 11:19:41 2021-01-12 2021-01-12
## 2  2021-04-30 02:01:48.3390 2021-04-30 02:01:48 2021-04-30 2021-04-30
## 3  2021-02-26 14:06:25.0460 2021-02-26 14:06:25 2021-02-26 2021-02-26
## 4  2021-04-27 11:49:19.8170 2021-04-27 11:49:19 2021-04-27 2021-04-27
## 5  2021-04-10 21:18:16.3310 2021-04-10 21:18:16 2021-04-10 2021-04-10
## 6  2021-03-04 22:29:25.0680 2021-03-04 22:29:25 2021-03-04 2021-03-04
## 7  2021-01-12 16:34:11.8430 2021-01-12 16:34:11 2021-01-12 2021-01-12
## 8  2021-04-05 21:06:16.7970 2021-04-05 21:06:16 2021-04-05 2021-04-05
## 9  2021-04-20 23:35:25.9130 2021-04-20 23:35:25 2021-04-20 2021-04-20
## 10 2021-02-23 23:12:51.4980 2021-02-23 23:12:51 2021-02-23 2021-02-23
##    solo_fecha2     fecha2
## 1   12/01/2021 2021-01-12
## 2   30/04/2021 2021-04-30
## 3   26/02/2021 2021-02-26
## 4   27/04/2021 2021-04-27
## 5   10/04/2021 2021-04-10
## 6   04/03/2021 2021-03-04
## 7   12/01/2021 2021-01-12
## 8   05/04/2021 2021-04-05
## 9   20/04/2021 2021-04-20
## 10  23/02/2021 2021-02-23

Podemos observar que las variables fecha y fecha2 son iguales. El formato default para las fechas de R es año-mes-día.  
 

Supongamos ahora que los valores de año, mes y día están separados:

bikes_sample2 <- bikes_sample %>% 
  select(original) %>% 
  mutate(anio = substr(original, 1,4),  #evitamos la ñ en R, a veces se generan errores
         mes= substr(original, 6,7), 
         dia = substr(original, 9,10)) 
bikes_sample2
##                    original anio mes dia
## 1  2021-01-12 11:19:41.1070 2021  01  12
## 2  2021-04-30 02:01:48.3390 2021  04  30
## 3  2021-02-26 14:06:25.0460 2021  02  26
## 4  2021-04-27 11:49:19.8170 2021  04  27
## 5  2021-04-10 21:18:16.3310 2021  04  10
## 6  2021-03-04 22:29:25.0680 2021  03  04
## 7  2021-01-12 16:34:11.8430 2021  01  12
## 8  2021-04-05 21:06:16.7970 2021  04  05
## 9  2021-04-20 23:35:25.9130 2021  04  20
## 10 2021-02-23 23:12:51.4980 2021  02  23

Las funciones make_date() y make_datetime() nos permiten unir los campos rápidamente. Como no tenemos hora, vamos a usar make_date().

bikes_sample2 <- bikes_sample2 %>% 
  mutate(fecha = make_date(year= anio, month = mes, day = dia))
bikes_sample2
##                    original anio mes dia      fecha
## 1  2021-01-12 11:19:41.1070 2021  01  12 2021-01-12
## 2  2021-04-30 02:01:48.3390 2021  04  30 2021-04-30
## 3  2021-02-26 14:06:25.0460 2021  02  26 2021-02-26
## 4  2021-04-27 11:49:19.8170 2021  04  27 2021-04-27
## 5  2021-04-10 21:18:16.3310 2021  04  10 2021-04-10
## 6  2021-03-04 22:29:25.0680 2021  03  04 2021-03-04
## 7  2021-01-12 16:34:11.8430 2021  01  12 2021-01-12
## 8  2021-04-05 21:06:16.7970 2021  04  05 2021-04-05
## 9  2021-04-20 23:35:25.9130 2021  04  20 2021-04-20
## 10 2021-02-23 23:12:51.4980 2021  02  23 2021-02-23

Podríamos no tener alguna de las variables, y en ese caso agregarla nosotros. Por ejemplo, si no tuviesemos el año, pero sabemos que son datos del 2021, lo podríamos incorporar nosotros.

bikes_sample2 <- bikes_sample2 %>% 
  mutate(fecha2 = make_date(year= 2021, month = mes, day = dia))
bikes_sample2
##                    original anio mes dia      fecha     fecha2
## 1  2021-01-12 11:19:41.1070 2021  01  12 2021-01-12 2021-01-12
## 2  2021-04-30 02:01:48.3390 2021  04  30 2021-04-30 2021-04-30
## 3  2021-02-26 14:06:25.0460 2021  02  26 2021-02-26 2021-02-26
## 4  2021-04-27 11:49:19.8170 2021  04  27 2021-04-27 2021-04-27
## 5  2021-04-10 21:18:16.3310 2021  04  10 2021-04-10 2021-04-10
## 6  2021-03-04 22:29:25.0680 2021  03  04 2021-03-04 2021-03-04
## 7  2021-01-12 16:34:11.8430 2021  01  12 2021-01-12 2021-01-12
## 8  2021-04-05 21:06:16.7970 2021  04  05 2021-04-05 2021-04-05
## 9  2021-04-20 23:35:25.9130 2021  04  20 2021-04-20 2021-04-20
## 10 2021-02-23 23:12:51.4980 2021  02  23 2021-02-23 2021-02-23

Vamos a retomar nuestro dataset original para transformar los campos starttime y stoptime a fecha-hora. Vamos a seleccionar los campos bikeid, usertype y los dos nuevos campos de fecha.

bikes2021_modificada <- bikes2021 %>% 
  mutate(inicio= ymd_hms(starttime), 
         final = ymd_hms(stoptime)) %>% 
    select(bikeid, usertype, tripduration, inicio, final) 

Ahora que ya sabemos transformar los datos a formato fecha, vamos a utilizar otras funciones de ‘lubridate’. Por ejemplo, la función round_date() nos permite redondear la fecha. La vamos a usar para ver la evolución de la cantidad de viajes diarios.

viajes_por_dia <- bikes2021_modificada %>% 
  mutate(dia = round_date(inicio, "day")) %>% 
  group_by(dia) %>% 
  summarise(cantidad =n())
## `summarise()` ungrouping output (override with `.groups` argument)
head(viajes_por_dia)
## # A tibble: 6 x 2
##   dia                 cantidad
##   <dttm>                 <int>
## 1 2021-01-01 00:00:00      525
## 2 2021-01-02 00:00:00     1608
## 3 2021-01-03 00:00:00     1811
## 4 2021-01-04 00:00:00     1371
## 5 2021-01-05 00:00:00     2101
## 6 2021-01-06 00:00:00     2030
viajes_por_dia %>% 
  ggplot() + 
  geom_line(aes(x=dia, y = cantidad), color = "darkslateblue") + 
  labs(title = "Viajes por día", 
       subtitle = "Boston 2021", 
       caption = "Fuente: BlueBike", 
       x = "", 
       y = "") + 
  scale_y_continuous(breaks = seq(0, 16000, 1000)) + 
  theme_minimal()

Vamos a utilizar otras funciones para poder explorar mejor los datos.
Las funciones year(), month(), day(), hour(), minute(), y second() nos permiten extraer ese componente de la fecha.
A partir de estas funciones, vamos a evaluar la cantidad de recorridos por mes de inicio, y por hora inicial y final.

bikes2021_modificada <- bikes2021_modificada %>% 
  mutate(mes = month(inicio), 
         hora_inicial = hour(inicio), 
         hora_final = hour(final))
head(bikes2021_modificada)
##   bikeid   usertype tripduration              inicio               final mes
## 1   5316   Customer          914 2021-01-01 00:00:04 2021-01-01 00:15:19   1
## 2   4917 Subscriber         1085 2021-01-01 00:00:21 2021-01-01 00:18:27   1
## 3   2881   Customer          946 2021-01-01 00:00:26 2021-01-01 00:16:12   1
## 4   4792 Subscriber          355 2021-01-01 00:00:30 2021-01-01 00:06:26   1
## 5   6062 Subscriber          511 2021-01-01 00:01:11 2021-01-01 00:09:43   1
## 6   4371 Subscriber         1004 2021-01-01 00:01:52 2021-01-01 00:18:36   1
##   hora_inicial hora_final
## 1            0          0
## 2            0          0
## 3            0          0
## 4            0          0
## 5            0          0
## 6            0          0

Si bien nosotros sabemos que el mes 1 es enero, prefeririamos verlo en texto. Dentro de la función month() se encuentran los parámetros ‘label’ y ‘abb’.
Veamos un ejemplo con today():

month(today())
## [1] 7
month(today(), label = TRUE)
## [1] Jul
## 12 Levels: Jan < Feb < Mar < Apr < May < Jun < Jul < Aug < Sep < ... < Dec

Como pueden ver, los textos están en inglés. Los queremos en español y no los queremos abreviados.

month(today(), label = TRUE, abbr = FALSE, locale = "es_ES.UTF-8")
## [1] julio
## 12 Levels: enero < febrero < marzo < abril < mayo < junio < ... < diciembre
#para Windows utilizar: "Spanish" 

¡Perfecto! Ahora volvamos a nuestra base:

bikes2021_modificada <- bikes2021_modificada %>% 
  mutate(mes = month(inicio, abbr = FALSE, label = TRUE, locale = "es_ES.UTF-8"), 
         hora_inicial = hour(inicio), 
         hora_final = hour(final))
head(bikes2021_modificada)
##   bikeid   usertype tripduration              inicio               final   mes
## 1   5316   Customer          914 2021-01-01 00:00:04 2021-01-01 00:15:19 enero
## 2   4917 Subscriber         1085 2021-01-01 00:00:21 2021-01-01 00:18:27 enero
## 3   2881   Customer          946 2021-01-01 00:00:26 2021-01-01 00:16:12 enero
## 4   4792 Subscriber          355 2021-01-01 00:00:30 2021-01-01 00:06:26 enero
## 5   6062 Subscriber          511 2021-01-01 00:01:11 2021-01-01 00:09:43 enero
## 6   4371 Subscriber         1004 2021-01-01 00:01:52 2021-01-01 00:18:36 enero
##   hora_inicial hora_final
## 1            0          0
## 2            0          0
## 3            0          0
## 4            0          0
## 5            0          0
## 6            0          0

Vamos a hacer un gráfico .

bikes2021_modificada %>% 
  ggplot() + 
  geom_bar(aes( x= mes, fill = mes)) + 
  labs(title = "Recorridos en bicicleta por mes", 
       subtitle = "Boston 2021", 
       caption = "Fuente: BlueBike", 
       x = "Mes", 
       y = "Cantidad (en miles)") + 
  scale_fill_brewer(palette = 8, type = "qual") + 
  scale_y_continuous(breaks =seq(0,200000, 20000), labels = seq(0, 200, 20)) + 
  theme_minimal() + 
  theme(legend.position = "none") 

Podríamos haber hecho el gráfico sin crear la nueva categoría ‘mes’:

bikes2021_modificada %>% 
  ggplot() + 
  geom_bar(aes( x= month(inicio, abbr = FALSE, label = TRUE, locale = "es_ES.UTF-8"), fill = mes)) + 
  labs(title = "Recorridos en bicicleta por mes", 
       subtitle = "Boston 2021", 
       caption = "Fuente: BlueBike", 
       x = "Mes", 
       y = "Cantidad (en miles)") + 
  scale_fill_brewer(palette = 8, type = "qual") + 
  scale_y_continuous(breaks =seq(0,200000, 20000), labels = seq(0, 200, 20)) + 
  theme_minimal() + 
  theme(legend.position = "none") 

Ahora vamos a ver la distribución por hora de inicio del recorrido:

bikes2021_modificada %>% 
  ggplot() + 
  geom_bar(aes(x=hora_inicial)) + 
  labs(title = "Recorridos en bicicleta por hora", 
       subtitle = "Boston - Enero a Abril 2021", 
       caption = "Fuente: BlueBike", 
       x = "Hora de inicio", 
       y = "Cantidad (en miles)") + 
  scale_y_continuous(breaks =seq(0,40000, 5000), labels = seq(0, 40, 5)) + 
  theme_minimal() 

¿La distribución depende del tipo de usuario?

bikes2021_modificada %>% 
  ggplot() + 
  geom_bar(aes(x=hora_inicial, fill = usertype)) + 
  labs(title = "Recorridos en bicicleta por hora", 
       subtitle = "Boston - Enero a Abril 2021", 
       caption = "Fuente: BlueBike", 
       fill = "Tipo de usuario",
       x = "Hora de inicio", 
       y = "Cantidad (en miles)") + 
  scale_y_continuous(breaks =seq(0,40000, 5000), labels = seq(0, 40, 5)) + 
  scale_fill_manual(values = c("darkolivegreen4", "darkslategray3"), labels = c("Cliente", "Abonado")) + 
  theme_minimal() 

¿El patrón es similar para la hora de final de recorrido?

bikes2021_modificada %>% 
  ggplot() + 
  geom_bar(aes(x=hora_final, fill = usertype)) + 
  labs(title = "Recorridos en bicicleta por hora", 
       subtitle = "Boston - Enero a Abril 2021", 
       caption = "Fuente: BlueBike", 
       fill = "Tipo de usuario",
       x = "Hora de finalización", 
       y = "Cantidad (en miles)") + 
  scale_y_continuous(breaks =seq(0,40000, 5000), labels = seq(0, 40, 5)) + 
  scale_fill_manual(values = c("darkolivegreen4", "darkslategray3"), labels = c("Cliente", "Abonado")) + 
  theme_minimal() 

Necesitamos modificar el dataset para que la comparación sea más sencilla. Para eso vamos a usar la función gather(). Aquí pueden ver como utilizar las funciones gather() y spread().

bikes2021_modificada %>% 
  select(hora_inicial, hora_final) %>% 
  gather(key = "tipo", value = "hora", 1:2) %>% 
  ggplot() + 
  geom_bar(aes(x=hora, fill=tipo), position = position_dodge(preserve = "single")) + 
  labs(title = "Recorridos en bicicleta por hora", 
       subtitle = "Boston - Enero a Abril 2021", 
       caption = "Fuente: BlueBike", 
       fill = "",
       x = "Hora", 
       y = "Cantidad (en miles)") + 
  scale_y_continuous(breaks =seq(0,40000, 5000), labels = seq(0, 40, 5)) + 
  scale_x_continuous(breaks = seq(0,23,1)) + 
  scale_fill_manual(values = c("darkolivegreen4", "darkslategray3"), labels = c("Final", "Inicial")) + 
  theme_minimal() 

También podríamos verlo con un gráfino de líneas. Para ello vamos a realizar un conteo por hora de inicio y por hora de finalización

df_hora <- bikes2021_modificada %>% 
  gather(key = "tipo", value = "hora", hora_inicial, hora_final) %>% 
  group_by(hora, tipo) %>% 
  summarise(duracion_promedio = round(mean(tripduration)/60, digits = 0), 
            cantidad =n())
## `summarise()` regrouping output by 'hora' (override with `.groups` argument)
head(df_hora)
## # A tibble: 6 x 4
## # Groups:   hora [3]
##    hora tipo         duracion_promedio cantidad
##   <int> <chr>                    <dbl>    <int>
## 1     0 hora_final                  25    23233
## 2     0 hora_inicial                26    20362
## 3     1 hora_final                  33    15416
## 4     1 hora_inicial                26    14155
## 5     2 hora_final                  32    10971
## 6     2 hora_inicial                29    10340

Ahora si, a graficar:

ggplot() + 
  geom_line(data = df_hora , aes(x = hora, y =cantidad, color =  tipo)) +
  labs(title = "Recorridos en bicicleta por hora", 
       subtitle = "Boston - Enero a Abril 2021", 
       caption = "Fuente: BlueBike", 
       color = "Hora",
       x = "Hora", 
       y = "Cantidad (en miles)") + 
  scale_y_continuous(breaks =seq(0,40000, 5000), labels = seq(0, 40, 5)) + 
  scale_x_continuous(breaks = seq(0,23,1)) + 
  scale_color_manual(values = c("darkolivegreen4", "darkslategray3"), labels = c("Final", "Inicial")) + 
  theme_minimal() 

Ahora veamos la duración promedio de los recorridos:

ggplot() + 
  geom_line(data = df_hora , aes(x = hora, y =duracion_promedio, color =  tipo)) +
  labs(title = "Recorridos en bicicleta por hora", 
       subtitle = "Boston - Enero a Abril 2021", 
       caption = "Fuente: BlueBike", 
       color = "Hora",
       x = "Hora", 
       y = "Duracion (en minutos)") + 
  scale_x_continuous(breaks = seq(0,23,1)) + 
  scale_color_manual(values = c("darkolivegreen4", "darkslategray3"), labels = c("Final", "Inicial")) + 
  theme_minimal() 

Podemos observar un patrón en la cantidad de viajes por hora, y alteraciones en la duración promedio. Vamos a analizar a continuación que ocurre según el día de la semana. Para ello, vamos a utilizar la función wday() que permite obtener el día de la semana.
Probemosla con nuestra muestra anterior:

wday(bikes_sample$fecha)
##  [1] 3 6 6 3 7 5 3 2 3 3

Al igual que la función mes, nos dice el número de día. Es poco intuitivo, asi que vamos a usar ‘abbr’ y ‘label’.

wday(bikes_sample$fecha, abbr = FALSE, label = TRUE, locale ="es_ES.UTF-8" )
##  [1] martes  viernes viernes martes  sábado  jueves  martes  lunes   martes 
## [10] martes 
## 7 Levels: domingo < lunes < martes < miércoles < jueves < ... < sábado

Investiguemos la cantidad de viajes por día:

bikes2021_modificada %>% 
  ggplot() + 
  geom_bar(aes( x= wday(inicio, abbr = FALSE, label = TRUE, locale = "es_ES.UTF-8"), fill = wday(inicio))) + 
  labs(title = "Recorridos en bicicleta por mes", 
       subtitle = "Boston - Enero a Abril 2021", 
       caption = "Fuente: BlueBike", 
       x = "Día de la semana", 
       y = "Cantidad (en miles)") + 
  scale_fill_distiller(palette = 8, type = "qual") + 
  scale_y_continuous(breaks =seq(0,200000, 20000), labels = seq(0, 200, 20)) + 
  theme_minimal() + 
  theme(legend.position = "none") 
## Warning: Using a discrete colour palette in a continuous scale.
##   Consider using type = "seq" or type = "div" instead

Veamos la variabilidad por mes:

bikes2021_modificada %>% 
  ggplot() + 
  geom_bar(aes( x= wday(inicio, abbr = FALSE, label = TRUE, locale = "es_ES.UTF-8"), fill = wday(inicio))) + 
  labs(title = "Recorridos en bicicleta por mes", 
       subtitle = "Boston - Enero a Abril 2021", 
       caption = "Fuente: BlueBike", 
       x = "Día de la semana", 
       y = "Cantidad (en miles)") + 
  scale_fill_distiller(palette = 8, type = "qual") + 
  scale_y_continuous(breaks =seq(0,40000, 5000), labels = seq(0, 40, 5)) + 
  theme_minimal() + 
  theme(legend.position = "none")  + 
  facet_wrap(~month(inicio, label = TRUE, abbr = FALSE, locale = "es_ES.UTF-8"))
## Warning: Using a discrete colour palette in a continuous scale.
##   Consider using type = "seq" or type = "div" instead

Excepto en el mes de Abril, no parecería haber variabilidad según el día de la semana.
 

Por último, queremos evaluar como se comporta la duración de recorridos en relación a si se trata de un fin de semana o no. Si bien contamos contamos con la variable tripduration en segundos, vamos a generar un nuevo cálculo a partir de la función time_length() del paquete lubridate

bikes2021_modificada <- bikes2021_modificada %>% 
  mutate(duracion = round(time_length(final - inicio, unit = "minutes"), digits = 1))
head(bikes2021_modificada$duracion)
## [1] 15.2 18.1 15.8  5.9  8.5 16.7

Primero vamos a ver la distribución de la duración.

bikes2021_modificada%>%
  ggplot() + 
  geom_boxplot(aes(y=duracion, x = ""))

Uno de los recorridos superó los 50.000min es decir las 833 horas o los 34 días. Nos parece muy poco probable que alguien esté utilizando la bicicleta durante tanto tiempo.
Vamos a filtrar registros inferiores a 2 horas o los 120 minutos.

bikes2021_modificada%>%
  filter(duracion <= 120) %>% 
  ggplot() + 
  geom_boxplot(aes(y=duracion, x = "")) + 
  labs(title = "Duración de los recorridos", 
       y = "Duración (min)", 
       x = "") + 
  scale_y_continuous(breaks = seq(0,130,20)) + 
  theme_minimal()

La gran mayoría de los registros se encuentra por debajo de los 50min. ¿Esta distribución depende del día de la semana?

bikes2021_modificada%>%
  filter(duracion <= 120) %>% 
  ggplot() + 
  geom_boxplot(aes(x = wday(inicio, abbr = FALSE, label = TRUE, locale = "es_ES.UTF-8"), 
                   y=duracion, 
                   fill = as.character(wday(inicio)))) + 
  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 modificar el día de inicio de la semana, ya que preferimos que sea lunes.

bikes2021_modificada%>%
  filter(duracion <= 120) %>% 
  ggplot() + 
  geom_boxplot(aes(x = wday(inicio, abbr = FALSE, label = TRUE, locale = "es_ES.UTF-8", week_start = 1), 
                   y=duracion, 
                   fill = as.character(wday(inicio)))) + 
  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.

 
 

¡Como vieron, con el paque ‘lubridate’ podemos trabajar sencillamente con las variables de tipo fecha!

 

BONUS TRACK!

  Podemos hacer un gráfico animado (gif)

gif <- function() {
  iterseq <- seq(0,23,1)
  for (i in iterseq) {
        plot <- ggplot() + 
          geom_line(data = df_hora , aes(x = hora, y =duracion_promedio, color =  tipo)) +
          geom_point(data = df_hora %>% filter(hora == i), 
                     aes(x = i, y = duracion_promedio, color = tipo), size = 4) + 
          labs(title = "Recorridos en bicicleta por hora", 
               subtitle = "Boston - Enero a Abril 2021", 
               caption = "Fuente: BlueBike", 
               color = "Hora",
               x = "Hora", 
               y = "Duracion (en minutos)") + 
          scale_x_continuous(breaks = seq(0,23,1)) + 
          scale_color_manual(values = c("darkolivegreen4", "darkslategray3"), 
                             labels = c("Final", "Inicial")) + 
          theme_minimal() 
        print(plot)
      
  }
}

Cargamos la librería para guardar y leer gif:

#install.packages("gifski")
library(gifski)

Ahora guardamos el gif

save_gif(gif(), "grafico_animado.gif", delay = 0.5, width = 600, height = 400, progress = FALSE)
knitr::include_graphics("grafico_animado.gif")