¿Cómo almacenar tanto la función como los datos de entrada dentro de columnas designadas de una tibble, y luego iterar sobre las filas para ejecutarlos?
Estoy intentando ejecutar un procedimiento de manipulación de datos dentro de un tibble utilizando herramientas del paquete {purrr}
. Mi método es organizar todo lo que necesito dentro de un tibble:
– los datos de entrada dentro de una columna
– la función que se aplicará a los datos de entrada, también tiene su propia columna.
Mi problema: ¿cómo puedo usar las funciones de mapeo de purrr para decir “tomar la función almacenada en la columna x
y aplicarla sobre los datos de la columna y
“?
A continuación se muestra un ejemplo mínimo, basado en mtcars
e iris
. Quiero resumir cada conjunto de datos, en el mismo flujo de trabajo: primero seleccionar las columnas, luego hacer alguna agregación. Para la parte de agregación, configuro anticipadamente 2 funciones, una para cada conjunto de datos.
– summarise_iris()
– summarise_mtcars()
Luego organizo todo lo que necesito dentro de un tibble (ver objeto trb
a continuación).
La primera parte, la selección de columna, funciona bien. Como se puede ver en trb_1
a continuación, dat_selected
es una nueva columna que muté, que almacena la salida del paso de selección.
Sin embargo, la segunda parte no funciona. Quiero tomar la función en la columna summarise_func
y aplicarla sobre los datos almacenados en la columna dat_selected
. Pero no funciona. ¿Por qué no? Utilicé intencionalmente map()
porque mapea solo 1 entrada a la función.
library(purrr)
library(tibble)
library(dplyr, warn.conflicts = FALSE)
summarise_iris <- function(.dat) {
.dat %>%
group_by(Species) %>%
summarise(across(starts_with("Sepal"), ~ mean(.x, na.rm = TRUE)))
}
# para probar: iris %>% summarise_iris()
summarise_mtcars <- function(.dat) {
.dat %>%
group_by(am) %>%
summarise(mpg_median = median(mpg))
}
# para probar: mtcars %>% summarise_mtcars()
trb <-
tribble(~original_data, ~cols_to_select, ~summarise_func,
mtcars, c("am", "disp", "mpg"), ~summarise_mtcars(.),
iris, c("Species", "Sepal.Length", "Sepal.Width"), ~summarise_iris(.)
)
trb_1 <-
trb %>%
mutate(dat_selected = map2(.x = original_data, .y = cols_to_select, .f = ~select(.x, all_of(.y))))
trb_1
#> # A tibble: 2 x 4
#> original_data cols_to_select summarise_func dat_selected
#> <list> <list> <list> <list>
#> 1 <df [32="" x="" 11]=""> <chr [3]=""> <formula> <df [32="" x="" 3]="">
#> 2 <df [150="" x="" 5]=""> <chr [3]=""> <formula> <df [150="" x="" 3]="">
trb_1 %>%
mutate(dat_summarised = map(.x = dat_selected, .f = summarise_func))
#> Error: Problem with `mutate()` column `dat_summarised`.
#> i `dat_summarised = map(.x = dat_selected, .f = summarise_func)`.
#> x Index 1 must have length 1, not 2
¿Cómo puedo lograr la salida deseada (ver abajo) utilizando el método dentro del tibble que intento incorporar? Es decir:
trb_1 %>%
mutate(dat_summarised = map(.x = dat_selected, .f = summarise_func))
para dar la salida deseada que es equivalente a lo que obtenemos si ejecutamos:
summarise_mtcars <- mtcars %>% summarise_mtcars()
summarise_iris <- iris %>% summarise_iris()
trb_1 %>%
tibble::add_column(dat_summarised = list(summarise_mtcars, summarise_iris))
ACTUALIZACIÓN
No sé si lo siguiente es en la dirección correcta, pero basado en esta respuesta, pensé en utilizar rlang::as_function ()
de tal manera que:
trb_1 %>%
mutate(dat_summarised = map(.x = dat_selected, .f = ~rlang::as_function(summarise_func)))
Pero ahora da un error diferente:
x No se puede convertir una lista en una función
davy.ai
Para lograr la salida deseada utilizando el método en tabla que estás tratando de incorporar, puedes utilizar la función
pmap()
del paquetepurrr
en lugar demap()
.pmap()
te permite mapear sobre múltiples entradas al mismo tiempo, lo cual es lo que necesitas hacer en este caso para aplicar la función almacenada ensummarise_func
a los datos almacenados endat_selected
.Aquí está el código modificado:
En este código,
pmap()
toma una lista de las columnas que quieres mapear (dat_selected
ysummarise_func
), y una fórmula que aplica la función de la segunda entrada a los datos de la primera entrada (~ ..2(..1)
).Aquí está la salida de este código:
Como puedes ver,
dat_summarised
ahora contiene los datos resumidos para cada conjunto de entrada, como se deseaba.