be_*_*een 5 r non-standard-evaluation
我正在编写一个需要加权回归的函数。我一再收到权重参数的错误,并且我创建了一个最小的可重现示例,您可以在此处找到:
wt_reg <- function(form, data, wts) {
lm(formula = as.formula(form), data = data,
weights = wts)
}
wt_reg(mpg ~ cyl, data = mtcars, wts = 1:nrow(mtcars))
Run Code Online (Sandbox Code Playgroud)
这返回
eval(extras, data, env) 中的错误:找不到对象“wts”
如果你单独运行这一切,它工作正常。我已经深入研究了 lm,看来问题是调用eval(mf, parent.frame())
. 即使 wts 在 parent.frame() 中,它似乎也没有在调用中正确评估。这里有更多细节:
mf 被分配为与
stats::model.frame(formula = as.formula(form), data = data, weights = wts,
drop.unused.levels = TRUE)
Run Code Online (Sandbox Code Playgroud)
当我跑
parent.frame()$wts
Run Code Online (Sandbox Code Playgroud)
它确实返回一个数字向量。但是当我跑
eval(stats::model.frame(formula = as.formula(form), data = data, weights = wts,
drop.unused.levels = TRUE), parent.frame())
Run Code Online (Sandbox Code Playgroud)
它没有。
我可以跑
stats::model.frame(formula = as.formula(parent.frame()$form),
data = parent.frame()$data, weights = parent.frame()$wts,
drop.unused.levels = TRUE)
Run Code Online (Sandbox Code Playgroud)
它有效。如果您想使用顶部的示例,您可以自己测试。
有什么想法吗?我真的不知道这里发生了什么......
公式在 R 中的特殊之处在于它们不仅跟踪符号/变量名称,还跟踪创建它们的环境。查看
ff <- mpg ~ cyl
environment(ff)
# <environment: R_GlobalEnv>
foo <- function() {
ff <- mpg ~ cyl
environment(ff)
}
foo()
# <environment: 0x0000026172e505d8> private function environment (different each time)
Run Code Online (Sandbox Code Playgroud)
问题是lm
会尝试使用创建公式的环境来查找变量而不是父框架。由于您在对 的调用中创建了公式,因此wt_reg
该公式适用于全局范围。但wts
只存在于函数作用域中。您可以更改函数以将公式上的环境更改为本地函数环境,然后一切正常
wt_reg <- function(form, data, wts) {
ff <- as.formula(form)
environment(ff) <- environment()
lm(formula = ff, data = data,
weights = wts)
}
wt_reg(mpg ~ cyl, data = mtcars, wts = 1:nrow(mtcars))
Run Code Online (Sandbox Code Playgroud)
在eval(mf, parent.frame)
你指的是在lm()
呼唤model.frame()
你的公式。从?model.frame
帮助页面上的描述来看:“公式、子集和...中的所有变量首先在数据中查找,然后在公式环境中查找(有关详细信息,请参阅公式()的帮助)并收集到一个数据框”。所以它再次查看公式的环境,而不是调用框架。