从封闭环境

时间:2016-01-04 12:53:45

标签: 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中封装的数据的方式有关:它似乎并未在封闭范围内查找fit(在这种情况下是testit函数),但直接跳到全局范围。

我确信这个问题与R的范围规则有些微妙关系。如果我能找到一个快速解决方案,可以防止我使用这个边缘案例麻烦包装作者,那就太棒了。

谢谢, 安德鲁。

3 个答案:

答案 0 :(得分:4)

尝试插入下面标有##的行。如果运行修改后的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

答案 1 :(得分:3)

无需将其变为全局变量。问题实际上在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是“到处寻找;先回归”的懒惰版本。

答案 2 :(得分:1)

我联系了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
}

为了澄清,应该注意的是,这两行代码做了不同的事情:第二个片段实际上满足了我的原始目的(检查拟合中的统计上显着的中断),而第一个片段检查是否存在是第二次休息。