Tidyr Use Glue Strings Later in Function

Tidy eval now supports glue strings

So this works great:

my_summarise5 <- function(data, mean_var ) {
  data %>% 
    mutate(
      "mean_{{mean_var}}" := mean({{ mean_var }}), 
    ) 
}

mtcars %>% my_summarise5(cyl)

But then

my_summarise5 <- function(data, mean_var ) {
  data %>% 
    mutate(
      "mean_{{mean_var}}" := mean({{ mean_var }}), 
    "mean_{{mean_var}}_plusone" :=  "mean_{{mean_var}}"+1
    )
}

mtcars %>% my_summarise5(cyl)

Throws

Error: Problem with `mutate()` input `mean_cyl_plusone`.
x non-numeric argument to binary

Would some 'paste' or 'glue' thing in the "mean_{{mean_var}}_plusone" := "mean_{{mean_var}}"+1 part fix this?

Note this is obviously not a useful case, its a MWE for the syntax. I actually want to define two new columns with different names, one which uses the other ... otherwise I have to repeat and it also gets messy.

2 Answers

Use across:

my_summarise5 <- function(data, mean_var ) {
  data %>% 
    mutate(
      "mean_{{mean_var}}" := mean({{ mean_var }}),
      across(last_col(), ~.+1, .names = "{col}_plusone")
    )
}

mtcars %>% my_summarise5(cyl) %>% head

giving:

                   mpg cyl disp  hp drat    wt  qsec vs am gear carb mean_cyl mean_cyl_plusone
Mazda RX4         21.0   6  160 110 3.90 2.620 16.46  0  1    4    4   6.1875           7.1875
Mazda RX4 Wag     21.0   6  160 110 3.90 2.875 17.02  0  1    4    4   6.1875           7.1875
Datsun 710        22.8   4  108  93 3.85 2.320 18.61  1  1    4    1   6.1875           7.1875
Hornet 4 Drive    21.4   6  258 110 3.08 3.215 19.44  1  0    3    1   6.1875           7.1875
Hornet Sportabout 18.7   8  360 175 3.15 3.440 17.02  0  0    3    2   6.1875           7.1875
Valiant           18.1   6  225 105 2.76 3.460 20.22  1  0    3    1   6.1875           7.1875
1

I love the tidyverse, but using base R can have a big payoff, by making your code readable and maintainable.

my_summarise5 <- function (data, mean_var) {
  mean_name <- paste0("mean_", mean_var)
  plus_one_name <- paste0(mean_name, "_plus_one")

  data[[mean_name]] <- mean(data[[mean_var]])
  data[[plus_one_name]] <- data[[mean_name]] + 1

  data
}

Anybody who knows R can understand the above.

tmp <- data.frame(a = 1:5)
my_summarise5(tmp, "a")
##   a mean_a mean_a_plus_one
## 1 1      3               4
## 2 2      3               4
## 3 3      3               4
## 4 4      3               4
## 5 5      3               4

If you want to pass a bare symbol as mean_var, then just add this line to the start of the function:

mean_var <- as.character(substitute(mean_var))

This isn't perfect, because it'll break with deeply nested functions. So you could mix a little tidyverse in by doing (something like) mean_var <- enquo(mean_var). But I still would prefer the simple data manipulation to stuff with mutate and across.

Your Answer

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

Marcus Vance

Marcus Vance

Cybersecurity & Digital Privacy Researcher

Marcus Vance is a cybersecurity auditor and technology writer dedicated to educating the public about online safety, data privacy regulations, enterprise security, and emerging cyber threats.

Share this article
Twitter Facebook Pinterest