Encadenar operaciones en dplyr

En esta nueva entrada, vas a aprender a encadenar operaciones en dplyr utilizando el operador %>% de magrittr y cómo esto es particularmente útil para calcular estadísticas por categorías.

Relacionado: TUTORIAL: Data wrangling en R con dplyr y tidyr - Parte 1

Al igual que en la entrada anterior, utilizo el conjunto de datos iris que ya viene cargado en R.

Requisitos

  1. Tener instalado R y dplyr
  2. Saber qué es una función y qué son los argumentos de una función en R
  3. Las ganas de aprender

Recomendado

  1. Utilizar RStudio
  2. Saber construir expresiones lógicas

Calcular la media, la desviación estándar u otras funciones resumen de una o más variables

Problema: Calcular estadísticas como el total, la media, percentiles, etc.
Solución: Utilizar el verbo summarise (resumir) junto con la función apropiada.

El primer argumento de la función summarise es el data.frame; el resto, son funciones resumen (summary functions). Las funciones resumen son aquellas que toman varios valores y devuelven un sólo valor, como por ejemplo la media, la desviación estándar o los percentiles.

iris <- as_tibble(iris) # Esto no es estrictamente necesario, pero es recomendable
summarise(iris, 
          n = n(), # Devuelve el número de filas del conjunto de datos
          Sepal.Width.Mean = mean(Sepal.Width), 
          Sepal.Width.Sd = sd(Sepal.Width),
          Sepal.Width.Q1 = quantile(Sepal.Width, .25),
          Sepal.Width.Q2 = quantile(Sepal.Width, .50),
          Sepal.Width.Q3 = quantile(Sepal.Width, .75),
          Sepal.Length.Mean = mean(Sepal.Length), 
          Sepal.Length.Sd = sd(Sepal.Length))
## # A tibble: 1 x 8
##       n Sepal.Width.Mean Sepal.Width.Sd Sepal.Width.Q1 Sepal.Width.Q2
##   <int>            <dbl>          <dbl>          <dbl>          <dbl>
## 1   150             3.06          0.436            2.8              3
## # … with 3 more variables: Sepal.Width.Q3 <dbl>, Sepal.Length.Mean <dbl>,
## #   Sepal.Length.Sd <dbl>

Nota: la función n sirve para contar cuántas filas tiene un conjunto de datos.

Los nombres de los argumentos son los nombres de las columnas resultantes. Si no lo haces, las columnas del resultado tendrían nombres horribles como sd(Sepal.Width). Aunque no es obligatorio, es útil que las columnas tengan un nombre apropiado para utilizarlo después.

Calcular estadísticas por grupo

Problema: Calcular estadísticas por grupo
Solución: Utilizar el verbo group_by (agrupar por) junto con el verbo summarise
Vamos a calcular las mismas estadísticas del caso anterior, pero ahora para cada una de las categorías de Species. Para esto, vamos a decirle a R que queremos la tabla iris agrupada según esa variable y luego utilizamos summarise.

summarise(
  group_by(
    iris, 
    Species
  ), 
  n = n(),
  Sepal.Width.Mean = mean(Sepal.Width), 
  Sepal.Width.Sd = sd(Sepal.Width),
  Sepal.Width.Q1 = quantile(Sepal.Width, .25),
  Sepal.Width.Q2 = quantile(Sepal.Width, .50),
  Sepal.Width.Q3 = quantile(Sepal.Width, .75),
  Sepal.Length.Mean = mean(Sepal.Length), 
  Sepal.Length.Sd = sd(Sepal.Length)
)
## # A tibble: 3 x 9
##   Species     n Sepal.Width.Mean Sepal.Width.Sd Sepal.Width.Q1
##   <fct>   <int>            <dbl>          <dbl>          <dbl>
## 1 setosa     50             3.43          0.379           3.2 
## 2 versic…    50             2.77          0.314           2.52
## 3 virgin…    50             2.97          0.322           2.8 
## # … with 4 more variables: Sepal.Width.Q2 <dbl>, Sepal.Width.Q3 <dbl>,
## #   Sepal.Length.Mean <dbl>, Sepal.Length.Sd <dbl>

Encadenar operaciones utilizando el operador %>%

Problema: No anidar las funciones para que el código sea más legible
Solución: Encadenar operaciones utilizando el operador %>%
En el ejemplo anterior tuvimos que anidar las funciones summarise y group_by. Esto lo podemos evitar de la siguiente manera

iris %>% 
  group_by(Species) %>% 
  summarise(
    Sepal.Width.Mean = mean(Sepal.Width), 
    Sepal.Length.Mean = mean(Sepal.Length)
  )
## # A tibble: 3 x 3
##   Species    Sepal.Width.Mean Sepal.Length.Mean
##   <fct>                 <dbl>             <dbl>
## 1 setosa                 3.43              5.01
## 2 versicolor             2.77              5.94
## 3 virginica              2.97              6.59

Esto lo podríamos leer de la siguiente manera

Toma la tabla iris, luego agrupa por Species, y luego calcula la media de Sepal.Width y de Sepal.Length.

Por supuesto, leerlo en inglés es mejor porque todo queda en un sólo idioma

Take the table iris, then group by Species, and then calculate the mean of Sepal.Width and the mean of Sepal.Length.

Una vez que tienes este resultado, puedes seguir encadenando operaciones sobre la tabla. Por ejemplo, si quieres sólo las categorías donde Sepal.Width.Mean sea menor que 3, y luego ordenarlas de mayor a menor según Sepal.Length.Mean el código sería el siguiente:

iris %>% 
  group_by(Species) %>% 
  summarise(
    Sepal.Width.Mean = mean(Sepal.Width), 
    Sepal.Length.Mean = mean(Sepal.Length)
  ) %>% 
  filter(Sepal.Width.Mean < 3) %>% 
  arrange(desc(Sepal.Length.Mean))
## # A tibble: 2 x 3
##   Species    Sepal.Width.Mean Sepal.Length.Mean
##   <fct>                 <dbl>             <dbl>
## 1 virginica              2.97              6.59
## 2 versicolor             2.77              5.94

Aquí se ve la utilidad de darle nombre a las columnas resultantes: para utilizarlas en operaciones consecutivas.

¡Pensar en hacer esto mismo anidando funciones da dolor de cabeza! Mientras que un código anidado se lee de adentro hacia afuera, un código encadenado por el operador %>% se lee de arriba hacia abajo. Para más información sobre cómo funciona el operador %>% sigue el tutorial de magrittr.

Un truco importante: Puedes escribir rápidamente %>% en RStudio pulsando CTRL + ALT + M en windows y linux, y CMD + shift + M en Mac, lo cual es mucho más cómodo que escribirlo directamente.

Conclusión

Utilizar el operador %>% junto con dplyr mantiene el código bastante legible. Ya que las funciones de dplyr son verbos, puedes leer el operador %>% como luego y cada verbo como una orden, de manera que el código se lea como Toma la tabla tal y luego haz esto, luego haz lo otro, luego… y así. En la próxima parte, te enseño a combinar mutate con funciones de resumen y group_by para hacer cálculos de fila por grupos.