Home > Mobile >  Dynamically apply a specific function to different variables
Dynamically apply a specific function to different variables

Time:11-29

I have a csv that defines one row per variable (referring to real data variables that are defined in another dataset). After light massaging, I have a named vector like fxs.

I then can iterate over the variables, and execute the appropriate function. Is there another way I should consider? Perhaps with rlang::exec() and/or purrr::map_*(). I like that I can wrap the two main lines with separate tryCatch() blocks to more accurately describe if there was an error parsing the function, or an error executing the function.

fxs <- c(
  "Sepal.Length"   = "\\(x) x *   1",
  "Sepal.Width"    = "\\(x) x *  10",
  "Petal.Length"   = "\\(x) x * 100"
)

d <- iris[1:5, 1:3]
for (variable in colnames(d)) {
  fx <- base::eval(base::parse(text = fxs[[variable]]))
  d[[variable]] <- fx(d[[variable]])
}
d

I was thinking it looks a lot like the inside of a dplyr::mutate() statement. Maybe it's better to try to convert the csv into statements passed to .... I haven't done anything beyond this proof-of-concept, so I have a lot of flexibility if someone has a different approach.

d |> 
  dplyr::mutate(
    Sepal.Length   = Sepal.Length *   1,
    Sepal.Width    = Sepal.Width  *  10,
    Petal.Length   = Petal.Length * 100,
  )

Desired output:

  Sepal.Length Sepal.Width Petal.Length
1          5.1          35          140
2          4.9          30          140
3          4.7          32          130
4          4.6          31          150
5          5.0          36          140

CodePudding user response:

We may use cur_column() to subset the expression within across

library(dplyr)
d %>% 
  dplyr::mutate(
    dplyr::across(
      .cols = dplyr::all_of(names(fxs)), 
      .fns  = ~ eval(parse(text = fxs[[dplyr::cur_column()]]))(.x)
    )
  )

-output

  Sepal.Length Sepal.Width Petal.Length
1          5.1          35          140
2          4.9          30          140
3          4.7          32          130
4          4.6          31          150
5          5.0          36          140

Or using exec

library(purrr)
d %>% 
  dplyr::mutate(
    dplyr::across(
      .cols = dplyr::all_of(names(fxs)), 
      .fns  = ~ rlang::exec(
        rlang::parse_expr(fxs[[dplyr::cur_column()]]), 
        .x
      )
    )
  )

If the functions are all doing the multiplication, instead create a named vector or list of values to be multiplied and avoid the eval/parse

nm1 <- c(Sepal.Length = 1, Sepal.Width = 10, Petal.Length = 100)
d %>% 
  dplyr::mutate(
    dplyr::across(
      .cols = dplyr::all_of(names(nm1)),
      .fns  = ~ nm1[dplyr::cur_column()] * .x
    )
  )
  • Related