es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

¿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

Tags:  , , , ,

Answer

  1. Avatar for 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 paquete purrr en lugar de map(). 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 en summarise_func a los datos almacenados en dat_selected.

    Aquí está el código modificado:

    trb_1 %>%
      mutate(
        dat_summarised = pmap(list(dat_selected, summarise_func), ~ ..2(..1))
      )
    

    En este código, pmap() toma una lista de las columnas que quieres mapear (dat_selected y summarise_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:

    # A tibble: 2 x 4
      original_data  cols_to_select summarise_func dat_summarised  
      <list>         <list>         <list>         <list>          
    1 <df [32="" x="" 11]=""> <chr [3]="">      <formula>      <tibble [2="" x="" 2]="">
    2 <df [150="" x="" 5]=""> <chr [3]="">      <formula>      <tibble [3="" x="" 3]="">
    

    Como puedes ver, dat_summarised ahora contiene los datos resumidos para cada conjunto de entrada, como se deseaba.

Comments are closed.