在 mgcv::gam() 包装器中传递偏移参数

问题描述

我围绕 mgcv::gam() 编写了一个包装函数,以将模型直接写入磁盘并做一些额外方便的事情。到目前为止,一切都很好,每个参数都被传递并且有效。除了,当我添加要传递给 mgcv::gam(offset = ) 的偏移参数时。下面是一些示例代码

require(mgcv)

gam1 = function(form,data,family,knots = NULL) {
  gam(formula = form,knots = knots,data = data,family = family)
}

gam1(Sepal.Length ~ s(Sepal.Width),data = iris,family = 'gaussian')

参数传递给 gam()(例如节、家庭)。作品。但是,如果我将 offset_ 添加到聚会中,它不会传递但会引发错误

gam2 = function(form,knots = NULL,offset_ = NULL) {
  gam(formula = form,family = family,offset = offset_)
}

gam2(Sepal.Length ~ s(Sepal.Width),family = 'gaussian',offset_ = NULL)

offset_ 未传递并引发此错误Error in eval(extras,env) : object 'offset_' not found。如果我改用 offset,它会抛出这个错误invalid type (closure) for variable '(offset)'

问:为什么我的包装器无法传递 offset_?我怎样才能让它运行?

错误发生在下面源代码片段的最后一行。但是我不太明白为什么。

mf$drop.unused.levels <- drop.unused.levels
mf[[1]] <- quote(stats::model.frame) ## as.name("model.frame")
pmf <- mf
mf <- eval(mf,parent.frame()) # the model frame Now contains all the data 

解决方法

问题在于范围界定。 gam() 首先在 formula 参数中查找 offsetdata 中的变量,然后在附加到 formula 的环境中查找。通常这就是创建 formula 的环境;在您的示例中,这将是全球环境。

例如,您应该能够通过将 offset_ 变量添加到 data 的本地副本来使事情工作

gam3 <- function(form,data,family,knots = NULL,offset_ = NULL) {
  if (is.null(offset_)) {
    gam(formula = form,data = data,family = family,offset = NULL)
  } else {
    data$offset_copy <- offset_
    gam(formula = form,offset = offset_copy)
  }
}

如果 data 有一个名为 offset_copy 的列,这将覆盖它,所以一定要使用一个不在 data 中的名称。

编辑添加:@GavinSimpson 建议修改他的答案中的公式,以避免 predict() 出现问题。我建议进行与他使用的不同的修改:不要解析公式和偏移量,只需直接修改公式即可。例如,

fun <- function(form,offset_ = NULL,...) {
    ## capture what was passed to offset_,unevaluated
    off <- substitute(offset_)
    if (!is.null(offset_)) { # need to add offset
        form[[3]] <- call("+",form[[3]],call("offset",off))
    }
    ## fit and return model
    gam(form,...)
}

函数 call() 创建了一个调用,因此 form[[3]] 行将公式的 RHS 替换为对旧 RHS 的 "+" 的未评估调用,以及对 {{1} 的调用}} 包含偏移量。

这样做而不是解析的优点是它应该正确处理异常情况,例如可能会分解为几行的极长公式或偏移量,或者环境很重要的公式,因为此版本保持环境不变。

,

@user2554330 解释了原因和解决方法。我想提出一种完全替代的方法。

通常最好在公式中包含偏移量。如果你不这样做,那么它会在 predict() 之类的东西中被忽略。

如果 offset_ 不是 NULL,我建议您在公式中添加偏移量。像这样的工作:

fun <- function(form,...) {
    ## capture what use passed to offset_,unevaluated
    off <- deparse(substitute(offset_))
    new_form <- form # copy as we may be modifying formula
    if (!is.null(offset_)) { # need to add offset
        form <- as.character(form) # coerce to character
        ## paste some bits of the formula back together
        new_form <- paste(form[2],form[1],form[3])
        ## add the offset
        off_form <- paste0("offset(",off,")")
        new_form <- paste(new_form,off_form,sep = " + ")
        ## coerce to formula
        new_form <- as.formula(new_form)
    }
    ## fit and return model
    gam(new_form,...)
}

这里正在使用

N <- 50
effort <- rep(5,N)
df <- data.frame(y = runif(N),x = runif(N),z = runif(N))
fun(y ~ s(x) + s(z),offset_ = log(effort),data = df)

返回:

> 
Family: gaussian 
Link function: identity 

Formula:
y ~ s(x) + s(z) + offset(log(effort))

Estimated degrees of freedom:
1 1  total = 3 

GCV score: 0.08046669```

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...