代码之家  ›  专栏  ›  技术社区  ›  Adrian Keister

对于R,在MASS::boxcox函数中,我经常错误地得到可怕的“‘data’必须是data.frame、environment或list”错误

  •  0
  • Adrian Keister  · 技术社区  · 8 月前

    我在2023年11月14英寸的MacBook Pro上运行R 4.4.1和MASS 7.3-61,它有MacOS 14.6。

    以下是一些可复制的MWE代码:

    require(MASS)
    
    set.seed(42)
    x = rnorm(5)
    y = rnorm(5)
    
    df = data.frame(x, y)
    
    lmod = lm(y~x, data=df)
    boxcox(lmod)
    

    这会产生错误:

    Error in model.frame.default(formula = y ~ x, data = df, drop.unused.levels = TRUE) : 
      'data' must be a data.frame, environment, or list
    

    变量 df 显然是一个数据帧,因此此错误消息完全错误:

    > class(df)
    [1] "data.frame"
    > is.data.frame(df)
    [1] TRUE
    

    我显然正确地指定了模型,所以原因并不相关。如果我尝试 traceback() 函数,它产生以下结果:

    16: stop("'data' must be a data.frame, environment, or list")
    15: model.frame.default(formula = y ~ x + cat, data = df, drop.unused.levels = TRUE)
    14: stats::model.frame(formula = y ~ x + cat, data = df, drop.unused.levels = TRUE)
    13: eval(mf, parent.frame())
    12: eval(mf, parent.frame())
    11: lm(formula = y ~ x + cat, data = df, y = TRUE, qr = TRUE)
    10: eval(call, parent.frame())
    9: eval(call, parent.frame())
    8: update.default(object, y = TRUE, qr = TRUE, ...)
    7: update(object, y = TRUE, qr = TRUE, ...)
    6: boxcox.lm(lmod, plotit = TRUE)
    5: boxcox(lmod, plotit = TRUE) at test_boxcox.R#20
    4: eval(ei, envir)
    3: eval(ei, envir)
    2: withVisible(eval(ei, envir))
    1: source("~/Projects/non_repo_data/test_boxcox.R")
    

    但通过 stats::model.frame.default 函数的源代码没有显示这一点 stop 指挥任何地方。我完全不明白为什么会发生这种情况,甚至不知道错误是从哪里产生的。当然感觉像一个bug。

    1 回复  |  直到 8 月前
        1
  •  2
  •   Ben Bolker    8 月前

    tl;博士 你必须给数据帧命名,而不是 df ,这样它就不会与内置的R对象碰撞。

    错误本身来自第526行 src/library/stats/R/models.R .

    这可以说是一个bug,或者至少是一个“不恰当”( sensu 比尔·维纳布尔斯),在 MASS::boxcox ,但这也说明了为什么避免变量和内置对象之间的名称重叠是件好事。(我提交了一份 bug report .)

    继续你的例子:

    dff <- df  ## rename your data frame
    lmod <- lm(y~x, data=dff)
    boxcox(lmod)
    

    boxcox.default(lmod)中出错:响应变量必须为正

    发生此错误是因为您构造了一个稍微不合适的示例(这对于显示您想要的内容很好)。

    lmod <- lm(abs(y)~x, data=dff)
    boxcox(lmod)  ## works
    

    我们可以通过查看以下输出来了解正在发生的事情 traceback() :

    12: stop("'data' must be a data.frame, environment, or list")
    11: model.frame.default(formula = y ~ x, data = df, drop.unused.levels = TRUE)
    10: stats::model.frame(formula = y ~ x, data = df, drop.unused.levels = TRUE)
    9: eval(mf, parent.frame())
    8: eval(mf, parent.frame())
    7: lm(formula = y ~ x, data = df, y = TRUE, qr = TRUE)
    6: eval(call, parent.frame())
    5: eval(call, parent.frame())
    4: update.default(object, y = TRUE, qr = TRUE, ...)
    3: update(object, y = TRUE, qr = TRUE, ...)
    2: boxcox.lm(lmod)
    1: boxcox(lmod)
    
    • boxcox 正在呼叫 update() 确保拟合的模型具有所需的所有组件(特别是存储的QR分解)
    • update() 正在重新呼叫 lm()
    • lm() 正在呼叫 model.frame()
    • 当我们到达那里时, model.frame() 正在一个可以看到内置功能的环境中进行评估 df (在 stats 在它看到您的数据帧之前(在 .GlobalEnv ).

    只需要比我现在想做的多做一点工作就可以建立 确切地 那些都是什么 parent.frame() 调用正在看到。从内部 lm() 呼叫(您可以通过设置到达那里 options(error = recover) 你可以看到 父框架的封闭环境 parent.frame()$enclos <environment:base> 我不太确定我们怎么从那里走到 <environment: namespace:stats> ,这就是我们要做的 df 从。。。