Reputation: 7517
When outside a function, argument pbkrtest.limit = nobs(m1)
below works just fine (thus, no message is generated).
But when inside the foo()
function, nobs(m1)
is not recognized (thus, a message is generated)! I'm really wondering what is going on?
library(lme4)
library(emmeans)
dat <- read.csv('https://raw.githubusercontent.com/hkil/m/master/z.csv')
m1 <- lmer(y~ year*group + (1|stid), data = dat)
## WORKS FINE:
emtrends(m1, pairwise ~ group, var = "year", infer = c(T, T), pbkrtest.limit = nobs(m1))
## BUT NOW `nobs(m)` doesn't work inside the function:
foo <- function(m){
emtrends(m, pairwise ~ group, var = "year", infer = c(T, T), pbkrtest.limit = nobs(m))
}
## RUN:
foo(m = m1)
Upvotes: 2
Views: 326
Reputation: 6770
I have looked at this more closely in terms of the actual code in emtrends()
. It boils down to what is demonstrated in the following code:
# comparison of evaluation methods with lazy eval
# a trivial function
foo = function(x, ...)
x^2
# calls foo three ways
foodoo = function(...) {
message("x1 = ", foo(...))
message("x2 = ", do.call("foo", list(...)))
cl = match.call()
cl[[1]] = quote(foo)
message("x3 = ", eval(cl))
}
# test value
val = 3.5
# test runs
foodoo(x = 5)
## x1 = 25
## x2 = 25
## x3 = 25
foodoo(x = val)
## x1 = 12.25
## x2 = 12.25
## x3 = 12.25
# same tests, wrapped in a function...
bar = function(z) {
foodoo(x = z)
}
bar(5)
## x1 = 25
## x2 = 25
## Error in foo(x = z): object 'z' not found
bar(val)
## x1 = 12.25
## x2 = 12.25
## Error in foo(x = z): object 'z' not found
Created on 2020-05-31 by the reprex package (v0.3.0)
The problem the OP experienced is related to that third method of evaluation. In emtrends()
, the code matches the call, doctors it up for use by a different function, and then evaluates it. I'm not sure I fully understand all of this, but what is passed in function calls are actually promises, not values; a promose consists of an unevaluated expression and the environment in which it should be evaluated.
In looking at foodoo()
, it appears that, with the first two methods, those promises are updated to the current environment before foo
is called, whereas in the third method, the original call is kept intact so that the promises are not changed. And apparently (I'm still not quite sure why), those original environments are good enough when we call foodoo()
from the global environment, but obviously not good enough when called from a different environment. If anybody cares to comment and explain further, I'm all ears.
Anyway, I am happy because I was able to fix the problem by forcing evaluation of all ...
arguments and replacing them in cl
:
# new foodoo
foodoo = function(...) {
message("x1 = ", foo(...))
message("x2 = ", do.call("foo", list(...)))
cl = match.call()
cl[[1]] = quote(foo)
dots = eval(list(...)) # added
cl[names(dots)] = dots # added
message("x3 = ", eval(cl))
}
bar(5)
## x1 = 25
## x2 = 25
## x3 = 25
bar(val)
## x1 = 12.25
## x2 = 12.25
## x3 = 12.25
Upvotes: 1
Reputation: 269556
Try evaluating it in the parent.frame:
foo <- function(m, envir = parent.frame()) {
s <- substitute(emtrends(m, pairwise ~ group, var = "year", infer = c(TRUE, TRUE),
pbkrtest.limit = nobs(m)))
eval(s, envir)
}
foo(m = m1)
and report it to the developer at https://github.com/rvlenth/emmeans/issues
Also use TRUE rather than T because T can be overridden but TRUE cannot be.
Upvotes: 4