为什么我在将 "lm" 模型传递给 effectsize::effectsize() 时收到有关 stats::model.frame() 的错误?

Why am I getting an error about stats::model.frame() when passing an "lm" model to effectsize::effectsize()?

我在构建包装 lm() 的函数时遇到了一个奇怪的情况。具体来说,在将函数的模型输出传递给 effectsize::effectsize().

时,我收到一个与 stats::model.frame() 有关的错误

在下面的示例中有两种情况,AB。在 A 中,我定义了一个函数,它首先构建一个 formula 对象 my_formula <- as.formula(paste0(y, "~", x)),然后将其传递给 lm()。此函数 returns class "lm" 的一个对象。当我将该对象传递给 effectsize::effectsize() 时,出现错误:

Error in stats::model.frame(formula = my_formula, data = data_std, drop.unused.levels = TRUE) : object 'my_formula' not found

奇怪的是,当我将同一个对象传递给 stats::model.frame() 时,它起作用了。

在场景 B 中,我构建了一个函数,其中公式在 lm() 内指定,而不是抢占式对象。在那种情况下,将输出传递给 effectsize() 是可行的。

可重现的例子

library(dplyr, warn.conflicts = FALSE)
library(effectsize)

## scenario A -- failing
my_lm_external_formula <- function(.dat, predicted, predictor){
  
  my_formula <- as.formula(paste0(predicted, "~", predictor))
  
  lm(formula = my_formula, data = .dat)
}
## the following line fails
my_lm_external_formula(.dat = mtcars, predicted = "mpg", predictor =  "am") %>% effectsize()
#> Error in stats::model.frame(formula = my_formula, data = data_std, drop.unused.levels = TRUE): object 'my_formula' not found
## although this one works
my_lm_external_formula(.dat = mtcars, predicted = "mpg", predictor =  "am") %>% stats::model.frame()
#>                      mpg am
#> Mazda RX4           21.0  1
#> Mazda RX4 Wag       21.0  1
#> Datsun 710          22.8  1
#> Hornet 4 Drive      21.4  0
#> Hornet Sportabout   18.7  0
#> Valiant             18.1  0
#> Duster 360          14.3  0
#> Merc 240D           24.4  0
#> Merc 230            22.8  0
#> Merc 280            19.2  0
#> Merc 280C           17.8  0
#> Merc 450SE          16.4  0
#> Merc 450SL          17.3  0
#> Merc 450SLC         15.2  0
#> Cadillac Fleetwood  10.4  0
#> Lincoln Continental 10.4  0
#> Chrysler Imperial   14.7  0
#> Fiat 128            32.4  1
#> Honda Civic         30.4  1
#> Toyota Corolla      33.9  1
#> Toyota Corona       21.5  0
#> Dodge Challenger    15.5  0
#> AMC Javelin         15.2  0
#> Camaro Z28          13.3  0
#> Pontiac Firebird    19.2  0
#> Fiat X1-9           27.3  1
#> Porsche 914-2       26.0  1
#> Lotus Europa        30.4  1
#> Ford Pantera L      15.8  1
#> Ferrari Dino        19.7  1
#> Maserati Bora       15.0  1
#> Volvo 142E          21.4  1

####
####
####

# scenario B -- working
my_lm_built_in_formula_via_pipe <- function(.dat, predicted, predictor){
  
  .dat %>%
    select(my_predicted = {{ predicted }}, my_predictor = {{ predictor }}) %>%
    lm(my_predicted ~ my_predictor, data = .)
}
## both calls work:
my_lm_built_in_formula_via_pipe(.dat = mtcars, predicted = "mpg", predictor = "am") %>% effectsize()
#> # Standardization method: refit
#> 
#> Parameter    | Coefficient (std.) |        95% CI
#> -------------------------------------------------
#> (Intercept)  |           2.94e-17 | [-0.29, 0.29]
#> my_predictor |               0.60 | [ 0.30, 0.90]
my_lm_built_in_formula_via_pipe(.dat = mtcars, predicted = "mpg", predictor = "am") %>% stats::model.frame()
#>                     my_predicted my_predictor
#> Mazda RX4                   21.0            1
#> Mazda RX4 Wag               21.0            1
#> Datsun 710                  22.8            1
#> Hornet 4 Drive              21.4            0
#> Hornet Sportabout           18.7            0
#> Valiant                     18.1            0
#> Duster 360                  14.3            0
#> Merc 240D                   24.4            0
#> Merc 230                    22.8            0
#> Merc 280                    19.2            0
#> Merc 280C                   17.8            0
#> Merc 450SE                  16.4            0
#> Merc 450SL                  17.3            0
#> Merc 450SLC                 15.2            0
#> Cadillac Fleetwood          10.4            0
#> Lincoln Continental         10.4            0
#> Chrysler Imperial           14.7            0
#> Fiat 128                    32.4            1
#> Honda Civic                 30.4            1
#> Toyota Corolla              33.9            1
#> Toyota Corona               21.5            0
#> Dodge Challenger            15.5            0
#> AMC Javelin                 15.2            0
#> Camaro Z28                  13.3            0
#> Pontiac Firebird            19.2            0
#> Fiat X1-9                   27.3            1
#> Porsche 914-2               26.0            1
#> Lotus Europa                30.4            1
#> Ford Pantera L              15.8            1
#> Ferrari Dino                19.7            1
#> Maserati Bora               15.0            1
#> Volvo 142E                  21.4            1

reprex package (v2.0.0)

于 2021-08-15 创建

知道为什么我在场景 A 中出现错误(但在场景 B 中没有),以及为什么 effectsize() 失败而 stats::model.frame() 没有?

当您将 lm 对象传递给 effectsize() 时,它会在您当前的 R 环境中重新评估调用,而不是在创建公式变量的环境中,因此它会抛出错误,因为它找不到 my_formula

对于stats::model.frame,因为你已经传递了lm对象,它只是拉出模型矩阵,不需要评估,你可以尝试传递它的公式:

x_formula <- function(.dat, predicted, predictor){
  
  my_formula <- as.formula(paste0(predicted, "~", predictor))
  my_formula
}

x_formula(.dat = mtcars,"am","gear") %>% stats::model.frame(data=mtcars)
Error in x_formula(.dat = mtcars, "am", "gear") : 
  could not find function "x_formula"

你可以看到它也失败了。所以这是一个错误的比较。

您可以强制计算调用,参见 this book chapter,但我不太确定这样做是否正确:

my_lm_external_formula <- function(.dat, predicted, predictor){
  
  f = reformulate(response=predictor,termlabels=predicted)
  fit = lm(f, data = .dat)
  fit$call$formula = eval(f)
  fit

}

my_lm_external_formula(.dat = mtcars, predicted = "mpg", predictor =  "am") %>% 
effectsize()

# Standardization method: refit

Parameter   | Coefficient (std.) |        95% CI
------------------------------------------------
(Intercept) |          -1.77e-17 | [-0.29, 0.29]
mpg         |               0.60 | [ 0.30, 0.90]

您经常 运行 涉及 lm 的范围问题和 lm 对象的方法。我在使用该语言进行计算方面有很好的经验,但我不能保证您永远不会 运行 遇到问题。那里有一些方法不适用于尚未在与其调用相同的环境中创建的 lm 对象,一些不好的方法甚至需要在全局环境中创建它。

my_lm_external_formula <- function(.dat, predicted, predictor){
  
  predicted <- as.name(predicted)
  predictor <- as.name(predictor)
  
  eval(bquote(lm(formula = .(predicted) ~ .(predictor), data = .dat)))
}  

effectsize(
  my_lm_external_formula(.dat = mtcars, 
                         predicted = "mpg", 
                         predictor =  "am")
)
#works