Borrador

Si usas dplyr, debes aprender a usar across y where

Aprende a usar las funciones across y where de dplyr para realizar operaciones en múltiples columnas de manera eficiente. Estas herramientas mejoran la manipulación y el procesamiento de datos en R.
Autor/a
Afiliación

Carlos Aguero

Fecha de publicación

30 de mayo de 2024

Si trabajas con datos, hay dos situaciones comunes que seguro has tenido que resolver. La primera es seleccionar o filtrar tus datos considerando el tipo de datos de alguna variable. Por ejemplo, ACP o un clustering jerárquico, es necesario seleccionar solo las variables numéricas. La segunda situación es cuando necesitas aplicar una función a todas las columnas de tu tabla, como escalar todas las columnas aplicando la misma función, por ejemplo, scale, a cada columna o a cada columna numérica.

Estos escenarios se vuelven fáciles de escribir y leer cuando utilizamos las funciones across y where de dplyr. Con across, puedes aplicar una función a múltiples columnas simultáneamente, y con where, puedes filtrar columnas basándote en condiciones específicas, como su tipo de dato.

La mejor forma de explicar esto es usando ejemplos concretos. A continuación, veamos una serie de casos y cómo resolverlos con estas funciones.

Para estos ejemplos usaremos un dataset que me encanta, llamado pinguinos 🐧. Se presenta como una alternativa al típico dataset iris, el cual, no sé vos, pero al menos yo ya estoy un poco harto 💕. Originalmente presentado en el paquete palmerpenguins desarrollado por Allison Horst, Alison Hill y Kristen Gorman, y luego traducido al español por el paquete datos, una hermosa contribución de Riva Quiroga, Edgar Ruiz, Mauricio Vargas, Mauro Lepore, Rayna Harris, Daniela Vasquez y Joshua Kunst.

Ejemplo 1: Seleccionar solamente las variables numéricas

library(tidyverse)
library(datos)
glimpse(pinguinos)
Rows: 344
Columns: 8
$ especie         <fct> Adelia, Adelia, Adelia, Adelia, Adelia, Adelia, Adelia…
$ isla            <fct> Torgersen, Torgersen, Torgersen, Torgersen, Torgersen,…
$ largo_pico_mm   <dbl> 39.1, 39.5, 40.3, NA, 36.7, 39.3, 38.9, 39.2, 34.1, 42…
$ alto_pico_mm    <dbl> 18.7, 17.4, 18.0, NA, 19.3, 20.6, 17.8, 19.6, 18.1, 20…
$ largo_aleta_mm  <int> 181, 186, 195, NA, 193, 190, 181, 195, 193, 190, 186, …
$ masa_corporal_g <int> 3750, 3800, 3250, NA, 3450, 3650, 3625, 4675, 3475, 42…
$ sexo            <fct> macho, hembra, hembra, NA, hembra, macho, hembra, mach…
$ anio            <int> 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, …
x <- select(pinguinos,where(is.numeric))
glimpse(x)
Rows: 344
Columns: 5
$ largo_pico_mm   <dbl> 39.1, 39.5, 40.3, NA, 36.7, 39.3, 38.9, 39.2, 34.1, 42…
$ alto_pico_mm    <dbl> 18.7, 17.4, 18.0, NA, 19.3, 20.6, 17.8, 19.6, 18.1, 20…
$ largo_aleta_mm  <int> 181, 186, 195, NA, 193, 190, 181, 195, 193, 190, 186, …
$ masa_corporal_g <int> 3750, 3800, 3250, NA, 3450, 3650, 3625, 4675, 3475, 42…
$ anio            <int> 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, …

Podemos, a la función where, pasar cualquier función que reciba un vector y retorne un TRUE/FALSE, lo cual es útil al combinarlo con las funciones que R ya nos provee para verificar tipos como is.numeric, is.factor, is.character, is.logical. O podríamos, ¿por qué no?, escribir nuestras propias funciones cortas.

Por ejemplo, quiero las columnas que no contengan valores ausentes NA.

select(pinguinos, where(\(x) all(!is.na(x))))
# A tibble: 344 × 3
   especie isla       anio
   <fct>   <fct>     <int>
 1 Adelia  Torgersen  2007
 2 Adelia  Torgersen  2007
 3 Adelia  Torgersen  2007
 4 Adelia  Torgersen  2007
 5 Adelia  Torgersen  2007
 6 Adelia  Torgersen  2007
 7 Adelia  Torgersen  2007
 8 Adelia  Torgersen  2007
 9 Adelia  Torgersen  2007
10 Adelia  Torgersen  2007
# ℹ 334 more rows

Ejemplo 2: Escalar todas las variables numéricas

pinguinos |> 
  mutate(
    across(
      where(is.numeric),
      \(x) as.vector(scale(x))
    )
  )
# A tibble: 344 × 8
   especie isla  largo_pico_mm alto_pico_mm largo_aleta_mm masa_corporal_g sexo 
   <fct>   <fct>         <dbl>        <dbl>          <dbl>           <dbl> <fct>
 1 Adelia  Torg…        -0.883        0.784         -1.42          -0.563  macho
 2 Adelia  Torg…        -0.810        0.126         -1.06          -0.501  hemb…
 3 Adelia  Torg…        -0.663        0.430         -0.421         -1.19   hemb…
 4 Adelia  Torg…        NA           NA             NA             NA      <NA> 
 5 Adelia  Torg…        -1.32         1.09          -0.563         -0.937  hemb…
 6 Adelia  Torg…        -0.847        1.75          -0.776         -0.688  macho
 7 Adelia  Torg…        -0.920        0.329         -1.42          -0.719  hemb…
 8 Adelia  Torg…        -0.865        1.24          -0.421          0.590  macho
 9 Adelia  Torg…        -1.80         0.480         -0.563         -0.906  <NA> 
10 Adelia  Torg…        -0.352        1.54          -0.776          0.0602 <NA> 
# ℹ 334 more rows
# ℹ 1 more variable: anio <dbl>

Ejemplo 3: Resumir todas la variables numéricas calculando la media

pinguinos |> 
  summarise(across(where(is.numeric), mean,
    na.rm = TRUE))
Warning: There was 1 warning in `summarise()`.
ℹ In argument: `across(where(is.numeric), mean, na.rm = TRUE)`.
Caused by warning:
! The `...` argument of `across()` is deprecated as of dplyr 1.1.0.
Supply arguments directly to `.fns` through an anonymous function instead.

  # Previously
  across(a:b, mean, na.rm = TRUE)

  # Now
  across(a:b, \(x) mean(x, na.rm = TRUE))
# A tibble: 1 × 5
  largo_pico_mm alto_pico_mm largo_aleta_mm masa_corporal_g  anio
          <dbl>        <dbl>          <dbl>           <dbl> <dbl>
1          43.9         17.2           201.           4202. 2008.

Ejemplo 4: Resumir todas la variables numéricas calculando la media y la mediana

x <- pinguinos |> 
  summarise(across(
    .cols = where(is.numeric),
    .fns = list(media = mean, mediana = median),
    na.rm = TRUE)
  )

glimpse(x)
Rows: 1
Columns: 10
$ largo_pico_mm_media     <dbl> 43.92193
$ largo_pico_mm_mediana   <dbl> 44.45
$ alto_pico_mm_media      <dbl> 17.15117
$ alto_pico_mm_mediana    <dbl> 17.3
$ largo_aleta_mm_media    <dbl> 200.9152
$ largo_aleta_mm_mediana  <dbl> 197
$ masa_corporal_g_media   <dbl> 4201.754
$ masa_corporal_g_mediana <dbl> 4050
$ anio_media              <dbl> 2008.029
$ anio_mediana            <dbl> 2008
pinguinos |> 
  summarise(across(where(is.numeric), list(mean = mean,media = median), .names = "{col}_{fn}"))
# A tibble: 1 × 10
  largo_pico_mm_mean largo_pico_mm_media alto_pico_mm_mean alto_pico_mm_media
               <dbl>               <dbl>             <dbl>              <dbl>
1                 NA                  NA                NA                 NA
# ℹ 6 more variables: largo_aleta_mm_mean <dbl>, largo_aleta_mm_media <int>,
#   masa_corporal_g_mean <dbl>, masa_corporal_g_media <int>, anio_mean <dbl>,
#   anio_media <dbl>

Cómo citar

BibTeX
@online{aguero2024,
  author = {Aguero, Carlos},
  title = {Si usas dplyr, debes aprender a usar across y where},
  date = {2024-05-30},
  url = {https://aprendetidyverse.com/posts/004_across_where.html},
  langid = {es}
}
Por favor, cita este trabajo como:
Aguero, Carlos. 2024. “Si usas dplyr, debes aprender a usar across y where.” May 30, 2024. https://aprendetidyverse.com/posts/004_across_where.html.