I've been experimenting a bit recently with NSE and lazyeval, and I was attempting to create expressions for each row of a dataframe, to then be evaluated against another, different dataframe. However, the method I chose throws an error as shown in the example below:
suppressPackageStartupMessages(library(tidyverse))
suppressPackageStartupMessages(library(lazyeval))
my_function <- function(f, data) {
data %>%
mutate(new_column = Sepal.Length + f_eval(f, data))
}
mtcars %>%
transpose() %>%
map_dfr(~ my_function(~uq(.x$gear) + uq(.x$mpg)*Sepal.Length, iris))
#> Error in mutate_impl(.data, dots):
#> Evaluation error: the ... list does not contain 2 elements.
Working through this, it has occurred to me that there are better methods available to acheive the same end result (creating a list-column of expressions in mtcars
, or using tidyr::crossing
seem like more idiomatic alternatives?).
However, I'm still a bit unsure when it comes to actually understanding the error thrown by this example.
As far as I can tell the error is raised like so (hopefully this makes sense - let me know if not):
f_eval
-> eval_expr
-> complain
-> clone_env
-> as.list
Specifically, the formula's environment (f_enf(f)
in f_eval
) is passed into as.list
, which fails because it is empty.
To check this, I inserted a print
into f_eval
like so:
f_eval <- function (f, data = NULL) {
if (!is_formula(f)) {
stop("`f` is not a formula", call. = FALSE)
}
expr <- f_rhs(f_interp(f, data = data))
print(as.list(f_env(f))) # My insertion here
eval_expr(expr, f_env(f), data)
}
#> list()
Reading through Advanced R's section on Environments, my best guess is that when transposing my dataframe into a list, a new environment is created for each list element, rather than inheriting from the global environment. Something like:
suppressPackageStartupMessages(library(tidyverse))
letters[1:3] %>%
map(environment) %>%
map(as.list)
#> [[1]]
#> list()
#>
#> [[2]]
#> list()
#>
#> [[3]]
#> list()
So I guess my questions are:
- Is my best guess anywhere close to the truth?
- What implications does this have for using NSE with
purrr::map
and similar functions, if any?