scipyを使用して2次元補間を実行するにはどうすればよいですか?


105

このQ&Aは、scipyを使用した2次元(および多次元)補間に関する標準的な(-ish)を目的としています。さまざまな多次元補間法の基本的な構文に関する質問がよくあります。これらもまっすぐに設定したいと思います。

私は、散乱、二次元データポイントのセットを持っている、と私は、好ましくは、のようなものを使用して、素敵な表面としてそれらをプロットしたいcontourfplot_surfacematplotlib.pyplot。scipyを使用して、2次元または多次元のデータをメッシュに補間するにはどうすればよいですか?

scipy.interpolateサブパッケージを見つけましたが、interp2dor bisplrepまたはgriddataor を使用するとエラーが発生し続けrbfます。これらのメソッドの適切な構文は何ですか?

回答:


163

免責事項:私は主に構文上の考慮事項と一般的な動作を考慮してこの投稿を書いています。ここで説明する方法のメモリとCPUの側面についてはよく知りません。この回答は、補間の品質を考慮すべき主な側面となるように、データセットがかなり小さい人を対象としています。非常に大きなデータセットを操作する場合、パフォーマンスの高いメソッド(griddataおよびRbf)は実行できない場合があることを知っています。

3種類の多次元補間法(interp2d/ splines、griddataおよびRbf)を比較します。2種類の内挿タスクと2種類の基礎となる関数(内挿されるポイント)をそれらに適用します。特定の例では2次元の補間を示しますが、実行可能な方法は任意の次元に適用できます。各メソッドは、さまざまな種類の補間を提供します。すべての場合で、3次補間(またはそれに近いもの1)を使用します。補間を使用する場合は常に、生データと比較してバイアスが発生し、使用される特定の方法が最終的に生じるアーティファクトに影響することに注意することが重要です。常にこれに注意し、責任を持って補間してください。

2つの補間タスクは

  1. アップサンプリング(入力データは長方形グリッド上にあり、出力データはより密なグリッド上にあります)
  2. 通常のグリッドへの散在データの補間

(ドメイン上の[x,y] in [-1,1]x[-1,1])2つの関数は

  1. スムーズでフレンドリーな機能:cos(pi*x)*sin(pi*y); 範囲内[-1, 1]
  2. 悪(特に、非連続)関数:x*y/(x^2+y^2)原点付近の値が0.5の場合。範囲内[-0.5, 0.5]

それらは次のようになります。

fig1:テスト関数

最初に、これらの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]がスムーズなテスト機能のために機能する方法を次に示します。

fig2:スムーズなアップサンプリング

これは簡単な作業ですが、出力にはすでに微妙な違いがあります。一見すると、3つの出力すべてが妥当です。基礎となる機能に関する私たちの事前の知識に基づいて、注意すべき2つの機能がありますgriddatay==-1プロットの境界に注意してください(xラベルに最も近い):関数は厳密にゼロでなければなりません(y==-1滑らかな関数の節線であるため)が、これは当てはまりませんgriddata。またx==-1、プロットの境界(背後、左側)にも注意してください。基礎となる関数にはで極大(境界付近のゼロ勾配)が[-1, -0.5]ありますが、griddata出力はこの領域で明らかに非ゼロ勾配を示しています。効果は微妙ですが、それでもバイアスです。(の忠実性Rbfラジアル関数のデフォルトの選択(吹き替えmultiquadratic)を使用するとさらに優れます。)

悪の機能とアップサンプリング

少し難しいタスクは、私たちの悪の機能でアップサンプリングを実行することです:

fig3:邪悪なアップサンプリング

3つの方法の間に明らかな違いが現れ始めています。表面プロットを見ると、からの出力に明らかな偽の極値がありますinterp2d(プロットされた表面の右側にある2つのこぶに注意してください)。ながらgriddataRbf一見類似の結果を生じるように見える、後者は近く深い最小値を生成するように思われる[0.4, -0.4]ことは、基礎となる関数は存在しません。

ただし、非常にRbf優れている重要な側面が1つあります。それは、基礎となる関数の対称性を尊重します(もちろん、サンプルメッシュの対称性によっても可能になります)。からの出力griddataは、サンプルポイントの対称性を壊します。サンプルポイントは、滑らかなケースではすでに弱く見えています。

スムーズな機能と散在したデータ

ほとんどの場合、散在するデータに対して補間を実行する必要があります。このため、これらのテストがより重要になることを期待しています。上記のように、サンプルポイントは対象のドメインで疑似均一に選択されました。現実的なシナリオでは、各測定で追加のノイズが発生する可能性があるため、最初に生データを補間することが理にかなっているかどうかを検討する必要があります。

スムーズ関数の出力:

fig4:滑らかな散布補間

現在、すでにホラーショーが行われています。少なくとも最小限の量の情報を保持するために、出力をinterp2dからの間[-1, 1]だけをプロット用にクリップしました。基本的な形状の一部が存在する一方で、メソッドが完全に機能しなくなる大きなノイズの多い領域があることは明らかです。の2番目のケースではgriddata、形状がかなりうまく再現されていますが、等高線図の境界にある白い領域に注意してください。これはgriddata、入力データポイントの凸包の内部でのみ機能する(つまり、外挿を実行しない)ためです。凸包の外側にある出力ポイントのデフォルトのNaN値を維持しました。2これらの機能を考慮するRbfと、パフォーマンスが最も良いようです。

悪の機能と散在するデータ

そして私たち全員が待っていた瞬間:

fig5:邪悪な散乱補間

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_densey_dense何の努力もしていません。またRbf、補間は任意の次元をサポートしています。

そう、 scipy.interpolate.Rbf

  • クレイジーな入力データに対しても行儀の良い出力を生成します
  • 高次元での補間をサポート
  • 入力ポイントの凸包の外側を外挿します(もちろん、外挿は常にギャンブルであり、通常、これに依存するべきではありません)。
  • 最初のステップとして補間器を作成するので、さまざまな出力ポイントでそれを評価することは追加の労力が少ない
  • 任意の形状の出力ポイントを持つことができます(長方形のメッシュに制約されるのではなく、後で参照)。
  • 入力データの対称性を維持する傾向がある
  • キーワードの半径の関数の複数の種類をサポートfunctionmultiquadricinversegaussianlinearcubicquinticthin_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データもフラット化する必要があります。出力ポイントに関しては、もう少し自由があります。何らかの理由で、これらは多次元配列のタプルとして指定することもできます。helpof 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次元補間に特に使用されるという点で、以前の主題よりもすでに特別ですが、これは多変量補間の最も一般的なケースだと思います。

構文に関する限り、interp2dRbf、実際に補間された値を提供するために呼び出すことができる補間インスタンスを最初に構築する必要があるという点でに似ています。ただし、注意点があります。出力ポイントは長方形メッシュ上に配置する必要があるため、補間器の呼び出しに入る入力は、次のように、出力グリッドにまたがる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.pybisplrepいくつかの設定があり、最終的には

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値はレンダラーを混乱させるので、背面にあるはずの表面の部分が前面にあるようにプロットされます。これは視覚化の問題であり、補間ではありません。


2
データポイントの数と次元の数によっては、RBFはgriddataよりも多くのメモリを消費する可能性があります。また、griddataには、Rbfのように2つのステップで使用できる、基になるオブジェクトLinearNDInterpolatorがあります。
denfromufa

1
Griddataの3次補間は2(?)次元に制限されています。高次元の場合、chebfunに基づくスモリヤックスパースグリッドは検討に値します。
denfromufa

1
私はすべての散乱の補間オプションを研究し、このリンク、と私のコメントを終了してみましょう:scicomp.stackexchange.com/questions/19137/...
denfromufa

4
griddata線形補間はローカル、griddata 3次補間はグローバルです。連続性/微分可能性を維持する方法を理解する時間がなかったため、外挿はサポートされていません。Rbfは小さなデータセットでは問題ありませんが、n個のデータポイントを補間するには、nxn行列を反転する必要があり、n> 5000になると最終的に不可能になります。また、Rbfはデータの分布に影響されやすいため、手動でパラメーターを微調整する必要がある場合があります。大きなデータセットに対してRbfを実行することは可能ですが、これはscipyでは実装されていません。
pv。

1
大規模なデータセットのrbfは次のとおりです:github.com/scipy/scipy/issues/5180
denfromufa
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.