tidyr: using mutate inside a function
Asked Answered
V

2

6

I'd like to use mutate function from the tidyverse to create a new column based on the old column using only a data frame and strings, which represent column headers, as inputs.

I can get this to work without using the tidyverse (see function f below), but I'd like to get it to work using the tidyverse (see function f.tidy below)

Can someone please post a solution for adding this column using mutate called from a inside function?

df <- data.frame('test' = 1:3, 'tcy' = 4:6)
# test tcy
#    1   4
#    2   5
#    3   6  

f.tidy <- function(df, old.col, new.col) {
  df.rv <- df %>%
    mutate(new.col = .data$old.col + 1)
  return(df.rv)
}

f <- function(df, old.col, new.col) {
  df.rv <- df
  df.rv[, new.col] <- df.rv[, old.col] + 1
  return(df.rv)
}

old.col <- 'tcy'
new.col <- 'dan'

f.tidy(df = df, old.col = old.col, new.col = new.col)
# Evaluation error: Column 'old.col': not found in data
f(df = df, old.col = old.col, new.col = new.col)
# Produces Desired Output:
# test tcy dan
#    1   4   5
#    2   5   6
#    3   6   7
Valence answered 30/3, 2018 at 14:29 Comment(1)
You should review the Programming with dplyr guide: dplyr.tidyverse.org/articles/programming.htmlPreach
P
8

We could use rlang to convert it to symbol and then evaluate with !!

f.tidy <- function(df, old.col, new.col) {

  df %>%
      mutate(!! (new.col) := !!rlang::sym(old.col) + 1)

}

f.tidy(df = df, old.col = old.col, new.col = new.col)
#   test tcy dan
#1    1   4   5
#2    2   5   6
#3    3   6   7

Or another option is mutate_at with rename_at

f.tidy <- function(df, old.col, new.col) {

 df %>%
    mutate_at(vars(old.col),  funs(new = .+ 1)) %>%
    rename_at(vars(matches("new")), ~ new.col)

 }

f.tidy(df = df, old.col = old.col, new.col = new.col)
#   test tcy dan
#1    1   4   5
#2    2   5   6
#3    3   6   7
Polymeric answered 30/3, 2018 at 14:34 Comment(0)
C
1

I know this has been answered but I had a problem understanding the solution myself so after experimenting and watching https://www.youtube.com/watch?v=YlZuT6PWOEY&ab_channel=IDGTECHtalk I hope this can help somebody else.

This is my solution using {{}} and enquo() from package rlang:

library(rlang)
library(dplyr)
df <- data.frame('test' = 1:3, 'tcy' = 4:6)
f_tidy <- function(df, old_col, new_col_str){
  old_col_quo <- enquo(old_col) # Store as "meta" variable to evaluate when needed using "!!".
  df <- df %>%
    mutate({{new_col_str}} := !!old_col_quo + 1)
  return(df)
}
df %>% f_tidy(old_col = tcy, new_col_str = "mutated_col")

Output:

  test tcy mutated_col
1    1   4           5
2    2   5           6
3    3   6           7 
Chuddar answered 4/1, 2023 at 8:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.