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