免責事項:私は主に構文上の考慮事項と一般的な動作を考慮してこの投稿を書いています。ここで説明する方法のメモリとCPUの側面についてはよく知りません。この回答は、補間の品質を考慮すべき主な側面となるように、データセットがかなり小さい人を対象としています。非常に大きなデータセットを操作する場合、パフォーマンスの高いメソッド(griddata
およびRbf
)は実行できない場合があることを知っています。
3種類の多次元補間法(interp2d
/ splines、griddata
およびRbf
)を比較します。2種類の内挿タスクと2種類の基礎となる関数(内挿されるポイント)をそれらに適用します。特定の例では2次元の補間を示しますが、実行可能な方法は任意の次元に適用できます。各メソッドは、さまざまな種類の補間を提供します。すべての場合で、3次補間(またはそれに近いもの1)を使用します。補間を使用する場合は常に、生データと比較してバイアスが発生し、使用される特定の方法が最終的に生じるアーティファクトに影響することに注意することが重要です。常にこれに注意し、責任を持って補間してください。
2つの補間タスクは
- アップサンプリング(入力データは長方形グリッド上にあり、出力データはより密なグリッド上にあります)
- 通常のグリッドへの散在データの補間
(ドメイン上の[x,y] in [-1,1]x[-1,1]
)2つの関数は
- スムーズでフレンドリーな機能:
cos(pi*x)*sin(pi*y)
; 範囲内[-1, 1]
- 悪(特に、非連続)関数:
x*y/(x^2+y^2)
原点付近の値が0.5の場合。範囲内[-0.5, 0.5]
それらは次のようになります。
最初に、これらの4つのテストで3つのメソッドがどのように動作するかを示し、次に3つすべての構文を詳しく説明します。メソッドに何を期待すべきかがわかっている場合は、その構文の学習に時間を費やしたくない場合があります(あなたを見てくださいinterp2d
)。
テストデータ
明確にするために、入力データを生成するために使用したコードを次に示します。この特定のケースでは、データの基礎となる関数を明確に認識していますが、これを使用して、補間メソッドの入力を生成します。私は便宜上(そしてほとんどの場合データを生成するために)numpyを使用していますが、scipyだけでも十分です。
import numpy as np
import scipy.interpolate as interp
# auxiliary function for mesh generation
def gimme_mesh(n):
minval = -1
maxval = 1
# produce an asymmetric shape in order to catch issues with transpositions
return np.meshgrid(np.linspace(minval,maxval,n), np.linspace(minval,maxval,n+1))
# set up underlying test functions, vectorized
def fun_smooth(x, y):
return np.cos(np.pi*x)*np.sin(np.pi*y)
def fun_evil(x, y):
# watch out for singular origin; function has no unique limit there
return np.where(x**2+y**2>1e-10, x*y/(x**2+y**2), 0.5)
# sparse input mesh, 6x7 in shape
N_sparse = 6
x_sparse,y_sparse = gimme_mesh(N_sparse)
z_sparse_smooth = fun_smooth(x_sparse, y_sparse)
z_sparse_evil = fun_evil(x_sparse, y_sparse)
# scattered input points, 10^2 altogether (shape (100,))
N_scattered = 10
x_scattered,y_scattered = np.random.rand(2,N_scattered**2)*2 - 1
z_scattered_smooth = fun_smooth(x_scattered, y_scattered)
z_scattered_evil = fun_evil(x_scattered, y_scattered)
# dense output mesh, 20x21 in shape
N_dense = 20
x_dense,y_dense = gimme_mesh(N_dense)
スムーズな機能とアップサンプリング
最も簡単なタスクから始めましょう。形状のメッシュ[6,7]
からいずれかのメッシュへのアップサンプリング[20,21]
がスムーズなテスト機能のために機能する方法を次に示します。
これは簡単な作業ですが、出力にはすでに微妙な違いがあります。一見すると、3つの出力すべてが妥当です。基礎となる機能に関する私たちの事前の知識に基づいて、注意すべき2つの機能がありますgriddata
。y==-1
プロットの境界に注意してください(x
ラベルに最も近い):関数は厳密にゼロでなければなりません(y==-1
滑らかな関数の節線であるため)が、これは当てはまりませんgriddata
。またx==-1
、プロットの境界(背後、左側)にも注意してください。基礎となる関数にはで極大(境界付近のゼロ勾配)が[-1, -0.5]
ありますが、griddata
出力はこの領域で明らかに非ゼロ勾配を示しています。効果は微妙ですが、それでもバイアスです。(の忠実性Rbf
ラジアル関数のデフォルトの選択(吹き替えmultiquadratic
)を使用するとさらに優れます。)
悪の機能とアップサンプリング
少し難しいタスクは、私たちの悪の機能でアップサンプリングを実行することです:
3つの方法の間に明らかな違いが現れ始めています。表面プロットを見ると、からの出力に明らかな偽の極値がありますinterp2d
(プロットされた表面の右側にある2つのこぶに注意してください)。ながらgriddata
とRbf
一見類似の結果を生じるように見える、後者は近く深い最小値を生成するように思われる[0.4, -0.4]
ことは、基礎となる関数は存在しません。
ただし、非常にRbf
優れている重要な側面が1つあります。それは、基礎となる関数の対称性を尊重します(もちろん、サンプルメッシュの対称性によっても可能になります)。からの出力griddata
は、サンプルポイントの対称性を壊します。サンプルポイントは、滑らかなケースではすでに弱く見えています。
スムーズな機能と散在したデータ
ほとんどの場合、散在するデータに対して補間を実行する必要があります。このため、これらのテストがより重要になることを期待しています。上記のように、サンプルポイントは対象のドメインで疑似均一に選択されました。現実的なシナリオでは、各測定で追加のノイズが発生する可能性があるため、最初に生データを補間することが理にかなっているかどうかを検討する必要があります。
スムーズ関数の出力:
現在、すでにホラーショーが行われています。少なくとも最小限の量の情報を保持するために、出力をinterp2d
からの間[-1, 1]
だけをプロット用にクリップしました。基本的な形状の一部が存在する一方で、メソッドが完全に機能しなくなる大きなノイズの多い領域があることは明らかです。の2番目のケースではgriddata
、形状がかなりうまく再現されていますが、等高線図の境界にある白い領域に注意してください。これはgriddata
、入力データポイントの凸包の内部でのみ機能する(つまり、外挿を実行しない)ためです。凸包の外側にある出力ポイントのデフォルトのNaN値を維持しました。2これらの機能を考慮するRbf
と、パフォーマンスが最も良いようです。
悪の機能と散在するデータ
そして私たち全員が待っていた瞬間:
interp2d
あきらめることは大きな驚きではありません。実際、呼び出し中に、スプラインを作成できないことについて不平を言うinterp2d
友好的な人がいることを期待する必要がありますRuntimeWarning
。他の2つの方法についてRbf
は、結果が推定されるドメインの境界の近くでも、最高の出力を生成するようです。
したがって、3つの方法について、優先順位の高い順にいくつかの言葉を述べます(最悪の場合、誰にも読まれないように)。
scipy.interpolate.Rbf
Rbf
このクラスは、「放射基底関数」の略です。正直なところ、この投稿について調査を始めるまで、このアプローチを検討したことはありませんが、将来的にこれらを使用することになると確信しています。
スプラインベースのメソッド(後述)と同様に、使用法は2つのステップで行われます。最初のステップRbf
は、入力データに基づいて呼び出し可能なクラスインスタンスを作成し、次に、指定された出力メッシュに対してこのオブジェクトを呼び出して、補間結果を取得します。スムーズアップサンプリングテストの例:
import scipy.interpolate as interp
zfun_smooth_rbf = interp.Rbf(x_sparse, y_sparse, z_sparse_smooth, function='cubic', smooth=0) # default smooth=0 for interpolation
z_dense_smooth_rbf = zfun_smooth_rbf(x_dense, y_dense) # not really a function, but a callable class instance
この場合、入力ポイントと出力ポイントの両方が2D配列であり、出力z_dense_smooth_rbf
は同じ形状でx_dense
、y_dense
何の努力もしていません。またRbf
、補間は任意の次元をサポートしています。
そう、 scipy.interpolate.Rbf
- クレイジーな入力データに対しても行儀の良い出力を生成します
- 高次元での補間をサポート
- 入力ポイントの凸包の外側を外挿します(もちろん、外挿は常にギャンブルであり、通常、これに依存するべきではありません)。
- 最初のステップとして補間器を作成するので、さまざまな出力ポイントでそれを評価することは追加の労力が少ない
- 任意の形状の出力ポイントを持つことができます(長方形のメッシュに制約されるのではなく、後で参照)。
- 入力データの対称性を維持する傾向がある
- キーワードの半径の関数の複数の種類をサポート
function
:multiquadric
、inverse
、gaussian
、linear
、cubic
、quintic
、thin_plate
およびユーザ定義の任意
scipy.interpolate.griddata
私の以前のお気に入り、griddata
は、任意の次元での補間の一般的な主力です。節点の凸包の外側の点に単一のプリセット値を設定する以外に外挿を実行しませんが、外挿は非常に気まぐれで危険なものであるため、これは必ずしもコンドではありません。使用例:
z_dense_smooth_griddata = interp.griddata(np.array([x_sparse.ravel(),y_sparse.ravel()]).T,
z_sparse_smooth.ravel(),
(x_dense,y_dense), method='cubic') # default method is linear
少しぎこちない構文に注意してください。入力ポイントは[N, D]
、D
次元の形状の配列で指定する必要があります。このためには、まず2D座標配列をフラット化し(を使用ravel
)、次に配列を連結して結果を転置する必要があります。これには複数の方法がありますが、すべてがかさばるようです。入力z
データもフラット化する必要があります。出力ポイントに関しては、もう少し自由があります。何らかの理由で、これらは多次元配列のタプルとして指定することもできます。help
of griddata
は誤解を招くものであることに注意してください。これは、同じことが入力ポイントにも当てはまることを示唆しています(少なくともバージョン0.17.0の場合)。
griddata(points, values, xi, method='linear', fill_value=nan, rescale=False)
Interpolate unstructured D-dimensional data.
Parameters
----------
points : ndarray of floats, shape (n, D)
Data point coordinates. Can either be an array of
shape (n, D), or a tuple of `ndim` arrays.
values : ndarray of float or complex, shape (n,)
Data values.
xi : ndarray of float, shape (M, D)
Points at which to interpolate data.
一言で言えば、 scipy.interpolate.griddata
- クレイジーな入力データに対しても行儀の良い出力を生成します
- 高次元での補間をサポート
- 外挿を実行せず、入力ポイントの凸包の外側の出力に単一の値を設定できます(を参照
fill_value
)
- 1回の呼び出しで内挿値を計算するため、出力ポイントの複数のセットのプローブはゼロから始まります
- 任意の形状の出力ポイントを持つことができます
- 1次元および2次元の3次の任意の次元での最近傍および線形補間をサポートします。最近傍と線形補間は、それぞれ内部
NearestNDInterpolator
と内部で使用さLinearNDInterpolator
れます。1dキュービック補間はスプラインを使用し、2dキュービック補間はCloughTocher2DInterpolator
連続的に微分可能な区分的キュービック補間を作成するために使用します。
- 入力データの対称性に違反する可能性があります
scipy.interpolate.interp2d
/scipy.interpolate.bisplrep
私が議論interp2d
している唯一の理由とその親類は、それが偽の名前を持っていること、そして人々がそれを使用しようとする可能性が高いということです。スポイラー警告:使用しないでください(scipyバージョン0.17.0以降)。これは、2次元補間に特に使用されるという点で、以前の主題よりもすでに特別ですが、これは多変量補間の最も一般的なケースだと思います。
構文に関する限り、interp2d
はRbf
、実際に補間された値を提供するために呼び出すことができる補間インスタンスを最初に構築する必要があるという点でに似ています。ただし、注意点があります。出力ポイントは長方形メッシュ上に配置する必要があるため、補間器の呼び出しに入る入力は、次のように、出力グリッドにまたがる1dベクトルでなければなりませんnumpy.meshgrid
。
# reminder: x_sparse and y_sparse are of shape [6, 7] from numpy.meshgrid
zfun_smooth_interp2d = interp.interp2d(x_sparse, y_sparse, z_sparse_smooth, kind='cubic') # default kind is 'linear'
# reminder: x_dense and y_dense are of shape [20, 21] from numpy.meshgrid
xvec = x_dense[0,:] # 1d array of unique x values, 20 elements
yvec = y_dense[:,0] # 1d array of unique y values, 21 elements
z_dense_smooth_interp2d = zfun_smooth_interp2d(xvec,yvec) # output is [20, 21]-shaped array
使用時の最も一般的な間違いの1つは、interp2d
完全な2Dメッシュを補間呼び出しに入れることです。これにより、メモリが爆発的に消費され、できれば急いでしまいMemoryError
ます。
今、最大の問題interp2d
は、それがしばしば機能しないことです。これを理解するためには、内部を調べる必要があります。これinterp2d
は、FITPACKルーチン(Fortranで記述)のラッパーである下位レベルの関数bisplrep
+のbisplev
ラッパーであることがわかります。前の例と同等の呼び出しは次のようになります
kind = 'cubic'
if kind=='linear':
kx=ky=1
elif kind=='cubic':
kx=ky=3
elif kind=='quintic':
kx=ky=5
# bisplrep constructs a spline representation, bisplev evaluates the spline at given points
bisp_smooth = interp.bisplrep(x_sparse.ravel(),y_sparse.ravel(),z_sparse_smooth.ravel(),kx=kx,ky=ky,s=0)
z_dense_smooth_bisplrep = interp.bisplev(xvec,yvec,bisp_smooth).T # note the transpose
さて、ここについてのことだinterp2d
:(scipyのダウンロードバージョン0.17.0に)すてきな存在であるにコメントinterpolate/interpolate.py
のためにはinterp2d
:
if not rectangular_grid:
# TODO: surfit is really not meant for interpolation!
self.tck = fitpack.bisplrep(x, y, z, kx=kx, ky=ky, s=0.0)
実際にinterpolate/fitpack.py
、bisplrep
いくつかの設定があり、最終的には
tx, ty, c, o = _fitpack._surfit(x, y, z, w, xb, xe, yb, ye, kx, ky,
task, s, eps, tx, ty, nxest, nyest,
wrk, lwrk1, lwrk2)
以上です。根底にinterp2d
あるルーチンは、実際に補間を実行するためのものではありません。それらは十分に行儀の良いデータには十分かもしれませんが、現実的な状況ではおそらく別のものを使用したいと思うでしょう。
結論として、 interpolate.interp2d
- よく調整されたデータでもアーティファクトにつながる可能性があります
- 特に2変量問題用です(ただし
interpn
、グリッド上で定義された入力ポイントには制限があります)。
- 外挿を行う
- 最初のステップとして補間器を作成するので、さまざまな出力ポイントでそれを評価することは追加の労力が少ない
- 長方形グリッド上でのみ出力を生成できます。分散出力の場合は、ループで補間器を呼び出す必要があります
- 線形、三次、五次補間をサポート
- 入力データの対称性に違反する可能性があります
1の基底関数cubic
とそのlinear
種類の関数Rbf
が、同じ名前の他の補間器に正確に対応していないことは、かなり確実です。
2これらのNaNは、表面プロットが奇妙に見える理由でもあります。matplotlibは、適切な深度情報を持つ複雑な3Dオブジェクトをプロットすることを歴史的に困難にしています。データのNaN値はレンダラーを混乱させるので、背面にあるはずの表面の部分が前面にあるようにプロットされます。これは視覚化の問題であり、補間ではありません。