【问题标题】:Function accessing data from enclosing environment从封闭环境访问数据的函数
【发布时间】:2016-01-04 12:53:44
【问题描述】:

我正在使用分段包,但在从函数中调用 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(在这种情况下是@ 987654329@ 函数),但直接跳到全局范围。

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

谢谢, 安德鲁。

【问题讨论】:

    标签: r


    【解决方案1】:

    尝试在下面插入标记为## 的行。仍然存在一个差异,这没有考虑到运行修改后的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
    

    【讨论】:

    • 谢谢。该警告只是由于测试数据的构建方式所致。不是相关问题。
    【解决方案2】:

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

    【讨论】:

    • 问题在于davis.test 内部的评估。请参阅traceback 输出。
    • 调用函数的具体环境可以通过:segmented(get("fit", sys.frame(1)), seg.Z = ~ x)来访问,这样可以避免使用dynGet()时可能在别处发生命名空间冲突。
    【解决方案3】:

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

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

    【讨论】:

      猜你喜欢
      • 2019-06-09
      • 1970-01-01
      • 1970-01-01
      • 2022-01-24
      • 1970-01-01
      • 1970-01-01
      • 2012-04-28
      • 2012-10-28
      • 1970-01-01
      相关资源
      最近更新 更多