Use custom function (with tidy eval) within mutate & across

I have the following data:

my_data <- structure(list(case_one = c("A", "B", "A", "B", "A", "B", "A", 
"B", "A", "B"), case_two = c("A", "A", "A", "A", "A", "B", "B", 
"B", "B", "B"), id = 1:10), class = c("tbl_df", "tbl", "data.frame"
), row.names = c(NA, -10L))


# A tibble: 10 × 3
   case_one case_two    id
   <chr>    <chr>    <int>
 1 A        A            1
 2 B        A            2
 3 A        A            3
 4 B        A            4
 5 A        A            5
 6 B        B            6
 7 A        B            7
 8 B        B            8
 9 A        B            9
10 B        B           10

What works:

creating custom function and using it to transform each column individually

times_100 <- function(data, case_column){
  data %>%
    mutate("{{case_column}}" := case_when(
      {{case_column}} == "A" ~ paste({{case_column}}, id, sep = "_"),
      {{case_column}} == "B" ~ paste({{case_column}}, id * 100, sep = "_")
    ))
}


my_data %>%
  times_100(case_one) %>% 
  times_100(case_two)


# A tibble: 10 × 3
   case_one case_two    id
   <chr>    <chr>    <int>
 1 A_1      A_1          1
 2 B_200    A_2          2
 3 A_3      A_3          3
 4 B_400    A_4          4
 5 A_5      A_5          5
 6 B_600    B_600        6
 7 A_7      B_700        7
 8 B_800    B_800        8
 9 A_9      B_900        9
10 B_1000   B_1000      10

What I want:

Using the custom function to work within mutate(across(contains("case")), so I can apply it to multiple columns at once.

# like this: 
my_data %>%
  mutate(across(contains("case"), ~ tolower(.x)))

# but with my custom function

I tried different ways of changing my function so it would work within that context, but cant get it to work.

>Solution :

Another option, which corresponds to your expected function, is to drop the mutate call and the data argument and use case_when() directly, but we need to add id as argument to access the column:

library(dplyr)

times_100 <- function(x, id) {
  case_when(
      x == "A" ~ paste(x, id, sep = "_"),
      x == "B" ~ paste(x, id * 100, sep = "_")
    )
}

my_data %>%
  mutate(across(contains("case"), ~ times_100(.x, id)))

Data from OP

my_data <- structure(list(case_one = c("A", "B", "A", "B", "A", "B", "A", 
"B", "A", "B"), case_two = c("A", "A", "A", "A", "A", "B", "B", 
"B", "B", "B"), id = 1:10), class = c("tbl_df", "tbl", "data.frame"
), row.names = c(NA, -10L))

Leave a Reply