【R言語】欠損値補完パッケージ「simputation」が使いやすそう、という話
「Rで欠損値埋めるのにいいパッケージないかな〜」と思って色々探してみたら、「simputation」という良さそうなパッケージを見つけました。
参考文献
紹介スライド
そもそもこのパッケージの事を知ったのは、下記のスライドがきっかけです。「こんな感じでコードがかけたら便利だなあ」みたいなものが的確に実装されている事がわかりました。
www.slideshare.net使い方
当然英語ではあるものの製作者(?)から提供されている具体例も非常にシンプルでわかりやすく、基本的な機能の使い方はこれで身につけられると思います。かく言う僕もまだこれを読んだ程度の理解です。
https://cran.r-project.org/web/packages/simputation/vignettes/intro.html
Rのhelp
最後に、いつものドキュメント。
https://cran.r-project.org/web/packages/simputation/simputation.pdf
{simputation}の何がいいのか
基本的な使い方は参考文献に譲るとして、ここではどのあたりが使いやすいのかを僕視点で解説します。
なお、この記事では例を紹介する際に、irisデータを以下の操作で加工したものを利用します。上記使い方でも利用しているものです。
dat <- iris dat[1:3,1] <- dat[3:7,2] <- dat[8:10,5] <- NA head(dat,10)
中身はこんな感じです:
## Sepal.Length Sepal.Width Petal.Length Petal.Width Species ## 1 NA 3.5 1.4 0.2 setosa ## 2 NA 3.0 1.4 0.2 setosa ## 3 NA NA 1.3 0.2 setosa ## 4 4.6 NA 1.5 0.2 setosa ## 5 5.0 NA 1.4 0.2 setosa ## 6 5.4 NA 1.7 0.4 setosa ## 7 4.6 NA 1.4 0.3 setosa ## 8 5.0 3.4 1.5 0.2 <NA> ## 9 4.4 2.9 1.4 0.2 <NA> ## 10 4.9 3.1 1.5 0.1 <NA>
書き方がシンプル
「simputation=simple + imputation」と言う意味らしいですが、その名前通り、非常にシンプルに記述する事ができます。
例えば、「Sepal.LengthをSepal.WidthとPetal.Lengthで回帰して補完する」と言うコードをRの組み込み関数のみで表現する場合、下記のようになるでしょう。
mod <- lm(Sepal.Length ~ Sepal.Width + Petal.Length, data = dat) pred <- predict(mod, newdata = dat) da1 <- dat da1$Sepal.Length <- ifelse(is.na(da1$Sepal.Length), pred, da1$Sepal.Length)
内容としては、
- Sepal.WidthとPetal.LengthでSepal.Lengthを回帰
- 予測値を算出
NA
が含まれるレコードだけ予測値に置き換え
といった感じです。
これを、{simputation}を使って書くと、一行で済みます。
da1 <- impute_lm(dat, Sepal.Length ~ Sepal.Width + Petal.Length)
「一行で書ければいいってものじゃない」と言うのはその通りですが、この一行をみて「Sepal.LengthをSepal.WidthとPetal.Lengthで回帰して補完する」という意図は即読み取れるのではないでしょうか。
{dplyr}と相性がいい
2点目は、dplyrとの相性の良さです。impute_lm
に代表されるような{simputation}のimpute用関数は、全て下記の形を取っています。
impute_hoge(dat, formula, ...) # dat : データ # formula : 補完式
重要なのは引数の順番です。dat
が一番最初にあります。つまり、パイプ演算子を使って連鎖的に書く事が可能です。
da4 <- dat %>% impute_lm(Sepal.Length ~ Sepal.Width + Species) %>% impute_median(Sepal.Length ~ Species) %>% impute_cart(Species ~ .)
impute関数が非常に便利
impute_lm
の他にも、{simputation}には様々な補完メソッドを実装されています。しかし、いくら豊富な補完メソッドがあるとはいえ、全てはカバーしきれないものです。
しかし、そんな時でも使える関数を{simputation}は用意してくれています。それがimpute
関数です。例えば、最初の例である「Sepal.LengthをSepal.WidthとPetal.Lengthで回帰して補完する」を、impute
関数を使う場合は以下のように表現します。
mod <- lm(Sepal.Length ~ Sepal.Width + Petal.Length, data = dat) da1 <- dat %>% impute(Sepal.Length ~ mod)
このコードの意味は、「Sepal.Lengthをmodを使ってimputeする」です。modのところに持ってくるmodelオブジェクトを変えれば、任意の方法で補完を行う事が可能です。
また、この関数は訓練データと検証データを区別している際にも役に立ちます。欠損値補完用のモデルを構築する際には当然訓練データを使いますが、検証用データにも同じモデルを適用する必要があります。その場合は下記のように書けば良いことになります。
mod <- lm(Sepal.Length ~ Sepal.Width + Petal.Length, data = X_train) X_test <- X_test %>% impute(Sepal.Length ~ mod)
モデルを保持しておく必要がある、という意味では手間は組み込み関数と同じですが、見た目非常にスッキリしていると思います。
まとめ
ここでは挙げていないですが、{simputation}にはまだまだ良いところがあります。詳細は使い方などを見てください。
- グループごとの欠損値補完
- 複数欠損値の同時補完
- {VIM}パッケージを使って高速化(?)
3つ目に関しては、「実際にサンプルコードをループさせたらむしろ遅かった」とか「tibbleに対して適用したら何故かエラーが出た(他のサンプルコードでは出ていない)」等、よくわからないところは多いのですが、それ以外に関しては非常に使いやすいと思っています。現状欠損値補完するならこれ一択ではないでしょうか。
というわけで、あまりにも使いやすかった{simputation}の紹介でした。