1

currently I am running into some problems with Non-Standard Evaluation when trying to wrap a function around some calculations done with dplyr und purrr that I use on multiple occasions.

I have read about NSE and also think I know the point where my function does not work properly - however, I could not figure out why this is.

Examplarily, I want to wrap a function around the following calculations, where the grouping variable as well as the name of the new variable, the utilized categorization variable and the input variable for the mean should be dynamic:

 Data <- Data %>%
  group_by(WeekBeforeRelease) %>%
  mutate(visitors_genreother_instr = map_dbl(Genre_Category, ~ mean(Visitors[Genre_Category != .x]))) %>%
  ungroup() %>%
  as.data.frame()

Which turns this function into the following, using NSE as described here:

Function_Other <- function(ENDOGVAR, VARNAME, GROUP_MOVIE, GROUP_TIME){

  ENDOGVAR <- enquo(ENDOGVAR)
  VARNAME <- quo_name(enquo(VARNAME))
  GROUP_MOVIE <- enquo(GROUP_MOVIE)
  GROUP_TIME <- enquo(GROUP_TIME)

  Data <<- Data %>%
    group_by(!!GROUP_TIME) %>%
    mutate(!!VARNAME := map_dbl(!!GROUP_MOVIE, ~mean(!!ENDOGVAR[!!GROUP_MOVIE != .x]))) %>%
    ungroup() %>%
    as.data.frame()
}

However, this does not seem to handle the subsetting with brackets in the mean calculation well. If I subtitute the !!ENDOGVAR with Visitors, everything works fine and as intended. However, as is, it produces the following error:

Error in NextMethod("[") : object '.x' not found 

I am happy about any help that points me towards understanding this problem.

Thanks a lot in advance!

rondo

1 Answers1

1

We can wrap the !! in braces to avoid any precedence of operations and should now work fine

library(tidyverse)
Function_Other <- function(ENDOGVAR, VARNAME, GROUP_MOVIE, GROUP_TIME){

  ENDOGVAR <- enquo(ENDOGVAR)
  VARNAME <- quo_name(enquo(VARNAME))
  GROUP_MOVIE <- enquo(GROUP_MOVIE)
  GROUP_TIME <- enquo(GROUP_TIME)

  Data %>%
    group_by(!!GROUP_TIME) %>%
    mutate(!!VARNAME := map_dbl(!!GROUP_MOVIE, ~
           mean((!!ENDOGVAR)[(!!GROUP_MOVIE) != .x]))) %>%
    ungroup() %>%
    as.data.frame()

}


Data <- mtcars
out <- Function_Other(mpg, newcol, am, gear)
head(out, 3)
#   mpg cyl disp  hp drat    wt  qsec vs am gear carb newcol
#1 21.0   6  160 110 3.90 2.620 16.46  0  1    4    4  21.05
#2 21.0   6  160 110 3.90 2.875 17.02  0  1    4    4  21.05
#3 22.8   4  108  93 3.85 2.320 18.61  1  1    4    1  21.05

Update

With rlang 0.4.0 (tested with dplyr 0.8.2) we can also use {{...}} for substitute, quote, and unquote. The previous function can be written as

Function_OtherN <- function(ENDOGVAR, VARNAME, GROUP_MOVIE, GROUP_TIME){  


  Data %>%
    group_by({{GROUP_TIME}}) %>%
    mutate({{VARNAME}} := map_dbl({{GROUP_MOVIE}}, ~
           mean({{ENDOGVAR}}[{{GROUP_MOVIE}} != .x]))) %>%
    ungroup() %>%
    as.data.frame()

}


out1 <- Function_OtherN(mpg, newcol, am, gear)

-checking with previous output

identical(out1, out)
[1] TRUE
akrun
  • 874,273
  • 37
  • 540
  • 662
  • 1
    Akrun, you're my hero - thanks so much, that has solved the problem (and my night!). Any idea on why the evaluation fails without the brackets? – Rondo Bohrens Apr 03 '19 at 04:00
  • @RondoBohrens The reason could be the operation precedence as there are lots of activitities happening where you are creating some logical expression, subsetting et.c – akrun Apr 03 '19 at 04:01
  • 1
    the solution by @akrun works perfectly but two things should be pointed out for good programming practices (which @akrun surely knows but some new readers might not): 1. pass the data instead of using a global variable Data. 2. passing the variables unquoted won't work if they are passed from a list or c() binding. it would be more practical to pass them as strings - in that case, they must be transformed (analogue to enquo) by 'rlang::syms()` – Agile Bean Aug 01 '19 at 10:32