データを差し替えて再投稿.
リストワイズは「すべての質問に回答した人」で条件付けることになるので,何でもかんでもリストワイズするのは危険かもしれない.
そこで多重代入法(Multivariate Imputation by Chained Equations; MICE)を使って欠損値の補完をやってみる.
miceパッケージを利用する.
irisデータを使う.中身を見てみよう.
ついでに,今回使うパッケージを読み込んでおく.
pacman::p_load(tidyverse, mice, estimatr, modelsummary)
tibble(iris)
## # A tibble: 150 × 5
## Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## <dbl> <dbl> <dbl> <dbl> <fct>
## 1 5.1 3.5 1.4 0.2 setosa
## 2 4.9 3 1.4 0.2 setosa
## 3 4.7 3.2 1.3 0.2 setosa
## 4 4.6 3.1 1.5 0.2 setosa
## 5 5 3.6 1.4 0.2 setosa
## 6 5.4 3.9 1.7 0.4 setosa
## 7 4.6 3.4 1.4 0.3 setosa
## 8 5 3.4 1.5 0.2 setosa
## 9 4.4 2.9 1.4 0.2 setosa
## 10 4.9 3.1 1.5 0.1 setosa
## # ℹ 140 more rows
今回はSepal.Lengthをターゲットにしてやってみる.
Sepal.Lengthの値をランダムにNAに変換して,データフレームを作る.
NAの割合は,ひとまず20%としておこう.
data <- iris
set.seed(1231)
num_na <- floor(nrow(data) * 0.2)
na_indices <- sample(1:nrow(data), num_na)
data$Sepal.Length[na_indices] <- NA
NAの具合は以下の通り.
colSums(is.na(data))
## Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## 30 0 0 0 0
では実際に,NAを補完していく.シード値の固定を忘れずに.
また,サンプルサイズが大きいと計算に時間がかかり,最悪の場合Rが爆発してそれまでの苦労が吹っ飛んでしまうらしい.
そういう場合は小さめのデータでテストしたり,変数を減らして負荷を軽減するなどの工夫をしたりすることが肝要らしい.
今回は全然大丈夫.
mice(data, m = 5, maxit = 100, seed = 12345) -> data_mice
mは補完データセット数(今回は5個)で,maxitは各欠測に対する補完の回数(今回は100回)となっている.
補完された値が初期値の影響を受けていないか確認する.
plot(data_mice)
いい感じに鎖が混ざっている.こちらについては,特に問題なさそう.
念のため,補完後のデータの分布に問題がないか確認しておく.
densityplot(data_mice)
青線が元データで,赤線が補完後データ(5つ)の分布となっている.
あんまり良くなさげだが,まあよしとしよう.
補完後のデータを使って,Sepal.LengthについてOLSをしてみる.
with(data_mice, lm_robust(Sepal.Length ~ Sepal.Width + Petal.Length + Petal.Width + Species)) -> lm_mice
結果をみるときはこれ.ぶっちゃけ見にくい.
summary(pool(lm_mice))
## term estimate std.error statistic df p.value
## 1 (Intercept) 2.1141131 0.29750022 7.106257 49.54150 4.291445e-09
## 2 Sepal.Width 0.5241021 0.10144322 5.166458 31.70339 1.255149e-05
## 3 Petal.Length 0.8435038 0.09677312 8.716303 17.67296 8.190570e-08
## 4 Petal.Width -0.4631615 0.18064955 -2.563867 62.68414 1.276167e-02
## 5 Speciesversicolor -0.6222502 0.28296995 -2.198997 52.21001 3.233130e-02
## 6 Speciesvirginica -0.8479610 0.40675129 -2.084716 57.80613 4.152276e-02
上記のコードで結果を確認することはできるが,目がチカチカする.
そこでmsummaryを使って,無理矢理キレイにまとめてみようと思う(警告メッセージでmodelsummaryが文句を言ってくるが,コードは通るのでひとまず良しとする).
ついでに,元のirisデータを使ったOLSの結果も載せて,多重代入版と比較する.
lm_robust(Sepal.Length ~ Sepal.Width + Petal.Length + Petal.Width + Species, data = iris) -> lm_normal
summary <- list("普通のOLS" = lm_normal,
"多重代入のOLS" = lm_mice)
msummary(summary, stars = c("*" = .05, "**" = .01, "***" = .001),
notes = list('括弧内は標準誤差'))
| 普通のOLS | 多重代入のOLS | |
|---|---|---|
| * p < 0.05, ** p < 0.01, *** p < 0.001 | ||
| 括弧内は標準誤差 | ||
| (Intercept) | 2.171*** | 2.114*** |
| (0.249) | (0.298) | |
| Sepal.Width | 0.496*** | 0.524*** |
| (0.081) | (0.101) | |
| Petal.Length | 0.829*** | 0.844*** |
| (0.073) | (0.097) | |
| Petal.Width | -0.315 | -0.463* |
| (0.166) | (0.181) | |
| Speciesversicolor | -0.724** | -0.622* |
| (0.240) | (0.283) | |
| Speciesvirginica | -1.023** | -0.848* |
| (0.351) | (0.407) | |
| Num.Obs. | 150 | |
| R2 | 0.867 | |
| R2 Adj. | 0.863 | |
| AIC | 79.1 | |
| BIC | 100.2 | |
| RMSE | 0.30 | |
表示されていない箇所があるのはご愛嬌.
多重代入法と少し仲良くなれたかもしれない.