Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

R: Understanding standard evaluation in mutate_

I am trying to mix constants and quoted variable names, as suggested in the Non-standard evaluation vignette using lazyeval::interp.

Here is an example that does what I want:

# create sample dataset
df_foo = data_frame(
  `(Weird) Variable name` = 100,
  group_var = sample(c("Yes", "No"), size = 100, replace = TRUE)
)

# function to update the value of weirdly named variable
update_var_1 = function(var_name) {
  df_foo %>% 
    mutate_(
      "(Weird) Variable name" = 
        interp(quote(ifelse(group_var_val == "Yes", var_name_val/10, var_name_val/20)), 
               group_var_val = as.name("group_var"),
               var_name_val = as.name(var_name))
    )
}

# test the function
update_var_1("(Weird) Variable name") %>% 
  head(n = 20)

Note that I have assigned the result of the lazy evaluation to the character vector ("(Weird) Variable name"). However, when I assign the result of the lazy evaluation to var_name that gets assigned to a literal called "var_name". Can someone help understand this behaviour?

# function to update the value of weirdly named variable
update_var_2 = function(var_name) {
  df_foo %>% 
    mutate_(
      var_name = 
        interp(quote(ifelse(group_var_val == "Yes", var_name_val/10, var_name_val/20)), 
               group_var_val = as.name("group_var"),
               var_name_val = as.name(var_name))
    )
}

# test the function
update_var_2("(Weird) Variable name") %>% 
  head(n = 20)

Should the two functions not have identical results?

like image 685
tchakravarty Avatar asked May 16 '16 17:05

tchakravarty


1 Answers

When calling any R function directly, you cannot use variables for parameter names (and by parameter names I mean those things to the left of the = symbol in a function call). Parameter names are always taken as literal values. These two are the same

f(a=3)
f("a"=3)

or look at

deparse(quote(f(a=3)))
# [1] "f(a = 3)"
deparse(quote(f("a"=3)))
# [1] "f(a = 3)"
a <- "b"
deparse(quote(f(a=3)))
# [1] "f(a = 3)"

a does not have to be a variable for the first to work, and even if such a variable exists, it is ignored. The quotes are basically thrown out when parsing -- it's not really a character value, it's a symbol.

If you need to dynamically set variable names, you need to build your parameters as a list and set the names of that list.

If you are going to pass the name of your parameter as a character value you can use setNames to set that parameter names that you can then pass to the .dots= parameter of the mutate_ function. For example

update_var_3 <- function(var_name) {
  df_foo %>% 
    mutate_(.dots=
    setNames(list(
        interp(quote(ifelse(group_var_val == "Yes", var_name_val/10, var_name_val/20)), 
               group_var_val = as.name("group_var"),
               var_name_val = as.name(var_name)
    )), var_name))
}

update_var_3("(Weird) Variable name") %>% 
  head(n = 20)

This is because these are all equivalent

df <- data_frame(a=1:10)
mutate(df, a=a+5)
mutate(df, "a"=a+5)  #identical to first, not really a character variable
mutate_(df, a=quote(a+5))
mutate_(df, .dots=list(a=quote(a+5)))
mutate_(df, .dots=setNames(list(quote(a+5)),"a"))
like image 87
MrFlick Avatar answered Nov 12 '22 11:11

MrFlick