如何使用 R 语言生成所有自变量的组合进行回归并筛选自变量系数估计值均显著的模型?

最近有个小伙伴问到了这样的一个问题,他想对所有自变量的组合进行回归,然后从中筛选系数估计值均显著的。

为此我们使用 auto 数据集进行演示:

library(tidyverse)
library(leaps)
library(modelr)
haven::read_dta("auto.dta") -> auto

假如我们想考虑这些变量:headroom trunk length displacement 对 mpg 的影响:

auto %>%
select(mpg, headroom, trunk, length, displacement) -> df

df

#> # A tibble: 74 × 5
#> mpg headroom trunk length displacement
#> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 22 2.5 11 186 121
#> 2 17 3 11 173 258
#> 3 22 3 12 168 121
#> 4 20 4.5 16 196 196
#> 5 15 4 20 222 350
#> 6 18 4 21 218 231
#> 7 26 3 10 170 304
#> 8 20 2 16 200 196
#> 9 16 3.5 17 207 231
#> 10 19 3.5 13 200 231
#> # ℹ 64 more rows

leaps 包可以实现选择最优模型的操作,不过结果不是很符合我们的需求,但是 leaps 包的 leaps() 函数可以返回所有可能的自变量组合,这样使用即可:

library(leaps)
df %>%
select(headroom, trunk, length, displacement) %>%
as.matrix() -> xm

df %>%
select(mpg) %>%
pull() -> y

leaps(x = xm, y = y) -> leapsdf

leapsdf$which -> regm

head(regm)

#> 1 2 3 4
#> 1 FALSE FALSE TRUE FALSE
#> 1 FALSE FALSE FALSE TRUE
#> 1 FALSE TRUE FALSE FALSE
#> 1 TRUE FALSE FALSE FALSE
#> 2 FALSE FALSE TRUE TRUE
#> 2 FALSE TRUE TRUE FALSE

然后依次循环所有的 15 个模型:

regm %>%
as_tibble() %>%
set_names("headroom", "trunk", "length", "displacement") %>%
mutate(m = row_number()) %>%
mutate(lmfit = map(m, ~lm(y ~ xm[,regm[.x,]]))) -> regdf
#> # A tibble: 6 × 11
#> m headroom trunk length displacement term estimate std.error statistic
#> <int> <lgl> <lgl> <lgl> <lgl> <chr> <dbl> <dbl> <dbl>
#> 1 1 FALSE FALSE TRUE FALSE xm[, re… -0.207 0.0185 -11.2
#> 2 2 FALSE FALSE FALSE TRUE xm[, re… -0.0445 0.00526 -8.45
#> 3 3 FALSE TRUE FALSE FALSE xm[, re… -0.787 0.130 -6.07
#> 4 4 TRUE FALSE FALSE FALSE xm[, re… -2.83 0.734 -3.86
#> 5 8 FALSE TRUE FALSE TRUE xm[, re… -0.327 0.138 -2.37
#> 6 8 FALSE TRUE FALSE TRUE xm[, re… -0.0352 0.00643 -5.47
#> # ℹ 2 more variables: p.value <dbl>, pvalmax <dbl>

broom 包的 tidy() 函数可以自动从模型中提取整洁的估计结果:

regdf %>%
mutate(lmfit = map(lmfit, broom::tidy)) %>%
unnest(lmfit) %>%
filter(term != "(Intercept)") %>%
group_by(m) %>%
# 计算最大的 p 值
mutate(pvalmax = max(p.value)) %>%
ungroup() %>%
# 筛选 p 值均小于 0.05 的模型
filter(pvalmax <= 0.05) %>%
select(m, everything())

#> # A tibble: 6 × 11
#> m headroom trunk length displacement term estimate std.error statistic
#> <int> <lgl> <lgl> <lgl> <lgl> <chr> <dbl> <dbl> <dbl>
#> 1 1 FALSE FALSE TRUE FALSE xm[, re… -0.207 0.0185 -11.2
#> 2 2 FALSE FALSE FALSE TRUE xm[, re… -0.0445 0.00526 -8.45
#> 3 3 FALSE TRUE FALSE FALSE xm[, re… -0.787 0.130 -6.07
#> 4 4 TRUE FALSE FALSE FALSE xm[, re… -2.83 0.734 -3.86
#> 5 8 FALSE TRUE FALSE TRUE xm[, re… -0.327 0.138 -2.37
#> 6 8 FALSE TRUE FALSE TRUE xm[, re… -0.0352 0.00643 -5.47
#> # ℹ 2 more variables: p.value <dbl>, pvalmax <dbl>

这样我们就得到了我们想要的结果。

变量多一些也不是问题。首先我们生成一些新的随机变量:

df %>%
mutate_at(c("headroom", "trunk", "length", "displacement"),
funs(`2` = . * runif(n = 74))) %>%
mutate_at(c("headroom", "trunk", "length", "displacement"),
funs(`3` = . * runif(n = 74))) -> df2
df2

#> # A tibble: 74 × 13
#> mpg headroom trunk length displacement headroom_2 trunk_2 length_2
#> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 22 2.5 11 186 121 1.54 2.07 178.
#> 2 17 3 11 173 258 1.35 4.11 152.
#> 3 22 3 12 168 121 2.25 10.2 1.19
#> 4 20 4.5 16 196 196 2.72 11.8 110.
#> 5 15 4 20 222 350 0.650 1.31 101.
#> 6 18 4 21 218 231 2.28 19.8 44.5
#> 7 26 3 10 170 304 2.51 5.08 20.9
#> 8 20 2 16 200 196 1.42 7.27 36.6
#> 9 16 3.5 17 207 231 3.44 0.790 128.
#> 10 19 3.5 13 200 231 2.00 7.18 150.
#> # ℹ 64 more rows
#> # ℹ 5 more variables: displacement_2 <dbl>, headroom_3 <dbl>, trunk_3 <dbl>,
#> # length_3 <dbl>, displacement_3 <dbl>

现在总共有这么些变量:

df2 %>%
colnames() %>%
dput()
#> c("mpg", "headroom", "trunk", "length", "displacement", "headroom_2",
#> "trunk_2", "length_2", "displacement_2", "headroom_3", "trunk_3",
#> "length_3", "displacement_3")

为了不至于组合过多,选择这些变量:”mpg”, “headroom”, “trunk”, “length”, “displacement”, “headroom_2”, “trunk_2”, “length_2”, “displacement_2”, “headroom_3”:

df2 %>%
select("mpg", "headroom", "trunk", "length",
"displacement", "headroom_2",
"trunk_2", "length_2",
"displacement_2", "headroom_3") -> df2

获取所有的组合:

df2 %>%
select("headroom", "trunk", "length",
"displacement", "headroom_2",
"trunk_2", "length_2",
"displacement_2", "headroom_3") %>%
as.matrix() -> xm

df2 %>%
select(mpg) %>%
pull() -> y

leaps(x = xm, y = y) -> leapsdf

leapsdf$which -> regm

head(regm)

#> 1 2 3 4 5 6 7 8 9
#> 1 FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE
#> 1 FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE
#> 1 FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> 1 FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE
#> 1 TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> 1 FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE

循环回归所有的组合并保留符合要求的:

# 循环
regm %>%
as_tibble() %>%
set_names("headroom", "trunk", "length",
"displacement", "headroom_2",
"trunk_2", "length_2",
"displacement_2", "headroom_3") %>%
mutate(m = row_number()) %>%
mutate(lmfit = map(m, ~lm(y ~ xm[,regm[.x,]]))) -> regdf

library(broom)

regdf %>%
mutate(lmfit = map(lmfit, broom::tidy)) %>%
unnest(lmfit) %>%
filter(term != "(Intercept)") %>%
group_by(m) %>%
mutate(pvalmax = max(p.value)) %>%
ungroup() %>%
filter(pvalmax <= 0.05) %>%
select(m, everything())

#> # A tibble: 8 × 16
#> m headroom trunk length displacement headroom_2 trunk_2 length_2
#> <int> <lgl> <lgl> <lgl> <lgl> <lgl> <lgl> <lgl>
#> 1 1 FALSE FALSE TRUE FALSE FALSE FALSE FALSE
#> 2 2 FALSE FALSE FALSE TRUE FALSE FALSE FALSE
#> 3 3 FALSE TRUE FALSE FALSE FALSE FALSE FALSE
#> 4 4 FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> 5 5 TRUE FALSE FALSE FALSE FALSE FALSE FALSE
#> 6 6 FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> 7 18 FALSE TRUE FALSE TRUE FALSE FALSE FALSE
#> 8 18 FALSE TRUE FALSE TRUE FALSE FALSE FALSE
#> # ℹ 8 more variables: displacement_2 <lgl>, headroom_3 <lgl>, term <chr>,
#> # estimate <dbl>, std.error <dbl>, statistic <dbl>, p.value <dbl>,
#> # pvalmax <dbl>

这样我们就解决了这个问题。

点击这里跳转到 RStata 短书平台获取附件:如何使用 R 语言生成所有自变量的组合进行回归并筛选自变量系数估计值均显著的模型?

评论