二次元データの(線形)補間を行う

一次元のケースは二年も前に書いた

でよいとして、二次元のケースをなんとかしたい。したいというか、しなければならない状況になった。

ググりまくったところ

というパッケージに入っている関数を使えばよさげだということがわかった。


この2つのパッケージは、手元の補間したいデータの構造に応じて使い分ければよさそうだった。具体的には、以下のように適当にデータを作っておく。

library(dplyr)
library(tidyr)
#適当なサンプルデータ
X0 <- 1:10
Y0 <- 1:10
df <- expand.grid(x=X0, y=Y0) %>% mutate(z=x^2-y^2+y)
ls <- list(x=X0, y=Y0, z=matrix(df$z, nrow=length(X0), ncol=length(Y0)))

これは、dfがデータフレームで、lsがリストのデータ構造になっている、データ形式だけが違う同一のデータだ。中身の意味は見ればだいたいわかるように、x, yという二次元座標値に値zが割り振られている、そういうもんだ。

> df %>% head
  x y  z
1 1 1  1
2 2 1  4
3 3 1  9
4 4 1 16
5 5 1 25
6 6 1 36
> ls %>% head
$x
 [1]  1  2  3  4  5  6  7  8  9 10
$y
 [1]  1  2  3  4  5  6  7  8  9 10
$z
      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
 [1,]    1   -1   -5  -11  -19  -29  -41  -55  -71   -89
 [2,]    4    2   -2   -8  -16  -26  -38  -52  -68   -86
(... 途中省略 ...)
 [9,]   81   79   75   69   61   51   39   25    9    -9
[10,]  100   98   94   88   80   70   58   44   28    10

このような2つの異なるデータ形式の同一のデータがあり、それらの二次元補間をしようと思った場合には、

  • データフレーム(data.frame)の場合はakimaパッケージ
  • リスト(list)のの場合はfieldsパッケージ

という使い分けで補間してやるのがよさそうだった。実際は、データ形式を揃えればどちらか片方のパッケージだけでいいんだけども。rlistパッケージあたりを使えば簡単にデータの変換できるのか?


実際にたしかめるために、まず、補間する点(x, y)の組を以下のように与えておく。そして、各点に対する線形な補間値を計算させてみる。また、あわせて答えの一致も確認する。

#補間する点
x <- c(2, 4, 8)
y <- c(3, 7, 2)

akimaパッケージの場合

akimaパッケージは不規則なグリッド*1上にあるデータのための補間パッケージだ。このパッケージで二次元線形補間をするためには

  • interp関数
  • inerpp関数

を用いる。interpp関数の方が"点ごと"の補間に対応して、interp関数は、下記コードのコメントに書いたようにx×yの直積集合の全ての元に対して補間を実行してくれる。

library("akima")
#点(2,3)(4,7)(8,2)での線形補間
inter_pp <- interpp(df$x, df$y, df$z, x, y)
#直積集合(2,4,8)×(3,7,2)での線形補間
inter_p  <- interp( df$x, df$y, df$z, x, y)

結果はリストで帰ってきて、zに補間値が入っている。

> inter_pp$z
[1]  -2 -26  62
> inter_p$z
     [,1] [,2] [,3]
[1,]   -2  -38    2
[2,]   10  -26   14
[3,]   58   22   62

ちなみにx, yには補間した位置情報が入ってる。また、interp/interpp関数ともにlinear引数があって、これをFALSE(デフォルトはTRUE)にするとスプライン補間にかわるはずなんだけど、このデータだとなぜか全部NAになった、なぜ???関数を掘っていったら、"sdsf3p"なるFortranの関数がよばれていたので、これを読まないとだめだな・・・

fieldsパッケージの場合

fieldsパッケージは空間統計のためのツールが格納されたパッケージとのことだ。このパッケージでは

という2つの関数が2次元線形補間を実行してくれる関数だ。これもakimaパッケージのinterp & interppの関係に近くて、点の補間かグリッドまるごと(直積集合)の補間かで関数が異なっているだけ。

> library(fields)
> #点(2,3)(4,7)(8,2)での線形補間
> interp.surface(ls, cbind(x, y))
[1]  -2 -26  62
> #直積集合(2,4,8)×(3,7,2)での線形補間
> interp.surface.grid(ls, list(x=x, y=y))
$x
[1] 2 4 8

$y
[1] 3 7 2

$z
     [,1] [,2] [,3]
[1,]   -2  -38    2
[2,]   10  -26   14
[3,]   58   22   62

結果はakimaパッケージのものとちゃんと一致している。どちらのパッケージを使うのかは、データ形式に応じて使い分けるか、変換の手間を惜しまず、どちらか一方だけ使っててもいい感じかな。

*1:均一なグリッドじゃないという意味かと