Tidyverse R: trabajando variables con la función across

Generalmente estamos interesados en realizar operaciones sobre varias variables simultáneamente, especialmente cuando estamos frente a una data tipo wide, teniendo diversas opciones [curso de R gratis]: 

i)                   Repetir la orden para cada variable.

ii)                 Usar loops.

iii)              Transformar la data en long format y trabajar by_group la sentencia buscada.

iv)               Utilizar purr.

v)                 Usar la función across.

 

En el tidyverso [introducción rápida a dplyr] de R, se utiliza la función across para realizar esta tarea, por lo que, en la siguiente entrada mostramos algunos ejemplos de cómo utilizarla usando la data Wage1 del paquete wooldridge.

 

across(.cols=vars,.fnd=fun,.names=nom)


La función across utiliza dos argumentos principales: 1) una lista de variables; y, ii) la función. El tercer argumento es opcional, el mismo se usa para colocar el nombre a las variables creadas, en caso de omitirlo la función sustituye las mismas variables con las que estamos trabajando.



Ejemplo 1. Obtener el promedio de una lista de variables usando c() o !c(). Obtenemos el promedio de las variables educ, exper y tenure dentro de la base indicada. En lugar de repetir la sentencia mean(x) para cada una de las variables en una lista determinada, se utiliza across anidada en la función summarise. 

library(tidyverse)

wage1 <- wooldridge::wage1 %>%
          as.tibble()

wage1 %>%
  summarise(across(c(educ,exper,tenure), mean))

## # A tibble: 1 x 3
##    educ exper tenure
##   <dbl> <dbl>  <dbl>
## 1  12.6  17.0   5.10

Ejemplo 2. Omitir las variables anteriores usando el operador (!) de negación al inicio de la lista de variables usadas:

wage1 %>%
  select(1:7) %>%
  summarise(across(!c(educ,exper,tenure), mean))

## # A tibble: 1 x 4
##    wage nonwhite female married
##   <dbl>    <dbl>  <dbl>   <dbl>
## 1  5.90    0.103  0.479   0.608

 

Ejemplo 3. Obtener el promedio de una lista de variables, segmentado por grupo. Lo relevante ahora es que se utiliza el operador : para indicarle al programa la secuencia de variables con las que necesitamos trabajar. Se utiliza la group_by para agrupar la data según las clases que aparecen en la variable services. Es decir, obtener el promedio por grupo.

library(tidyverse)
wage1 %>%
  group_by(services) %>%
  summarise(across(educ:tenure, mean),
            nObserva = n())

## # A tibble: 2 x 5
##   services  educ exper tenure nObserva
##      <int> <dbl> <dbl>  <dbl>    <int>
## 1        0  12.6  16.8   5.26      473
## 2        1  12.0  18.7   3.75       53

 

Ejemplo 4. Transformar el formato de varias variables de manera simultánea. Adicionalmente pudo usarse la función where para transformar formatos específicos, dado que esta utiliza operadores lógicos para identificar diferentes tipos de formatos en mis bases de datos.

class(wage1$female)

## [1] "integer"

wage2 <- wage1 %>%
  select(1:7) %>%
  mutate(across(nonwhite:married, as.factor))

wage2 %>% head()

## # A tibble: 6 x 7
##    wage  educ exper tenure nonwhite female married
##   <dbl> <int> <int>  <int> <fct>    <fct>  <fct> 
## 1  3.10    11     2      0 0        1      0     
## 2  3.24    12    22      2 0        1      1     
## 3  3       11     2      0 0        0      0     
## 4  6        8    44     28 0        0      1     
## 5  5.30    12     7      2 0        0      1     
## 6  8.75    16     9      8 0        0      1

Ejemplo 5. Realizar operaciones sobre una lista de variables usando una función anónima. En el ejemplo siguiente se obtiene el cuadrado de las variables elegidas.

~ indica que se va usar una función anónima, cuyo argumento puede ser . o .x (particularmente prefiero usar .x porque este facilita la lectura de terceros al momento de interpretar los códigos). Note que en lugar de usar algunas de las funciones definidas en el paquete base de R, algún paquete adjuntado o por el propio usuario, la misma función se define dentro de la misma función que estamos usando.

wage1 %>%
  select(1:7) %>%
  mutate(across(educ:tenure, ~.x^2)) %>%
  head()

## # A tibble: 6 x 7
##    wage  educ exper tenure nonwhite female married
##   <dbl> <dbl> <dbl>  <dbl>    <int>  <int>   <int>
## 1  3.10   121     4      0        0      1       0
## 2  3.24   144   484      4        0      1       1
## 3  3      121     4      0        0      0       0
## 4  6       64  1936    784        0      0       1
## 5  5.30   144    49      4        0      0       1
## 6  8.75   256    81     64        0      0       1

Ejemplo 6. Por default, la función across utiliza los mismos nombres de las variables sobre la que opera, usando “{.col}” (también se puede asignar el nombre de la función “{.fn}”, seguido de un número en caso de tener más de dos variables), esto facilita el trabajo con la función, pero tiene el inconveniente que sustituye la variable original por la trasformación sugerida, en caso de usar across dentro del comando mutate.

Para generar variables o columnas nuevas usamos el argumento .names y anexamos extensiones al nombre por default de las variables, podemos obtener nuevos nombres de las variables.

wage1 %>%
  select(1:4) %>%
  mutate(across(educ:tenure, ~.x^2, .names = "q_{.col}")) %>%
  head()

## # A tibble: 6 x 7
##    wage  educ exper tenure q_educ q_exper q_tenure
##   <dbl> <int> <int>  <int>  <dbl>   <dbl>    <dbl>
## 1  3.10    11     2      0    121       4        0
## 2  3.24    12    22      2    144     484        4
## 3  3       11     2      0    121       4        0
## 4  6        8    44     28     64    1936      784
## 5  5.30    12     7      2    144      49        4
## 6  8.75    16     9      8    256      81       64

Ejemplo 7. Condicionando nuestras operaciones a condiciones lógicas. En el siguiente ejemplo colocamos etiquetas sobre las variables tipo factor.

Hasta ahora, hemos indicado una lista de variables a la función, sin embargo, en muchas ocasiones no sabemos cuál es la lista de variables sobre las que deseamos operar, por la que deseamos operar sobe una lista de variables usando operaciones lógicas. Es decir, no siempre sabemos cuál es el listado de variables sobre la que deberíamos trabajar, sino que esta sigue alguna regla lógica. Por ejemplo, usar todas las variables tipos texto, elevar al cuadrado las variables que sumen más de 15, o crear logaritmo sobre todas las variables numéricas. En estos casos, anidamos la función where.

wage2 %>%
  select(1:7) %>%
  mutate(across(where(is.factor),
                ~factor(.x, labels = c("no","si")))) %>%
  head()

## # A tibble: 6 x 7
##    wage  educ exper tenure nonwhite female married
##   <dbl> <int> <int>  <int> <fct>    <fct>  <fct> 
## 1  3.10    11     2      0 no       si     no    
## 2  3.24    12    22      2 no       si     si    
## 3  3       11     2      0 no       no     no    
## 4  6        8    44     28 no       no     si    
## 5  5.30    12     7      2 no       no     si    
## 6  8.75    16     9      8 no       no     si

Ejemplo 8.

wage2 %>%
  summarise(across(where(is.factor), table))

## # A tibble: 2 x 3
##   nonwhite female  married
##   <table>  <table> <table>
## 1 472      274     206   
## 2  54      252     320

wage2 %>%
  summarise(across(is.numeric, mean))

## # A tibble: 1 x 4
##    wage  educ exper tenure
##   <dbl> <dbl> <dbl>  <dbl>
## 1  5.90  12.6  17.0   5.10

Ejemplo 9. Anidando condiciones. Ahora, aparte de ser una variable numérica seleccionamos solo aquellas variables cuyos nombres inician con e.

wage2 %>%
  summarise(across(is.numeric & starts_with("e"), mean))

## # A tibble: 1 x 2
##    educ exper
##   <dbl> <dbl>
## 1  12.6  17.0

Además, podemos negar alguna expresión de texto. Ahora seleccionamos aquellas variables numéricas que no inician con e:

wage2 %>%
  summarise(across(is.numeric & !starts_with("e"), mean))

## # A tibble: 1 x 2
##    wage tenure
##   <dbl>  <dbl>
## 1  5.90   5.10

Ejemplo 10. Seleccionar todas las variables usando la función everything.

wage1 %>%
  select(1:7) %>%
  summarise(across(everything(), mean, na.rm = TRUE))

## # A tibble: 1 x 7
##    wage  educ exper tenure nonwhite female married
##   <dbl> <dbl> <dbl>  <dbl>    <dbl>  <dbl>   <dbl>
## 1  5.90  12.6  17.0   5.10    0.103  0.479   0.608

Ejemplo 11. Aplicando varias funciones sobre el conjunto de variables. En el siguiente ejemplo obtenemos la media y usamos una función anónima para obtener la suma del cuadrado de las variables incluidas en el rango educ:exper.

 wage1 %>%

  select(1:7) %>%
  summarise(across(educ:exper,
                   list(mean = mean,
                        n_miss = ~sum(.x^2))))

## # A tibble: 1 x 4
##   educ_mean educ_n_miss exper_mean exper_n_miss
##       <dbl>       <dbl>      <dbl>        <dbl>
## 1      12.6       87040       17.0       249027

Ejemplo 12. Creando tasas de crecimiento sobre todas las variables usando una función anónima. Puntualmente se calculan las tasas y luego se asignan nombres a las variables, para evitar se sustituyan las originales:

(1613.63-1628.75)/1628.75*100

## [1] -0.9283193

EuStockMarkets %>% 
  as.tibble() %>%
  mutate(
    across(everything(),
           ~((./dplyr::lag(.)-1)*100),
           .names= "tc_{.col}"
  ))

## # A tibble: 1,860 x 8
##      DAX   SMI   CAC  FTSE tc_DAX tc_SMI  tc_CAC tc_FTSE
##    <dbl> <dbl> <dbl> <dbl>  <dbl>  <dbl>   <dbl>   <dbl>
##  1 1629. 1678. 1773. 2444. NA     NA     NA      NA    
##  2 1614. 1688. 1750. 2460. -0.928  0.620 -1.26    0.679
##  3 1607. 1679. 1718  2448. -0.441 -0.586 -1.86   -0.488
##  4 1621. 1684. 1708. 2470.  0.904  0.328 -0.576   0.907
##  5 1618. 1687. 1723. 2485. -0.178  0.148  0.878   0.579
##  6 1611. 1672. 1714. 2467. -0.467 -0.889 -0.511  -0.720
##  7 1631. 1683. 1734. 2488.  1.25   0.676  1.18    0.855
##  8 1640. 1704. 1757. 2508.  0.578  1.23   1.32    0.824
##  9 1635. 1698. 1754  2510. -0.287 -0.358 -0.193   0.0837
## 10 1646. 1716. 1754. 2497.  0.637  1.11   0.0171 -0.522
## # ... with 1,850 more rows

Referencias

- Barter, R. (2020). Across (dplyr 1.0.0): applying dplyr functions simultaneously across multiple columns.

 

- Wickham, H. (2020). dplyr 1.0.0: working across columns. tidyverse.org.


Tidyverse. (2020). Apply a function (or a

Entradas populares de este blog

Valores perdidos (NA) en R: identificación y tratamiento (I)

Ejemplos de tablas en Stata

Métodos y técnicas de Investigación Económica