1  Regresión

Presentamos un ejemplo de modelos de regresión a partir de datos reales de temperatura y uso del sistema de bicicletas Ecobici de la Ciudad Autónoma de Buenos Aires.

Cómo MidJourney imagina que se ilustra este problema bajo el prompt: /imagine an enchanting illustration of Buenos Aires, with the obelisk at the center of the image, and colorful weather elements interacting with a road and a bicycle path.

A continuación, se muestran algunos posibles ajustes de regresión que vinculan la cantidad de usuarios del sistema Ecobici por día en función de la temperatura, distinguiendo por usos en días de semana o fin de semana.

Código
rm(list=ls())
# Librerías necesarias
require(tidyverse)
require(ggfortify)
require(plotly)
require(kableExtra)
require(knitr)
require(devtools)
options(scipen=999)

1.1 Exploración inicial

En el dataset tempbici de la librería datosIC se incluyen datos de viajes con duración entre 5 y 60 minutos, cualquier día de la semana, desde el 4 de junio de 2022 hasta el 1ro de mayo de 2023. A continuación se muestran 10 datos de dicho conjunto.

Código
devtools::install_github("daniellaparada/datosIC")
library(datosIC)
datos <- tempbici
Dataset reducido
fecha n tmin tmax tmed dia tipo_dia
2022-06-04 2495 9.2 15.1 12.15 sábado Fin de semana
2022-06-05 1925 9.1 14.8 11.95 domingo Fin de semana
2022-06-06 8308 8.8 13.5 11.15 lunes Lunes a viernes
2022-06-07 8137 7.3 11.5 9.40 martes Lunes a viernes
2022-06-08 8854 7.3 11.6 9.45 miércoles Lunes a viernes
2022-06-09 8803 8.3 16.1 12.20 jueves Lunes a viernes
2022-06-10 6890 4.2 11.5 7.85 viernes Lunes a viernes
2022-06-11 2305 4.6 15.7 10.15 sábado Fin de semana
2022-06-12 2561 6.4 14.6 10.50 domingo Fin de semana
2022-06-13 9120 7.4 16.2 11.80 lunes Lunes a viernes

Como puede verse en el siguiente gráfico, la cantidad de registros de viajes de esa duración varía en función de la temperatura máxima del día, y de si se trata de un día de la semana (lunes a viernes) o fin de semana (sábado y domingo).

Código
ggplotly(
  ggplot(data = filter(datos), aes(
    x = tmax,
    y = n,
    key = fecha,
    col = tipo_dia
  )) +
    labs(x = "Temperatura máxima", y = "Cantidad de usos de Ecobici") +
    geom_point() +
    scale_color_manual(values = c("Fin de semana" = "deeppink2", "Lunes a viernes" = "dodgerblue2")) +
    theme_classic(),
  source = "select",
  tooltip = c("key")
)

En el gráfico se observa una tendencia similar en cuanto a cómo varía la cantidad de usos diarios del sistema en función de la temperatura máxima del día. Sin embargo, y aunque la tendencia es similar, la cantidad de usos se reduce notablemente los fines de semana.

El sistema de Ecobici es un sistema público de transporte y, como tal, es razonable que su uso sea intensivo durante los días hábiles, lo que explica las tendencias separadas que se ven en los gráficos anteriores. Sin embargo, en el conjunto de datos no se tiene información de feriados, por lo que hay algunas observaciones del grupo verde (lunes a viernes) que se observan próximos a los del grupo rojo (fines de semana). El 1ro de mayo es una de ellas. También hay fines de semana que coinciden con feriados en los que típicamente hay menos movimiento, como ocurre con la observación roja que corresponde al 1ro de enero. Y también ocurre lo contrario: fines de semana de uso atípico, como el de aquel domingo 18 de diciembre de 2022 en que Argentina se coronó campeón Mundial, y cuya observación, aunque de color rojo (domingo), se ubica dentro de la nube de puntos verdes.

Este dataset no cuenta con datos de precipitaciones para las fechas observadas, lo que podría ayudar a explicar las mermas en el uso del sistema de Ecobici de ciertas fechas [para más información sobre esto, puede consultarse la Sección 3]. Sin embargo, es notable ver que existe cierta tendencia entre la intensidad del uso y la temperatura. Y tal tendencia no obedece únicamente a una cuestión de temporadas climáticas (estaciones del año, por ejemplo). Esto puede observarse en los siguientes gráficos, en donde se muestra que la evolución de la temperatura en función de la fecha del año no exhibe el mismo comportamiento que el uso del sistema Ecobici.

Código
ggplotly(
  ggplot(data = filter(datos), aes(
    x = fecha,
    y = n,
    key = tmax,
    col = tipo_dia
  )) +
    labs(x = "Fecha", y = "Cantidad de usos de Ecobici") +
    geom_point() +
    scale_color_manual(values = c("Fin de semana" = "deeppink2", "Lunes a viernes" = "dodgerblue2")) +
    theme_classic(),
  source = "select",
  tooltip = c("key")
)
Código
ggplotly(
  ggplot(data = filter(datos), aes(x = fecha, y = tmax, key=fecha)) +
    labs(x = "Fecha", y = "Temperatura máxima") +
    geom_point(colour = "deeppink4") +
    theme_classic(),
  source = "select",
  tooltip = c("key")
)

Esto sugiere que un ajuste lineal podría no ser el más adecuado para modelar la relación entre estas variables. En la siguiente sección, se prueban ajustes lineales y cuadráticos para explicar el uso del sistema Ecobici en función de la temperatura máxima diaria, tanto para días de semana como para fines de semana.

1.2 Ajuste lineal

El ajuste lineal sobre el dataset completo muestra lo que anticipaban los gráficos anteriores. Aun cuando se ajusta por tipo de día, no se capta por completo la relación que se observa entre las variables a partir de los datos.

Código
ggplotly(
  ggplot(data = filter(datos), aes(
    x = tmax,
    y = n,
    color = tipo_dia,
    key = fecha
  )) +
    labs(x = "Temperatura máxima", y = "Cantidad de usos de Ecobici") +
    geom_point() +
    scale_color_manual(values = c("Fin de semana" = "deeppink2", "Lunes a viernes" = "dodgerblue2")) +
    geom_smooth(method = "lm") +
    theme_classic(),
  source = "select",
  tooltip = c("key")
)

1.2.1 Gráficos de diagnóstico

Los gráficos de diagnóstico exhiben estructura en los residuos en ambos ajustes.

1.2.1.1 Día de semana

Código
autoplot(lm(n ~ tmax,
            data = datos[datos$tipo_dia == "Lunes a viernes",]),
         label.size = 3)

Las observaciones 121, 135 y 236 corresponden a los siguientes casos (todos feriados nacionales).

fecha n tmin tmax tmed dia tipo_dia
2022-11-21 2422 14.7 19.6 17.15 lunes Lunes a viernes
2022-12-09 3500 22.4 34.6 28.50 viernes Lunes a viernes
2023-05-01 18 11.1 19.5 15.30 lunes Lunes a viernes

1.2.1.2 Fin de semana

Código
autoplot(lm(n ~ tmax,
            data = datos[datos$tipo_dia == "Fin de semana", ]),
         label.size = 3)

Las observaciones 13, 42, 58 y 62 corresponden a los siguientes casos.

fecha n tmin tmax tmed dia tipo_dia
2022-07-16 1403 8.4 11.7 10.05 sábado Fin de semana
2022-10-23 5392 10.8 18.8 14.80 domingo Fin de semana
2022-12-18 7660 19.5 27.2 23.35 domingo Fin de semana
2023-01-01 12 19.5 26.6 23.05 domingo Fin de semana

1.3 Ajuste cuadrático

El ajuste cuadrático mejora el ajuste anterior y parece modelar mejor la estructura observada.

Código
ggplotly(
  ggplot(data = filter(datos), aes(
    x = tmax,
    y = n,
    color = tipo_dia,
    key = fecha
  )) +
    labs(x = "Temperatura máxima", y = "Cantidad de usos de Ecobici") +
    geom_point() +
    scale_color_manual(values = c("Fin de semana" = "deeppink2", "Lunes a viernes" = "dodgerblue2")) +
    geom_smooth(method = "lm",
                formula = y ~ poly(x, 2)) +
    theme_classic(),
  source = "select",
  tooltip = c("key")
)

1.3.1 Gráficos de diagnóstico

En efecto, los gráficos de diagnóstico confirman que parte de la estructura de los residuos fue resuelta.

1.3.1.1 Día de semana

Código
autoplot(lm(n ~ poly(tmax, 2),
            data = datos[datos$tipo_dia == "Lunes a viernes", ]),
         label.size = 3)

Las observaciones 10, 121, 135, 220 y 236 corresponden a los siguientes casos (todos feriados nacionales).

fecha n tmin tmax tmed dia tipo_dia
2022-06-17 2397 5.8 13.2 9.50 viernes Lunes a viernes
2022-11-21 2422 14.7 19.6 17.15 lunes Lunes a viernes
2022-12-09 3500 22.4 34.6 28.50 viernes Lunes a viernes
2023-04-07 3261 19.1 23.1 21.10 viernes Lunes a viernes
2023-05-01 18 11.1 19.5 15.30 lunes Lunes a viernes

1.3.1.2 Fin de semana

Código
autoplot(lm(n ~ poly(tmax, 2),
            data = datos[datos$tipo_dia == "Fin de semana", ]),
         label.size = 3)

Las observaciones 42, 58 y 62 corresponden a los siguientes casos.

fecha n tmin tmax tmed dia tipo_dia
2022-10-23 5392 10.8 18.8 14.80 domingo Fin de semana
2022-12-18 7660 19.5 27.2 23.35 domingo Fin de semana
2023-01-01 12 19.5 26.6 23.05 domingo Fin de semana

1.4 Ajuste lineal para días templados

El ajuste podría ser lineal para explicar el uso del sistema Ecobici para días templados, por ejemplo, con temperaturas máximas inferiores a los 25°C.

Código
ggplotly(
  ggplot(
    data = filter(datos, tmax < 25),
    aes(
      x = tmax,
      y = n,
      color = tipo_dia,
      key = fecha
    )
  ) +
    labs(x = "Temperatura máxima (menor que 25°C)", y = "Cantidad de usos de Ecobici") +
    geom_point() +
    scale_color_manual(values = c("Fin de semana" = "deeppink2", "Lunes a viernes" = "dodgerblue2")) +
    geom_smooth(method = "lm") +
    theme_classic(),
  source = "select",
  tooltip = c("key")
)

1.4.1 Gráficos de diagnóstico

Los gráficos de diagnóstico exhiben menos estructura en los residuos respecto del ajuste lineal que cuando se consideraban todas las posibles temperaturas máximas. Es decir, parece razonable suponer que la tendencia en el uso del sistema Ecobici es creciente en relación con la temperatura máxima, siempre que esta no exceda cierto límite.

1.4.1.1 Día de semana

Código
autoplot(lm(n ~ tmax,
            data = datos[(datos$tipo_dia == "Lunes a viernes" &
                            datos$tmax < 25), ]),
         label.size = 3)

Las observaciones 10, 110, 122 y 135 corresponden a los siguientes casos (todos feriados nacionales).

fecha n tmin tmax tmed dia tipo_dia
2022-06-17 2397 5.8 13.2 9.50 viernes Lunes a viernes
2022-11-21 2422 14.7 19.6 17.15 lunes Lunes a viernes
2023-04-07 3261 19.1 23.1 21.10 viernes Lunes a viernes
2023-05-01 18 11.1 19.5 15.30 lunes Lunes a viernes

1.4.1.2 Fin de semana

Código
autoplot(lm(n ~ tmax,
            data = datos[(datos$tipo_dia == "Fin de semana" &
                            datos$tmax < 25), ]),
         label.size = 3)

Las observaciones 42, 46 y 48 corresponden a los siguientes casos.

fecha n tmin tmax tmed dia tipo_dia
2022-10-23 5392 10.8 18.8 14.80 domingo Fin de semana
2022-12-24 2079 17.5 24.2 20.85 sábado Fin de semana
2023-02-18 3614 9.4 23.7 16.55 sábado Fin de semana

2 Acerca de los datos

A continuación, se detallan aspectos de los conjuntos de datos que conformaron el dataset reducido para el desarrollo del ejemplo, a la vez que se incluyen las fuentes de los datos y el código utilizado para pre-procesarlo con la sintaxis de tidyverse. De esta forma, puede fácilmente replicarse y/o adaptarse si así se lo desea.

El dataset reducido con el que se desarrolló el ejemplo y que surge de tal pre-procesamiento, es tempbici de la librería datosIC.

2.1 Sobre el dataset de temperatura

Los datos de este ejemplo corresponden a datos de temperatura de los últimos 365 días tomados del Servicio Meterológico Nacional (disponibles acá). En particular, se considerarán los datos procesados de temperatura mínimas y máximas registradas en Aeroparque desde el 4 de junio de 2022 al 3 de julio de 2023.

Código
temperatura <-
  read_table("./fuente/01_regresion/registro_temperatura365d_smn.txt") %>%
  slice(2:n()) %>%
  filter(NOMBRE == "AEROPARQUE") %>%
  select(1:3) %>%
  mutate(
    fecha = dmy(FECHA),
    tmin = as.double(TMIN),
    tmax = as.double(TMAX),
    tmed = 0.5 * (tmin + tmax)
  )

2.2 Sobre el dataset de Ecobici

Los datos de este ejemplo corresponden a datos de uso del sistema Ecobici de la Ciudad de Buenos Aires (disponibles acá). En particular, se considerarán los datos de los años 2022 y 2023 correspondientes a viajes de entre 5 minutos y 1 hora de duración.

Ecobici es el sistema de transporte público de bicicletas de la Ciudad de Buenos Aires, que tiene estaciones automáticas con bicicletas a disposición las 24 horas, todos los días del año. El sistema es gratuito para todas las personas residentes del país de lunes a viernes (días hábiles) con hasta cuatro viajes de 30 minutos cada uno. Sin embargo, si se utiliza por un tiempo mayor que el indicado o durante los fines de semana, existen diferentes pases con variados costos. A modo de referencia, el pase que habilita a hacer 6 viajes diarios de hasta 60 minutos cada uno cualquier día de la semana tiene un costo de $1.785 (junio 2023).

Código
trips_2022 <- read_csv(
  "./fuente/01_regresion/trips_2022.csv",
  col_types = cols(fecha_origen_recorrido = col_datetime(format = "%Y-%m-%d %H:%M:%S"))
) %>%
  mutate(fecha = format(as_date(ymd_hms(
    fecha_destino_recorrido
  )))) %>%
  filter(duracion_recorrido > 300 && duracion_recorrido < 3600) %>%
  group_by(fecha) %>%
  count() %>%
  mutate(fecha = as_date(fecha))

trips_2023 <- read_csv(
  "./fuente/01_regresion/trips_2023.csv",
  col_types = cols(fecha_origen_recorrido = col_datetime(format = "%Y-%m-%d %H:%M:%S"))
) %>%
  mutate(fecha = format(as_date(ymd_hms(
    fecha_destino_recorrido
  )))) %>%
  filter(duracion_recorrido > 300 && duracion_recorrido < 3600) %>%
  group_by(fecha) %>%
  count() %>%
  mutate(fecha = as_date(fecha))

trips <- rbind(trips_2022, trips_2023)

2.3 Dataset pre-procesado: tempbici

Para reducir los datos al estudio de interés, se crea un dataset conjunto, tempbici, a partir de los datos de temperatura y de uso del sistema Ecobici en el que se dispone de las siguientes variables.

  • fecha: fecha, en el formato año-mes-día.
  • n: cantidad de registros de uso del sistema EcoBici en la fecha indicada.
  • tmin: temperatura mínima registrada en esa fecha.
  • tmax: temperatura máxima registrada en esa fecha.
  • tmed: temperatura media, construida como el promedio entre la temperatura mínima y máxima de esa fecha.
  • dia: día de la semana de la fecha indicada.
  • tipo_dia: tipo de día (Fin de semana o Lunes a viernes) de la fecha indicada.
Código
trips <- trips %>%
  filter(fecha >= min(temperatura$fecha) &
           fecha <= max(temperatura$fecha))

datos <-
  left_join(trips, temperatura, by = c("fecha" = "fecha")) %>%
  drop_na() %>%
  mutate(
    dia = weekdays(fecha),
    tipo_dia = ifelse(dia %in% c("sábado", "domingo"), "Fin de semana", "Lunes a viernes")
  ) %>%
  select(c(1, 2, 6:10)) 

El dataset tempbici está disponible en la librería datosIC.

Código
library(datosIC)
tempbici
Dataset reducido disponible en la librería 'datosIC'.
fecha n tmin tmax tmed dia tipo_dia
2022-06-04 2495 9.2 15.1 12.15 sábado Fin de semana
2022-06-05 1925 9.1 14.8 11.95 domingo Fin de semana
2022-06-06 8308 8.8 13.5 11.15 lunes Lunes a viernes
2022-06-07 8137 7.3 11.5 9.40 martes Lunes a viernes
2022-06-08 8854 7.3 11.6 9.45 miércoles Lunes a viernes
2022-06-09 8803 8.3 16.1 12.20 jueves Lunes a viernes
2022-06-10 6890 4.2 11.5 7.85 viernes Lunes a viernes
2022-06-11 2305 4.6 15.7 10.15 sábado Fin de semana
2022-06-12 2561 6.4 14.6 10.50 domingo Fin de semana
2022-06-13 9120 7.4 16.2 11.80 lunes Lunes a viernes
Código
sessionInfo()
R version 4.2.3 (2023-03-15 ucrt)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows 10 x64 (build 22621)

Matrix products: default

locale:
[1] LC_COLLATE=Spanish_Argentina.utf8  LC_CTYPE=Spanish_Argentina.utf8   
[3] LC_MONETARY=Spanish_Argentina.utf8 LC_NUMERIC=C                      
[5] LC_TIME=Spanish_Argentina.utf8    

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
 [1] datosIC_0.0.0.9000 devtools_2.4.5     usethis_2.1.6      knitr_1.42        
 [5] kableExtra_1.3.4   plotly_4.10.1      ggfortify_0.4.16   lubridate_1.9.2   
 [9] forcats_1.0.0      stringr_1.5.0      dplyr_1.1.2        purrr_1.0.1       
[13] readr_2.1.4        tidyr_1.3.0        tibble_3.2.1       ggplot2_3.4.2     
[17] tidyverse_2.0.0   

loaded via a namespace (and not attached):
 [1] nlme_3.1-162      fs_1.6.1          webshot_0.5.5     httr_1.4.5       
 [5] tools_4.2.3       profvis_0.3.7     utf8_1.2.3        R6_2.5.1         
 [9] lazyeval_0.2.2    mgcv_1.8-42       colorspace_2.1-0  urlchecker_1.0.1 
[13] withr_2.5.0       tidyselect_1.2.0  gridExtra_2.3     prettyunits_1.1.1
[17] processx_3.8.0    curl_5.0.0        compiler_4.2.3    cli_3.6.1        
[21] rvest_1.0.3       xml2_1.3.5        labeling_0.4.2    scales_1.2.1     
[25] callr_3.7.3       systemfonts_1.0.4 digest_0.6.31     rmarkdown_2.21   
[29] svglite_2.1.1     pkgconfig_2.0.3   htmltools_0.5.5   sessioninfo_1.2.2
[33] fastmap_1.1.1     highr_0.10        htmlwidgets_1.6.2 rlang_1.1.0      
[37] rstudioapi_0.14   shiny_1.7.4       farver_2.1.1      generics_0.1.3   
[41] jsonlite_1.8.4    crosstalk_1.2.0   magrittr_2.0.3    Matrix_1.5-4     
[45] Rcpp_1.0.10       munsell_0.5.0     fansi_1.0.4       lifecycle_1.0.3  
[49] stringi_1.7.12    yaml_2.3.7        pkgbuild_1.4.0    grid_4.2.3       
[53] promises_1.2.0.1  crayon_1.5.2      miniUI_0.1.1.1    lattice_0.21-8   
[57] splines_4.2.3     hms_1.1.3         ps_1.7.4          pillar_1.9.0     
[61] pkgload_1.3.2     glue_1.6.2        evaluate_0.20     data.table_1.14.8
[65] remotes_2.4.2     vctrs_0.6.1       tzdb_0.3.0        httpuv_1.6.9     
[69] gtable_0.3.4      cachem_1.0.7      xfun_0.39         mime_0.12        
[73] xtable_1.8-4      later_1.3.0       viridisLite_0.4.2 memoise_2.0.1    
[77] timechange_0.2.0  ellipsis_0.3.2   

3 Referencias