从封闭环境访问数据的函数

  • 本文关键字:数据 函数 访问 环境 r
  • 更新时间 :
  • 英文 :


我正在处理分段包,在函数内调用davies.test()时遇到问题。

考虑以下情况:

library(segmented)
data = data.frame(x = 1:21, y = c(10:1, 0:10))
fit = lm(y ~ x, data = data)
fit.seg = segmented(fit, seg.Z = ~ x)
davies.test(fit.seg, seg.Z = ~ x, alternative = "greater")

这非常有效,并表明分段回归具有两个统计上不同的斜率。

现在,如果我把所有这些打包成一个这样的函数:

testit <- function() {
  data = data.frame(x = 1:21, y = c(10:1, 0:10))
  fit = lm(y ~ x, data)
  fit.seg = segmented(fit, seg.Z = ~ x)
  davies.test(fit.seg, seg.Z = ~ x, alternative = "greater")$p.value
}
testit()

然后它工作得很好。。。

但是,如果我从全局环境中删除fit,那么它就会失败。

> rm(fit)
> testit()
 Error in eval(expr, envir, enclos) : object 'fit' not found

问题似乎出在davies.test试图访问fit中封装的数据的方式上:它似乎没有在封闭作用域(在本例中是testit函数)中查找fit,而是直接跳到全局作用域。

我确信这个问题与R的作用域规则的一些微妙之处有关。如果我能找到一个快速的解决方案,防止我用这个边缘案例困扰包作者,那就太好了。

谢谢,安德鲁。

尝试插入下面标记为##的行。仍然有一个差异,这没有考虑到,正如运行修改后的testit时出现的警告所示,但输出pvalue是相同的,因此它可能足以满足您的需求。当然,这是软件包中的一个错误,最好是询问软件包的维护人员是否愿意修复它

library(segmented)
testit <- function() {
  data = data.frame(x = 1:21, y = c(10:1, 0:10))
  fit = lm(y ~ x, data)
  fit.seg = segmented(fit, seg.Z = ~ x)
  environment(davies.test) <- environment() ##
  davies.test(fit.seg, seg.Z = ~ x, alternative = "greater")$p.value
}
testit()

给予:

[1] 0.01858149
Warning message:
In summary.lm(object) : essentially perfect fit: summary may be unreliable

无需将其设为全局变量。问题实际上出在segmented,而不是davies.test。它找不到fit

您可以使用dynGet在任何环境中定位fit,包括调用函数的环境:

testit <- function() {
  data = data.frame(x = 1:21, y = c(10:1, 0:10))
  fit = lm(y ~ x, data)
  fit.seg = segmented(dynGet("fit"), seg.Z = ~ x)
  davies.test(fit.seg, seg.Z = ~ x, alternative = "greater")$p.value
}
testit()

这应该如你所愿。

如果在不同的环境中有多个名为fit的变量,请使用get(请参见?get)指定要从哪个环境中获取该变量。dynGet是"到处看,先返回"的懒惰版本。

我联系了segmented的作者,他立即做出了回应。他对最初的问题提出的另一个解决方案是

testit <- function() {
  data = data.frame(x = 1:21, y = c(10:1, 0:10))
  fit = lm(y ~ x, data)
  fit.seg = segmented(fit, seg.Z = ~ x)
  fit.seg$call$obj<-fit
  davies.test(fit.seg, seg.Z = ~ x, alternative = "greater")$p.value
}

然而,他也指出lm对象实际上应该直接传递给davies.test(),如下所示:

testit <- function() {
  data = data.frame(x = 1:21, y = c(10:1, 0:10))
  fit = lm(y ~ x, data)
  davies.test(fit, seg.Z = ~ x, alternative = "greater")$p.value
}

不过,为了澄清起见,应该注意的是,这两位代码做了不同的事情:第二个片段实际上实现了我最初的目的(检查拟合中是否存在统计上显著的中断),而第一个片段则检查是否存在第二个中断。

最新更新