update()具有局部协变量的函数内的模型

时间:2014-05-03 13:22:31

标签: r glm lm

我需要从函数内部更新回归模型。理想情况下,该函数应适用于任何类型的模型(lmglmmultinomclm)。更准确地说,我需要添加一个或几个在函数内定义的协变量。这是一个例子。

MyUpdate <- function(model){
     randData <- data.frame(var1=rnorm(length(model$residuals)))
     model2 <- update(model, ".~.+randData$var1")
     return(model2)
}

以下是一个使用示例

data(iris)
model1 <- lm(Sepal.Length~Species, data=iris)
model2 <- MyUpdate(model1)

eval(expr,envir,enclos)中的错误:找不到对象'randData'

以下是glm的另一个例子

model1 <- glm(Sepal.Length>5~Species, data=iris, family=binomial)
model2 <- MyUpdate(model1)

有什么想法吗?

2 个答案:

答案 0 :(得分:8)

问题是var1在数据框和模型的环境中被查找,但不在MyUpdate的环境中。

1)要避免此问题,请更新模型,不仅要修改公式,还要修改包含var1的修订数据框:

MyUpdate <- function(model) {
     mf <- model.frame(model)
     n <- nrow(mf)
     var1 <- rnorm(n)
     update(model, formula = . ~ . + var1, data = data.frame(mf, var1))
}

以上可能是本答案中提出的解决方案的最佳解决方案,因为它避免了内部结构的混乱。它似乎适用于lmglmmultinomclm。下面的其他解决方案确实与内部结构相关,因此在模型拟合程序中不太通用。其他人都使用lm,但可能不适用于其他人。

测试如果MyUpdate如上所述,这是一个在问题中提到的每个模型拟合函数上都没有错误运行的测试,并且(2)中的解决方案都运行了测试没有错误。解决方案(3)至少与lm一起工作。

model.lm <- lm(Sepal.Length~Species, data=iris)
MyUpdate(model.lm)

model.glm <- glm(Sepal.Length~Species, data=iris)
MyUpdate(model.glm)

library(nnet)
example(multinom)
MyUpdate(bwt.mu)

library(ordinal)
model.clm <- clm(rating ~ temp * contact, data = wine)
MyUpdate(model.clm)

其余解决方案可以更直接地访问内部构件,从而降低了更改模型功能的可靠性。

2)与环境混淆

此外,这里有三个涉及混乱环境的解决方案。第一个是最干净的,然后是第二个,然后是第三个。第三个是最不可接受的,因为它实际上将var1写入模型的环境(危险地覆盖那里的任何var1),但它是最短的。他们使用lmglm multinomclm

请注意,我们并不需要将var1放入数据框中,也不需要将更新公式放在引号中,我们在下面的所有示例中都进行了更改。此外,return语句也可以删除,我们也这样做了。

2a)以下内容修改原始模型的环境以指向包含var1的新代理原型对象,其父级是原始模型环境。这里proto(p, var1 = rnorm(n))是代理原型对象(原型对象是具有不同语义的环境),p是代理的父级。

library(proto)

MyUpdate <- function(model){

     mf <- model.frame(model)
     n <- nrow(mf)
     var1 <- rnorm(n)
     p <- environment(formula(model))

     if (is.null(model$formula)) {
           attr(model$terms, ".Environment") <- proto(p, var1 = var1)
     } else environment(model$formula) <- proto(p, var1 = var1)

     update(model, . ~ . + var1)
}

有关详细信息,请参阅本文档中的代理部分:http://r-proto.googlecode.com/files/prototype_approaches.pdf

2b)这可以在没有原型的情况下完成,但代价是将##行扩展为包含一些额外丑陋环境操作的三行。这里e是代理环境。

MyUpdate <- function(model){
     mf <- model.frame(model)
     n <- nrow(mf)
     var1 <- rnorm(n)
     p <- environment(formula(model))

     e <- new.env(parent = p)
     e$var1 <- var1

     if (is.null(model$formula)) attr(model$terms, ".Environment") <- e
     else environment(model$formula) <- e

     update(model, . ~ . + var1)
}

2c)最短但最苛刻的是将var1粘贴到原来的model环境中:

MyUpdate <- function(model){
     mf <- model.frame(model)
     n <- nrow(mf)
     var1 <- rnorm(n)       

     if (is.null(model$formula)) attr(model$terms, ".Environment")$var1 <- var1
     else environment(model$formula)$var1 <- var1

     update(model, . ~ . + var1)
}

3)eval / substitute 此解决方案确实使用eval,这有时令人不悦。它可以在lmglm以及clm上工作,但输出不会显示var1,而是显示计算它的表达式。

MyUpdate <- function(model) {
     m <- eval.parent(substitute(update(model, . ~ . + rnorm(nrow(model.frame(model))))))
     m$call$formula <- update(formula(model), . ~ . + var1)
     names(m$coefficients)[length(m$coefficient)] <- "var1"
     m
}

已修订添加了其他解决方案,简化(1),在(2)中获得解决方案以运行测试部分中的所有示例。

答案 1 :(得分:3)

一些理论。公式对象通常具有关联的环境:

frm1 <- y~x # a formula created in the global environment ("in the console")
attr(frm1, ".Environment") # see also unclass(frm1)
## <environment: R_GlobalEnv>

此处,作用于frm1的函数将知道他们将在全球环境中寻找yx(除非另有说明,请参阅例如data arg of lm())。另一方面:

f <- function() { y~x }; frm2 <- f() # a formula created in a function
attr(frm2, ".Environment")
## <environment: 0x2f87e48>

这样的公式指向yx是&#34;局部变量&#34;在f()

如果将在全局环境中创建的公式传递给函数,则大多数情况下无法引用在该函数中创建的对象。

解决方案。基本的公式和环境有点“隐藏”和#34;使用lm()返回的对象。但是可以访问它们。下面的代码可以解决您的问题。

MyUpdate <- function(model){
     assign("randData", data.frame(var1=rnorm(length(model$residuals))),
        envir=attr(model1$terms, ".Environment"))
     model2 <- update(model, ".~.+randData$var1")
     return(model2)
}