zooパッケージを使って行列の欠損値を補間する(R Advent Calendar 2011)

R Advent Calendar 2011 : ATNDに参加中でして、その担当分の記事です。

さて掲題の話ですが、たまに(しょっちゅう?)歯抜けになってる値を含む行列やデータを捌かなきゃいけないことがありまして、その際にはよくRの市販本にあるように

x[is.na(x)] <- 0

と欠損値の箇所をある”特定の値”で置いてしまうことが多かったのですが、
もうちょっと行列(あるいはデータフレーム)の特徴を活かした値の補間ができないものかと考えていた所、
普通は時系列処理に使う”zooパッケージ”に入ってるna.locf等の補間系関数を使えばもっと色々とできるじゃないかと思いつきやってみたという内容です。

何はともあれまずはパッケージのインストール&読み込み

install.packages("zoo")
library(zoo)

ここではサンプルコードとしてちょうど真ん中の要素に欠損値を持つ行列を用意。

> x <- matrix(c(1:4,NA,6:9), nrow = 3, byrow = TRUE)
> x
     [,1] [,2] [,3]
[1,]    1    2    3
[2,]    4   NA    6
[3,]    7    8    9

この行列の”NA”を、周辺の値を使ってうまく補間してやる方法を以下にご紹介します。

最近接値で補間

この例として用意した行列の欠損値を最近接値((四方にある値のどれか。この場合2・4・6・8))で置き換えるにはna.locfを使います。
2・4・6・8それぞれに置き換えるには行列の転置とfromLastオプションを組み合わせて以下のように書けばそれぞれに補間できます。

2で補間する場合
> na.locf(x)
     [,1] [,2] [,3]
[1,]    1    2    3
[2,]    4    2    6
[3,]    7    8    9
4で補間する場合
> t(na.locf(t(x)))
     [,1] [,2] [,3]
[1,]    1    2    3
[2,]    4    4    6
[3,]    7    8    9
8で補間する場合
> na.locf(x, fromLast = TRUE)
     [,1] [,2] [,3]
[1,]    1    2    3
[2,]    4    8    6
[3,]    7    8    9
6で補間する場合
> t(na.locf(t(x), fromLast = TRUE))
     [,1] [,2] [,3]
[1,]    1    2    3
[2,]    4    6    6
[3,]    7    8    9

周辺値の平均値で補間

na.approx関数を使うと周辺値の平均値*1を使った補間も可能になります。
ただし元が時系列処理用のパッケージなんで、普通にやると行/列それぞれ一方向の値のみを使った補間になってしまいます。
・・・説明が難しいので具体例を見てください。

行方向の値で補間
> na.approx(x)
     [,1] [,2] [,3]
[1,]    1    2    3
[2,]    4    5    6
[3,]    7    8    9

この場合「(2+8)÷2=5」として補間されてます。

列方向の値で補間
> t(na.approx(t(x)))
     [,1] [,2] [,3]
[1,]    1    2    3
[2,]    4    5    6
[3,]    7    8    9

この場合「(4+6)÷2=5」として補間されてます。

四方全部(行/列両方向)の値で補間

要するに上2つの結果を合わせればいいので、

> 0.5 * (na.approx(x) + t(na.approx(t(x))))
     [,1] [,2] [,3]
[1,]    1    2    3
[2,]    4    5    6
[3,]    7    8    9

とすればいいです。
トリッキーな書き方のような気もするけどこれしか思いつきませんでした。。。どなたかアイディアがあれば是非教えてください。

まとめ

・・・というわけで、zooパッケージにあるna.locf/na.approx関数を使えば上記のように簡単に行列の周辺構造を使った補間が可能になりますよいうお話でした。

*1:実際には線形補間値