3

I need to allow the users of a function to add new columns with strings created from the values of the supplied tibble. A simple illustration:

df <- data.frame(session=rep(LETTERS[1:3],2),
                 bundle=rep(letters[1:2],3),
                 xn1=sample(1:100,6),
                 xn2=sample(1:100,6))

myfun <- function(df, ...){
  dplyr::mutate(.data=df,!!!rlang::enexprs(...)) |>
    dplyr::mutate(across(where(is.character),~ glue::glue))
}

myfun(df,a=session,b="dsds{session}") 

which if the df is this:

> df
  session bundle xn1 xn2
1       A      a  31  95
2       B      b  45  64
3       C      a  12  38
4       A      b  56  13
5       B      a  70  93
6       C      b  53  73

then I want to get this output if myfun


> myfun(df,a=session,b="dsds{a}")
  session bundle xn1 xn2 a b
1       A      a  31  95 A dsdsA
2       B      b  45  64 B dsdsB 
3       C      a  12  38 C dsdsC
4       A      b  56  13 D dsdsA
5       B      a  70  93 B dsdsB
6       C      b  53  73 C dsdsC
 

But right now I get the errors that glue could not find the session values.

Error in `dplyr::mutate()` at test.R:14:2:
ℹ In argument: `across(where(is.character), ~glue::glue)`.
Caused by error in `across()`:
! Can't compute column `a`.
Caused by error in `dplyr_internal_error()`:
Run `rlang::last_trace()` to see where the error occurred.

Please note that I am not just trying to change the tibble. What I need is to create the myfun() function that would allow the user to create columns in the tibble flexibly.

Fredrik Karlsson
  • 485
  • 8
  • 21
  • I don't understand what the `a = session` term is supposed to be doing. Is it supposed to be ignored? – Jon Spring Apr 14 '23 at 17:32
  • I also don't understand what you mean by "apply ... to all charactered columns". It seems like you are adding a field, but not changing existing columns, whether character or not. – Jon Spring Apr 14 '23 at 17:38
  • Apologies. `a=session` has its effect in the first mutate but I forgot to edit in that output in the resulting tibble. Editing. – Fredrik Karlsson Apr 15 '23 at 06:43

2 Answers2

5

Is this what you're after? This changes character arguments into glue expressions before splicing everything into mutate():

library(dplyr)
library(rlang)
library(glue)

myfun <- function(.data, ...){
  vars <- exprs(...)
  vars <- lapply(vars, \(x)  if (is.character(x)) expr(glue(!!x)) else x)
  mutate(.data = .data, !!!vars) 
}

myfun(df, a = session, b = "dsds{a}", a_and_b = "{a} and {b}") 

  session bundle xn1 xn2 a     b     a_and_b
1       A      a  25  61 A dsdsA A and dsdsA
2       B      b 100  66 B dsdsB B and dsdsB
3       C      a  86  26 C dsdsC C and dsdsC
4       A      b  27  33 A dsdsA A and dsdsA
5       B      a  58  20 B dsdsB B and dsdsB
6       C      b   7  74 C dsdsC C and dsdsC
Ritchie Sacramento
  • 29,890
  • 4
  • 48
  • 56
  • Yes! That works, and even handles for instance `myfun(df, a = session, b = "dsds{a}", a_and_b_and_session = "{a} and {b} and {session}") `. Fantastic! – Fredrik Karlsson Apr 19 '23 at 07:02
2

How about this?

myfun2 <- function(df, colname, glue_phrase) {
  dplyr::mutate(df, {{ colname }} := glue::glue({{ glue_phrase }}))
}
    
df %>%
  myfun2(a, "{session}") %>%
  myfun2(b, "dsds{session}")

  session bundle xn1 xn2 a     b
1       A      a  38  70 A dsdsA
2       B      b  20  40 B dsdsB
3       C      a  28  44 C dsdsC
4       A      b  99  25 A dsdsA
5       B      a  44 100 B dsdsB
6       C      b  87  39 C dsdsC
Jon Spring
  • 55,165
  • 4
  • 35
  • 53
  • Perhaps your question is how to allow the user to do multiple column additions in one go, but if so, I will defer to more experienced users to figure that out! – Jon Spring Apr 14 '23 at 17:38
  • Yes, it is about the application of glue to all character columns that may be in the tibble, but for which the names are only known the user of the function. – Fredrik Karlsson Apr 15 '23 at 08:14
  • Edited to show revised output. I'm not sure how to do it in one step but maybe this is close enough. – Jon Spring Apr 15 '23 at 16:49