Home > Net >  Transform columns by a list (map) of functions
Transform columns by a list (map) of functions

Time:01-31

I have a named list of functions, where the names(transform_functions) correspond to the column names and the functions are the transformations to be applied to every specific column. For example, in the example below, transform_functions$height is function(x) x 1, so I expect the equivalent outcome of starwars |> mutate(height = height 1).

In total, this should do the equivalent of starwars |> mutate(height = height 1, birth_year = birth_year / 2)

Is there a non-cluttery way to write this? reduce2 works as shown below, but it seems I must be reinventing something here. across is not it, as it applies a specified set of functions to multiple columns.

library(dplyr)
library(purrr)

transform_functions <- list(
  height = function(x) x 1,
  birth_year = function(x) x/2
)



# This works, but is hardly readable and seems like a lot of hassle
purrr::reduce2(
  .x = transform_functions, 
  .y = names(transform_functions),
  .f = function(df, fun, col) 
    df |> mutate(!!col := fun(!!sym(col))),
  .init = starwars)

# Obviously doesn't work, since it would assign (rather than execute) the function to each column 
starwars |>
  mutate(!!!transform_functions)

CodePudding user response:

If you convert/wrap your transform_functions into unevaluated expressions, you can splice them into a mutate call:

transform_expressions = transform_functions |>
    imap(\(f, n) bquote(.(f)(.(as.name(n)))))
starwars |>
    mutate(!!! transform_expressions)

This becomes especially convenient if you can directly encode the expressions, rather than having to convert them from functions:

transform_expressions = alist(
    height = height   1,
    birth_year = birth_year / 2
)

CodePudding user response:

One options is to use imap_dfc() and an across() call:

library(dplyr)
library(purrr)

starwars %>%
  mutate(imap_dfc(transform_functions, ~ across(all_of(.y), .x)))

CodePudding user response:

starwars |>
  mutate(across(names(transform_functions), ~ transform_functions[[cur_column()]](.x)))

Mhm. I don't like cur_column() but I guess it works

  • Related