如何使用 R 中的 purrr::map 函数从多个 tidymodels 对象中提取模型摘要?

How can I extract model summary from multiple tidymodels objects using purrr::map functions in R?

我想使用 purrr::map_* 函数从涉及线性回归方法的多个模型中提取信息。我首先创建一些随机数据集。该数据集具有三个因变量和一个自变量。

set.seed(123)
library(olsrr)
#;-) 
#;-) Attaching package: 'olsrr'
#;-) The following object is masked from 'package:datasets':
#;-) 
#;-)     rivers
y1 <- rnorm(100) + runif(100)
y2 <- rnorm(100) + runif(100)
y3 <- rnorm(100) + runif(100)
x <- rnorm(100)
df1 <- data.frame(y1, y2, y3, x)

创建数据后,现在我正在创建三个模型。这一次,我使用基本函数 lm() 来创建模型。我正在使用 lm() 函数来证明它可以在不使用 tidymodels 框架的情况下工作。

m1 <- lm(y1 ~ x, data = df1)
m2 <- lm(y2 ~ x, data = df1)
m3 <- lm(y3 ~ x, data = df1)

现在我尝试通过 purrr::map 函数获取这些模型的摘要。我先创建一个模型列表,然后使用映射函数。

list_of_models <- list(m1, m2, m3)
model_summaries <- purrr::map(list_of_models, summary)
model_normality <- purrr::map(list_of_models, olsrr::ols_test_normality)
model_normality
#;-) [[1]]
#;-) -----------------------------------------------
#;-)        Test             Statistic       pvalue  
#;-) -----------------------------------------------
#;-) Shapiro-Wilk              0.9872         0.4517 
#;-) Kolmogorov-Smirnov        0.0865         0.4423 
#;-) Cramer-von Mises          8.9238         0.0000 
#;-) Anderson-Darling          0.5807         0.1276 
#;-) -----------------------------------------------
#;-) 
#;-) [[2]]
#;-) -----------------------------------------------
#;-)        Test             Statistic       pvalue  
#;-) -----------------------------------------------
#;-) Shapiro-Wilk              0.9499          8e-04 
#;-) Kolmogorov-Smirnov        0.1252         0.0869 
#;-) Cramer-von Mises          9.115          0.0000 
#;-) Anderson-Darling          1.6102          4e-04 
#;-) -----------------------------------------------
#;-) 
#;-) [[3]]
#;-) -----------------------------------------------
#;-)        Test             Statistic       pvalue  
#;-) -----------------------------------------------
#;-) Shapiro-Wilk               0.99          0.6685 
#;-) Kolmogorov-Smirnov        0.0551         0.9213 
#;-) Cramer-von Mises          5.8198         0.0000 
#;-) Anderson-Darling          0.3632         0.4347 
#;-) -----------------------------------------------

因此,我可以看到可以使用 map 函数来获取模型摘要,以及使用基函数 lm() 时的残差正态性测试。但我想使用 tidymodels 框架进行分析。

library(tidymodels)
#;-) Registered S3 method overwritten by 'tune':
#;-)   method                   from   
#;-)   required_pkgs.model_spec parsnip
#;-) -- Attaching packages --------------------------------- tidymodels 0.1.4.9000 --
#;-) v broom        0.7.11     v recipes      0.1.17
#;-) v dials        0.0.10     v rsample      0.1.1 
#;-) v dplyr        1.0.7      v tibble       3.1.6 
#;-) v ggplot2      3.3.5      v tidyr        1.1.4 
#;-) v infer        1.0.0      v tune         0.1.6 
#;-) v modeldata    0.1.1      v workflows    0.2.4 
#;-) v parsnip      0.1.7      v workflowsets 0.1.0 
#;-) v purrr        0.3.4      v yardstick    0.0.9
#;-) -- Conflicts ----------------------------------------- tidymodels_conflicts() --
#;-) x purrr::discard() masks scales::discard()
#;-) x dplyr::filter()  masks stats::filter()
#;-) x dplyr::lag()     masks stats::lag()
#;-) x recipes::step()  masks stats::step()
#;-) * Dig deeper into tidy modeling with R at https://www.tmwr.org
lm_spec <- linear_reg()
lm_fit1 <- fit(lm_spec, y1 ~ x, data = df1)
lm_fit2 <- fit(lm_spec, y2 ~ x, data = df1)
lm_fit3 <- fit(lm_spec, y3 ~ x, data = df1)
list_tidymodels <- c(lm_fit1, lm_fit2, lm_fit3)
get_lm_coefs <- function(x) {
  x %>%
    # get the lm model object
    extract_fit_engine() %>%
    # transform its format
    tidy()
}
map(list_tidymodels, get_lm_coefs)
#;-) Error in UseMethod("extract_fit_engine"): no applicable method for 'extract_fit_engine' applied to an object of class "NULL"

以上命令报错。现在终于,我试图在不使用 map 函数的情况下单独查找每个模型的摘要。

lm_fit1 %>%
  extract_fit_engine() %>%
  summary()
#;-) 
#;-) Call:
#;-) stats::lm(formula = y1 ~ x, data = data)
#;-) 
#;-) Residuals:
#;-)      Min       1Q   Median       3Q      Max 
#;-) -2.90637 -0.57659  0.02682  0.46190  2.45272 
#;-) 
#;-) Coefficients:
#;-)             Estimate Std. Error t value Pr(>|t|)    
#;-) (Intercept)  0.59226    0.09883   5.993 3.44e-08 ***
#;-) x           -0.10387    0.10697  -0.971    0.334    
#;-) ---
#;-) Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#;-) 
#;-) Residual standard error: 0.9748 on 98 degrees of freedom
#;-) Multiple R-squared:  0.00953,  Adjusted R-squared:  -0.0005771 
#;-) F-statistic: 0.9429 on 1 and 98 DF,  p-value: 0.3339
lm_fit1 %>%
  extract_fit_engine() %>%
  olsrr::ols_test_normality()
#;-) -----------------------------------------------
#;-)        Test             Statistic       pvalue  
#;-) -----------------------------------------------
#;-) Shapiro-Wilk              0.9872         0.4517 
#;-) Kolmogorov-Smirnov        0.0865         0.4423 
#;-) Cramer-von Mises          8.9238         0.0000 
#;-) Anderson-Darling          0.5807         0.1276 
#;-) -----------------------------------------------

所以,很明显只有当我将 tidymodels 与 map 函数结合使用时才会出现错误。我在这里做错了什么?

需要使用 list() 而不是 c() 创建 list_tidymodels

set.seed(123)

library(olsrr)
library(tidymodels)

y1 <- rnorm(100) + runif(100)
y2 <- rnorm(100) + runif(100)
y3 <- rnorm(100) + runif(100)
x <- rnorm(100)
df1 <- data.frame(y1, y2, y3, x)

lm_spec <- linear_reg()
lm_fit1 <- fit(lm_spec, y1 ~ x, data = df1)
lm_fit2 <- fit(lm_spec, y2 ~ x, data = df1)
lm_fit3 <- fit(lm_spec, y3 ~ x, data = df1)

list_tidymodels <- list(lm_fit1, lm_fit2, lm_fit3) # this line used `c(lm_fit1, ...)`.

get_lm_coefs <- function(x) {
  x %>%
    # get the lm model object
    extract_fit_engine() %>%
    # transform its format
    tidy()
}

map(list_tidymodels, get_lm_coefs)

#> [[1]]
#> # A tibble: 2 x 5
#>   term        estimate std.error statistic      p.value
#>   <chr>          <dbl>     <dbl>     <dbl>        <dbl>
#> 1 (Intercept)    0.592    0.0988     5.99  0.0000000344
#> 2 x             -0.104    0.107     -0.971 0.334       
#> 
#> [[2]]
#> # A tibble: 2 x 5
#>   term        estimate std.error statistic    p.value
#>   <chr>          <dbl>     <dbl>     <dbl>      <dbl>
#> 1 (Intercept)   0.523      0.101     5.16  0.00000129
#> 2 x             0.0705     0.110     0.642 0.522     
#> 
#> [[3]]
#> # A tibble: 2 x 5
#>   term        estimate std.error statistic  p.value
#>   <chr>          <dbl>     <dbl>     <dbl>    <dbl>
#> 1 (Intercept)   0.443      0.114     3.87  0.000197
#> 2 x             0.0437     0.124     0.353 0.725

reprex package (v2.0.1)

于 2022 年 1 月 20 日创建