为什么这个调用`lm(...,subset)的简单函数失败了?

时间:2016-05-19 14:48:56

标签: r function

我正在开发一个包含对lm()的调用的自定义函数,但由于某种原因,该函数失败了。我无法理解它失败的原因。

考虑将此示例简化为简单:

myfun <- function(form., data., subs., ...){
    lm(form., data., subs., ...)
}

这将导致错误:

myfun(mpg ~ cyl + hp, mtcars, TRUE)
## Error in eval(expr, envir, enclos) : object 'subs.' not found

但是直接使用lm()可以正常工作:

lm(mpg ~ cyl + hp, mtcars, TRUE)
## 
## Call:
## lm(formula = mpg ~ cyl + hp, data = mtcars, subset = TRUE)
## 
## Coefficients:
## (Intercept)          cyl           hp  
##    36.90833     -2.26469     -0.01912  

我试过调试,但仍然无法解决问题的根源。为什么自定义功能失败?显然subs. 已经提供给函数...

编辑:

虽然下面提出的大多数解决方案都有助于这个简单的情况,但如果我添加一个简单的扭曲,该功能仍将失败。例如,expand.model.frame()依赖于公式的环境,但如果我使用正常的评估解决方案则会失败:

myfun <- function(form., data., subs., ...){
    fit <- lm(form., data.[ subs., ], ...)
    expand.model.frame(fit, ~ drat)
}

myfun(mpg ~ cyl + hp, mtcars, TRUE)
## Error in eval(expr, envir, enclos) : object 'data.' not found

这显然与原始问题有关,但我无法弄清楚如何。模型公式的环境是否以某种方式被破坏了?

4 个答案:

答案 0 :(得分:5)

正如评论中所建议的,另一种解决方案是在非交互式使用中完全避免subset参数,而是使用标准评估:

myfun <- function(form., data., subs., ...){
    lm(form., data.[ subs., ], ...)
}

现在这可以按预期工作:

myfun(formula(mpg ~ cyl + hp), mtcars, TRUE)

但是,如果您的自定义函数随后包含expand.model.frame()或类似的调用,这似乎本身对subset参数的非标准评估很敏感,那么这仍然不够。为了使函数健壮并避免意外,您需要(1)在自定义函数中定义公式(另请参阅reformulate approach)和(2)将数据之前子集到lm()在显着避免subset参数时调用。

像这样:

myfun <- function(form., data., subs., ...){
    stopifnot(is.character(form.))
    data. <- data.[ subs., ]
    fit <- lm(as.formula(form.), data., ...)
    expand.model.frame(fit, ~ drat)
}

myfun("mpg ~ cyl + hp", mtcars, TRUE)

我尝试使用(1)或(2),但仍设法从某些函数中遇到奇怪的错误,而且只有(1)和(2)这两个错误似乎已经消失了远...

答案 1 :(得分:4)

此函数不起作用的原因是因为评估参数subset的方式:

  

所有'权重','子集'和'偏移'都在相同的情况下进行评估   作为'公式'中的变量的方式,首先是'数据',然后是'   '公式'的环境。

换句话说,lmsubs.中查找名为data的变量,然后在formula的环境中查找,因为没有subs.在这两种环境中的变量都会产生错误。

答案 2 :(得分:3)

您可以这样做:

myfun <- function(form., data., subs., ...){
    lm(as.formula(form.), data., subs., ...)
}

将其称为myfun("mpg ~ cyl + hp", mtcars, T)。这会强制在函数myfun的环境中创建公式,然后该函数将包含subs.

答案 3 :(得分:3)

根据@ErnestA的答案,您可以修改您的功能,以确保公式subs.的环境中存在form.

myfun <- function(form., data., subs., ...){
assign("subs.", subs., envir=environment(form.))
lm(form., data., subs., ...)
}

ETA避免污染form的环境,因此可以创建一个新的环境:

myfun <- function(form., data., subs., ...){
environment(form.) <- new.env(parent=environment(form.))
assign("subs.", subs., envir=environment(form.))
lm(form., data., subs., ...)
}

ETA也许唯一解决lm问题的方法是将form.的环境设置为myfun的环境:

myfun <- function(form., data., subs., ...){
environment(form.) <- environment()
lm(form., data., subs., ...)
}
myfun(mpg ~ cyl + hp, mtcars, TRUE)
## Call:
##   lm(formula = form., data = data., subset = subs.)
## 
## Coefficients:
##   (Intercept)          cyl           hp  
##      36.90833     -2.26469     -0.01912  

转到expand.model.frame问题,找不到subs.,尽管它位于使用?expand.model.frame所述的环境中。这是expand.model.frame中的错误吗?或至少与文档冲突?

myfun <- function(form., data., subs., ...){
environment(form.) <- environment()
fit <- lm(form., data., subs., ...)
print(ls(environment(formula(fit))))
expand.model.frame(fit, ~drat )
}
myfun(mpg ~ cyl + hp, mtcars, TRUE)
## [1] "data." "fit"   "form." "subs."
##  Error in eval(expr, envir, enclos) : object 'subs.' not found

subs.放入父环境似乎有效。

myfun <- function(form., data., subs., ...){
environment(form.) <- environment()
fit <- lm(form., data., subs., ...)
assign("subs.", subs., envir = parent.env(environment(formula(fit))))
expand.model.frame(fit, ~drat)
}
myfun(mpg ~ cyl + hp, mtcars, TRUE)
## mpg cyl  hp drat
## Mazda RX4           21.0   6 110 3.90
## Mazda RX4 Wag       21.0   6 110 3.90
## Datsun 710          22.8   4  93 3.85
## Hornet 4 Drive      21.4   6 110 3.08
## etc.

但这会导致污染父环境的问题,在这种情况下R_GlobalEnv。我无法使用除R_GlobalEnv之外的任何其他内容作为父级。