Como hemos visto, dplyr actúa sobre bases de datos utilizando cinco verbos fundamentales:

Para continuar con nuestro aprendizaje carguemos la base de datos de todos los vuelos que salieron del aeropuerto de Houston en el año de 2011 que hemos estado utilizando en clase (debe de estar en tu espacio de trabajo del proyecto, si creaste uno).

library(dplyr)
flights <- tbl_df(read.csv("flights.csv", stringsAsFactors = FALSE))
flights$date <- as.Date(flights$date)
head(flights)
## # A tibble: 6 x 14
##   date        hour minute   dep   arr dep_delay arr_delay carrier flight dest 
##   <date>     <int>  <int> <int> <int>     <int>     <int> <chr>    <int> <chr>
## 1 2011-01-01    14      0  1400  1500         0       -10 AA         428 DFW  
## 2 2011-01-02    14      1  1401  1501         1        -9 AA         428 DFW  
## 3 2011-01-03    13     52  1352  1502        -8        -8 AA         428 DFW  
## 4 2011-01-04    14      3  1403  1513         3         3 AA         428 DFW  
## 5 2011-01-05    14      5  1405  1507         5        -3 AA         428 DFW  
## 6 2011-01-06    13     59  1359  1503        -1        -7 AA         428 DFW  
## # … with 4 more variables: plane <chr>, cancelled <int>, time <int>, dist <int>

Junto con los cinco verbos básicos, dplyr puede trabajar con datos agrupados usando el comando group_by cuya función es ejecutar las acciones posteriores al nivel que la agrupación indique. Para ejemplificar, supongamos que queremos resumir el tiempo de retraso de llegadas, arr_delay por día. Para ello, seleccionamos como medidas de tendencia central la media y la mediana, como medidas de dispersión la desviación estándar y el rango intercuartil, y como medidas particulares de contexto la proporción de vuelos cuyo retraso es mayor a 15, 30, o 60 minutos. Nuestro resumen por día se calcula como sigue:

por_dia <- group_by(na.omit(flights), date)
         # para agrupar la base por día y que los cálculos
         # siguientes se realicen a ese nivel

summarise(por_dia, media = mean(arr_delay),
                   mediana = median(arr_delay),
                   sd = sd(arr_delay),
                   iqr = IQR(arr_delay),
                   mayor_15 = mean(arr_delay > 15),
                   mayor_30 = mean(arr_delay > 30),
                   mayor_60 = mean(arr_delay > 60))
## # A tibble: 365 x 8
##    date       media mediana    sd   iqr mayor_15 mayor_30 mayor_60
##    <date>     <dbl>   <dbl> <dbl> <dbl>    <dbl>    <dbl>    <dbl>
##  1 2011-01-01 10.1        5  21.2  21      0.280   0.121    0.0329
##  2 2011-01-02 10.5        3  29.5  22      0.267   0.145    0.0435
##  3 2011-01-03  6.04      -2  27.3  19.5    0.207   0.116    0.0386
##  4 2011-01-04  7.97       4  21.0  22      0.258   0.108    0.0224
##  5 2011-01-05  4.17      -1  25.1  18      0.183   0.0735   0.0120
##  6 2011-01-06  6.07       2  18.6  18      0.205   0.0729   0.0243
##  7 2011-01-07  3.91       0  19.0  16      0.170   0.0638   0.0198
##  8 2011-01-08  3.07      -2  21.1  16      0.158   0.0741   0.0261
##  9 2011-01-09 17.3       10  28.6  31      0.427   0.240    0.0709
## 10 2011-01-10 11.0        5  25.4  22.5    0.288   0.134    0.0388
## # … with 355 more rows

La lógica del cálculo es inmediata: Agrupando primero por día, pedimos la media, mediana, etc. de la variable arr_delay. Estos estadísticos se calculan, por el uso de group_by para cada uno de los días. Observe que se pueden utilizar funciones prestablecidas para hacer el resumen, como mean, median, sd, IQR (otros ejemplos son min, max, quantile(x, p), n(), ndistinct(), sum(), var(), mad()).

Observe también el uso de na.omit(flights) cuya función es eliminar todas las observaciones que tengan por lo menos un NA. Recuerde que en R el NA es epistémico, es decir, apunta a un “no saber” que, por lo tanto, se propaga a todos los cálculos. En caso de no haber incluido la función na.omit tendríamos que haber utilizado el argumento na.rm = TRUE en las funciones siguientes para remover los NAs antes del cálculo. Hay una sutil diferencia entre estos dos métodos: El primero quita todas las observaciones que tengan NA en alguna columna, mientras que el segundo sólo quita los NAs de la variable de interés. Complete el siguiente código para ver la diferencia:

completas <- filter(flights, !is.na(arr_delay))
summarise(...)

La nueva base, completas, excluye solo las observaciones en las que la variable de interés, arr_delay es NA.

La gran desventaja de utilizar esta sintaxis funcional es que se vuelve difícil leer (y programar) operaciones complejas, por ejemplo, qué resulta de ejecutar el siguiente código?

who_is <- filter(
  summarise(
    group_by(
      filter(
        flights,
        !is.na(arr_delay)
      ),
      date, hour
    ),
    medio = mean(arr_delay),
    num = n()
  ),
  num > 10
)

Operador Pipeline

La escritura y lectura de código en dplyr se facilita con el operador pipeline, %>% (atajo de teclado Ctrl + Shift + M), cuya operación puede representarse simbolicamente como \[x \;{\tiny\%>\%}\; f(y) = f(x, y).\]

Por ejemplo, los siguientes son equivalentes:

filter(flights, !is.na(arr_delay))
flights %>% filter(!is.na(arr_delay))

Los operadores pipeline pueden concatenarse, mandando el resultado de la última operacón, a la siguietne. Utilizando el operador pipeline (de adentro hacia afuera), vemos que la base who_is puede calcularse como

who_is <- fligths %>% 
              filter(!is.na(arr_delay)) %>% 
              group_by(date, hour) %>% 
              summarise(medio = mean(arr_delay),
              num = n()) %>% 
              filter(num > 10)

Para leer el código, se puede interpretar el operador pipeline como “y luego”. En este ejemplo, la base who_isse consigue con los siguienetes pasos: Pimero tomar flights, luego filtarla evitando datos faltantes de la variable arr_delay luego agrupando por fecha y hora, luego resumiendo con el valor medio y el número de observaciones, y luego, finalmente, filtrando cuando hubo más de 10 observaciones. Esta breve historia nos cuenta que estamos calculando el promedio y número de vuelos por día y hora; pero solo observando el resultado si en esa combinación de día/hora ha habido más de 10 vuelos.

Aplicando lo aprendido

Construya pipelines, utilizando todos los verbos básicos y el operador group_by, para responder a las siguientes preguntas:

  1. Qué destinos tienen el retraso de llegada promedio máximo (imprima diez)?
  2. Qué vuelos (es decir, combinaciones únicas de carrier y flight) ocurren diario, y a dónde van?
  3. Calcule la media y número de vuelos que ocurren en cada tiempo de salida. Utilice la varible time = hour + minute/60 en este ejercicio.

Compare sus respuestas:

  1. Destinos con mayor atraso de llegada medio:
## # A tibble: 116 x 3
##    dest  mean_arr_delay     n
##    <chr>          <dbl> <int>
##  1 ANC             26.1   125
##  2 CID             17.8   410
##  3 DSM             16.0   647
##  4 SFO             14.9  2818
##  5 BPT             14.3     3
##  6 GRR             13.7   677
##  7 DAY             13.7   451
##  8 VPS             12.5   880
##  9 ECP             12.4   729
## 10 SAV             12.3   863
## # … with 106 more rows
  1. Vuelos diarios y sus destinos:
## # A tibble: 7 x 4
## # Groups:   carrier, flight [7]
##   carrier flight dest      n
##   <chr>    <int> <chr> <int>
## 1 AA        1294 MIA     365
## 2 AS         731 SEA     365
## 3 CO           1 HNL     365
## 4 CO          62 EWR     365
## 5 CO          89 EWR     365
## 6 CO         106 EWR     365
## 7 MQ        3859 ORD     365
  1. Resumen por tiempos
## # A tibble: 1,207 x 3
##      time arr_delay     n
##     <dbl>     <dbl> <int>
##  1 0.0167      29.7     7
##  2 0.0333      64       2
##  3 0.05       141       4
##  4 0.0667      91.7     7
##  5 0.0833     114       3
##  6 0.1         51       5
##  7 0.117       90.3     6
##  8 0.133       91       2
##  9 0.15       103.      3
## 10 0.167      131.      3
## # … with 1,197 more rows

  1. Universidad Iberoamericana; Depto,