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

La forma abreviada \(x) x + 1 se interpreta como function(x) x + 1. Puede ser útil para hacer que el código que contiene expresiones de funciones simples sea más legible.

Ejemplo 2: Escalar todas las variables numéricas

En otro escenario, lo normal sería crear primero una tabla con todas las variables numéricas y escalarlas, ya que la función scale no soporta variables no numéricas. En este caso, podemos hacer que la función scale se aplique solamente a las variables numéricas, dejando las demás intactas.

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

Otro caso muy común es querer aplicar un resumen a múltiples columnas. Cuando tenemos muchas columnas o estas pueden, por la naturaleza de nuestros datos, cambiar de nombre con frecuencia, podemos implementar una solución más genérica que calcule la media para cada variable numérica sin depender del nombre de las variables.

pinguinos |> 
  summarise(
  across(
    where(is.numeric), # solamente numéricas
    \(x) mean(x, na.rm = TRUE)) # calculamos la media para cada variable
  )
# 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

Ahora bien, si queremos aplicar más de una función de resumen, en lugar de pasar una sola función como parámetro, podemos pasar una lista con varias funciones. A cada una de las columnas indicadas por where se le aplicarán todas las funciones de la lista.

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

Para evitar confusiones, es posible que indiquemos los nombres de los parámetros de la función across. El primer parámetro es .cols, que contiene el where o las variables de la tabla que se van a utilizar, y el segundo parámetro es .funs, que puede ser una función o una lista de las funciones que se van a aplicar a cada una de las columnas.

Ejemplos 5 Convertir todas las variables de tipo character en Factor

Aunque para muchas operaciones podemos trabajar con textos sin problema, la mayoría de los modelos necesitan variables de tipo factor en lugar de textos para funcionar correctamente. En este caso, por ejemplo, si queremos convertir todos los textos en factores, podemos hacerlo de la siguiente manera.

x <- pinguinos |> 
  mutate(
    across(
      where(is.character),
      as.factor
    )
  )

glimpse(x)
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, …

Estas funciones nos ofrecen muchas ventajas. Para mí, la principal es poder diseñar scripts que dependen más de los tipos que de los nombres. No tener que conocer la cantidad ni el nombre de las variables para poder realizar operaciones se vuelve mucho más práctico. Como resultado, tenemos un código más sencillo de leer y fácil de mantener en el tiempo, ya que es posible que requiera menos cambios que otras alternativas.

Importante 🚨

🚀¡Atención a todos los aspirantes a científicos de datos! 🚀

¿Quieres aprender de la mano de un instructor experto y certificado por RStudio? ¡Esta es tu oportunidad!

Este próximo sábado 3 de agosto a las 10:00 a.m. (hora Costa Rica), iniciaremos nuestro curso exclusivo: “Fundamentos de R y RStudio”.

Imagina ser guiado paso a paso por un profesional que no solo domina R y RStudio, sino que también sabe cómo enseñarte a dominar estas herramientas de manera efectiva.

🔍 ¿Por qué deberías unirte? 🔍

  • Aprendizaje personalizado: Obtén atención individualizada para asegurar tu éxito.

  • Seguimiento semanal: Recibe orientación semanal para aplicar lo que se ve en clase a tu área de expertise.

  • Horario flexible: Atiendo tus dudas en horarios que se adaptan a tu vida; puedes recibir asesoría en horario nocturno o fines de semana.

No dejes pasar esta oportunidad de transformar tu carrera y adquirir habilidades que están en alta demanda.

Inscríbete ahora y da el primer paso hacia tu futuro en la ciencia de datos.

📖 ¡Quiero saber más!

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.