11

I want to take two variables from a table and divide them by a third variable and add these computations as two new columns. The mutate_at gets me very close but within the custom function, f below, I want to access another column in the data set. Any suggestions or alternate tidy tools approaches?

library(dplyr)
# this works fine but is NOT what I want
f <- function(fld){
  fld/5
}

# This IS what I want where wt is a field in the data
f <- function(fld){
  fld/wt
}

mutate_at(mtcars, .vars = vars(mpg, cyl), .funs = funs(xyz = f))

# This works but is pretty clumsy
f <- function(fld, dat) fld/dat$wt
mutate_at(mtcars, .vars = vars(mpg, cyl), .funs = funs(xyz = f(., mtcars)))

# This is closer but still it would be better if the function allowed the dataset to be submitted to the function without restating the name of the dataset

f <- function(fld, second){
  fld/second
}

mutate_at(mtcars, .vars = vars(mpg, cyl), .funs = funs(xyz = f(., wt)))

4 Answers 4

14

Updated version for dplyr 1.0.6:

mtcars %>% 
  mutate(across(c(mpg, cyl), ~ . / wt))

Or this, which may be slower

mtcars %>% 
  mutate(across(c(mpg, cyl), `/`, wt))

Previous answer:

library(tidyverse)
f <- function(num, denom) num/denom

mtcars %>% 
  mutate_at(vars(mpg, cyl), f, denom = quote(wt))

Although in this specific example, a custom function isn't needed.

mtcars %>% 
  mutate_at(vars(mpg, cyl), `/`, quote(wt))
2
  • I think this answer is just as clumsy as the examples in the question. Sure, you stripped out all the .funs=funs... mess, but that was not the point of the question. This is just two more ways to do exactly what the solutions in the question did: call the function and pass it a second argument inside mutate(). The key point in the question is to find a way to avoid having to pass that second argument.
    – randy
    Commented Jul 3, 2021 at 0:46
  • Sorry for not being clear (and for the confrontational language). The denominator must be provided, but it is possible to do so without making it a function argument. I think this was the intent of the question because of the statement “This IS what I want”, which is followed by a function with only one argument, and because the question already includes two working solutions that do the same thing that this answer does but with slightly different syntax. I think this answer would be improved by showing the option f <- function(num) num/cur_data()$denom. If you don’t mind, I will add it.
    – randy
    Commented Jul 3, 2021 at 20:47
11

Maybe something like this?

f <- function(fld,var){
    fld/var
}

mtcars %>%
    mutate_at(vars(mpg,cyl), .funs = funs(xyz = f(.,wt)))

Edit (2020-08-24):

As of second semester of 2020, with the launch of dplyr 1.0.0, mutate_at has been superseded by combining mutate with the across function:

mtcars %>%
    mutate(across(c(mpg, cyl), ~ f(.,wt), .names = "{col}_xyz"))
1

Why not simply

mutate(mtcars, mpg2 = mpg / wt, cyl2 = cyl / wt)
2
  • That works for a couple of fields, but is not flexible and would be cumbersome with, say, 10 fields. If you changed your mind about the suffix ("2") you'd have to change it in several places etc.
    – ZRoss
    Commented Apr 10, 2018 at 17:07
  • Agreed, I guess i missed the crux of your question
    – whalea
    Commented Apr 10, 2018 at 17:10
1

There is a cur_data() function that will help make the mutate_at() call more compact because you will not have to specify a second argument to the function that is being applied to each column:

f <- function(fld){
  fld / cur_data()$wt
}
mutate_at(mtcars, .vars=vars(mpg, cyl), .funs=funs(xyz = f))

Additional notes:

  1. If you need the function to reference a grouping variable, use cur_data_all()
  2. mutate_at is now superseded by mutate(.data, across()), so it would be better to do
mtcars %>% mutate(across(.cols=c(mpg, cyl), .fns=f, .names='{.col}_xyz'))

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.