I'd like to define a function that takes a data.frame and a column name and returns the data.frame in with that column transformed (e.g. to lowercase). When the column name is known in advance, this is simple:
diamonds %>% mutate(cut = tolower(cut))
How do I define a function foo
, such that:
col <- "cut"
foo(diamonds, col)
does this same behavior? (Not looking for a base R or data.table
answer since I want to preserve dplyr
's ability to translate this into a lazily-evaluated SQL call).
If I just wanted my function to work using: foo(diamonds, cut)
, I just need enquo
and !!
foo <- function(df, col){
x <- enquo(col)
mutate(df, !!x := tolower(!!x))
}
If I want to take the column name in quotes, foo(diamonds, "cut")
, adding ensym
is sufficient:
foo <- function(df, col){
col <- ensym(col)
x <- enquo(col)
mutate(df, !!x := tolower(!!x))
}
but this fails when given a variable for an argument:
col <- "cut"
foo(diamonds, col)
Error in ~col : object 'col' not found
What am I missing that can evaluate the variable?
You can also avoid tidy eval entirely here by using mutate_at()
.
library(tidyverse)
(x <- tibble(
num = 1:3,
month = month.abb[num]
))
#> # A tibble: 3 x 2
#> num month
#> <int> <chr>
#> 1 1 Jan
#> 2 2 Feb
#> 3 3 Mar
x %>%
mutate(month = tolower(month))
#> # A tibble: 3 x 2
#> num month
#> <int> <chr>
#> 1 1 jan
#> 2 2 feb
#> 3 3 mar
foo <- function(df, col) {
mutate_at(df, .vars = col, .funs = tolower)
}
foo(x, "month")
#> # A tibble: 3 x 2
#> num month
#> <int> <chr>
#> 1 1 jan
#> 2 2 feb
#> 3 3 mar
this <- "month"
foo(x, this)
#> # A tibble: 3 x 2
#> num month
#> <int> <chr>
#> 1 1 jan
#> 2 2 feb
#> 3 3 mar
Created on 2019-03-09 by the reprex package (v0.2.1.9000)