1

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

1 Answer 1

3

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
    )
  )
Sign up to request clarification or add additional context in comments.

3 Comments

Your post helped be better understand dplyr::across(), and how different functions can be incorporated. I hope you don't mind that I changed your formatting a little. It helped me see the signature differences between rlang::prase_expr() and base::eval().
@wibeasley glad to know that it helped. Thanks for the edit.
Thanks for the tip on the named vector for multiplying. I'll keep that in mind for future applications. In the current case, the functions are like readr::parse_datetime() and readr::parse_number()

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.