Sean Williams Sean Williams - 6 months ago 77
R Question

Repeatedly mutate variable using dplyr and purrr

I'm self-taught in R and this is my first StackOverflow question. I apologize if this is an obvious issue; please be kind.

Short Version of my Question

I wrote a custom function to calculate the percent change in a variable year over year. I would like to use

function to apply my custom function to a vector of variable names. My custom function works when applied to a single variable, but fails when I chain it using

My custom function

calculate_delta <- function(df, col) {

#generate variable name
newcolname = paste("d", col, sep="")

#get formula for first difference.
calculate_diff <- lazyeval::interp(~(a + lag(a))/a, a =

#pass formula to mutate, name new variable the columname generated above
df %>%
mutate_(.dots = setNames(list(calculate_diff), newcolname)) }

When I apply this function to a single variable in the mtcars dataset, the output is as expected (although obviously the meaning of the result is non-sensical).

calculate_delta(mtcars, "wt")

Attempt to Apply the Function to a Character Vector Using Purrr

I think that I'm having trouble conceptualizing how map_at passes arguments to the function. All of the example snippets I can find online use map_at with functions like
, which don't require additional arguments. Here are my attempts at applying the function using

vars <- c("wt", "mpg")
mtcars %>% map_at(vars, calculate_delta)

This gives me this error message

Error in paste("d", col, sep = "") :
argument "col" is missing, with no default

I assume this is because map_at is passing
as the
, and not passing an argument for
. To get around that issue, I tried the following:

vars <- c("wt", "mpg")
mtcars %>% map_at(vars, calculate_delta, df = .)

That throws me this error:

Error: unrecognised index type

I've monkeyed around with a bunch of different versions, including removing the
argument from the
function, but I have had no luck.

Other potential solutions

1) A version of this using
, rather than
. I've tried solving the problem that way and had similar trouble. And my goal is to figure out a way to do this using purrr, if that is possible. Based on my understanding of
, this seems like a typical use case.

2) I can obviously think of how I would implement this using a for loop, but I'm trying to avoid that if possible for similar reasons.

Clearly I'm thinking about this wrong. Please help!


To clarify, I am curious if there is a method of repeatedly transforming variables that accomplishes two things.

1) Generates new variables within the original
without replacing replace the columns being mutated (as is the case when using

2) Automatically generates new variable labels.

3) If possible, accomplishes what I've described by applying a single function using

It may be that this is not possible, but I feel like there should be an elegant way to accomplish what I am describing.


Try simplifying the process:

delta <- function(x) (x + dplyr::lag(x)) /x
cols <- c("wt", "mpg")

mtcars %>% mutate_at(cols, delta)
mtcars %>% map_at(cols, delta)

#If necessary, in a function
f <- function(df, cols) {
  df %>% mutate_at(cols, delta)

f(iris, c("Sepal.Width", "Petal.Length"))
f(mtcars, c("wt", "mpg"))


If you would like to embed new names after, we can write a custom pipe-ready function:

Rename <- function(object, old, new) {
  names(object)[names(object) %in% old] <- new

mtcars %>% 
  mutate_at(cols, delta) %>% 
  Rename(cols, paste0("lagged",cols))

If you want to rename the resulting lagged variables:

mtcars %>% mutate_at(cols, funs(lagged = delta))