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