web-dev-qa-db-ja.com

2つの不規則なグリッド間の複数の補間のためのscipygriddataの高速化

同じ不規則なグリッド_(x, y, z)_で定義されている値がいくつかあり、新しいグリッド_(x1, y1, z1)_に補間したいと思います。つまり、f(x, y, z), g(x, y, z), h(x, y, z)があり、f(x1, y1, z1), g(x1, y1, z1), h(x1, y1, z1)を計算したいと思います。

現時点では、_scipy.interpolate.griddata_を使用してこれを行っていますが、うまく機能しています。ただし、各補間を個別に実行する必要があり、ポイントが多いため、計算が非常に重複して(つまり、最も近いポイントを見つける、グリッドを設定するなど)、非常に時間がかかります。

計算を高速化し、重複する計算を減らす方法はありますか?つまり、2つのグリッドを定義し、補間の値を変更するという方針に沿った何かですか?

24
s_haskey

scipy.interpolate.griddataに電話をかけるたびに、いくつかのことが起こっています。

  1. まず、sp.spatial.qhull.Delaunayを呼び出して、不規則なグリッド座標を三角測量します。
  2. 次に、新しいグリッドの各ポイントについて、三角形分割を検索して、どの三角形(実際には、どのシンプレックス、3Dの場合はどの四面体)に配置されているかを見つけます。
  3. 囲んでいるシンプレックスの頂点に関する新しい各グリッドポイントの重心座標が計算されます。
  4. 重心座標と、囲んでいるシンプレックスの頂点での関数の値を使用して、そのグリッドポイントの補間値が計算されます。

最初の3つの手順はすべての内挿で同じであるため、新しいグリッドポイントごとに、囲んでいるシンプレックスの頂点のインデックスと内挿の重みを格納できれば、計算量を大幅に最小限に抑えることができます。残念ながら、これは利用可能な機能を使用して直接行うのは簡単ではありませんが、実際には可能です。

import scipy.interpolate as spint
import scipy.spatial.qhull as qhull
import itertools

def interp_weights(xyz, uvw):
    tri = qhull.Delaunay(xyz)
    simplex = tri.find_simplex(uvw)
    vertices = np.take(tri.simplices, simplex, axis=0)
    temp = np.take(tri.transform, simplex, axis=0)
    delta = uvw - temp[:, d]
    bary = np.einsum('njk,nk->nj', temp[:, :d, :], delta)
    return vertices, np.hstack((bary, 1 - bary.sum(axis=1, keepdims=True)))

def interpolate(values, vtx, wts):
    return np.einsum('nj,nj->n', np.take(values, vtx), wts)

関数interp_weightsは、上記の最初の3つのステップの計算を実行します。次に、関数interpolateは、これらの計算値を使用して、ステップ4を非常に高速に実行します。

m, n, d = 3.5e4, 3e3, 3
# make sure no new grid point is extrapolated
bounding_cube = np.array(list(itertools.product([0, 1], repeat=d)))
xyz = np.vstack((bounding_cube,
                 np.random.Rand(m - len(bounding_cube), d)))
f = np.random.Rand(m)
g = np.random.Rand(m)
uvw = np.random.Rand(n, d)

In [2]: vtx, wts = interp_weights(xyz, uvw)

In [3]: np.allclose(interpolate(f, vtx, wts), spint.griddata(xyz, f, uvw))
Out[3]: True

In [4]: %timeit spint.griddata(xyz, f, uvw)
1 loops, best of 3: 2.81 s per loop

In [5]: %timeit interp_weights(xyz, uvw)
1 loops, best of 3: 2.79 s per loop

In [6]: %timeit interpolate(f, vtx, wts)
10000 loops, best of 3: 66.4 us per loop

In [7]: %timeit interpolate(g, vtx, wts)
10000 loops, best of 3: 67 us per loop

したがって、最初はgriddataと同じように動作します。これは良いことです。次に、補間の設定、つまりvtxwtsの計算は、griddataの呼び出しとほぼ同じです。しかし第3に、同じグリッド上の異なる値を事実上短時間で補間できるようになりました。

ここで考慮されていないgriddataが行う唯一のことは、外挿する必要のあるポイントにfill_valueを割り当てることです。これを行うには、重みの少なくとも1つが負であるポイントをチェックします。例:

def interpolate(values, vtx, wts, fill_value=np.nan):
    ret = np.einsum('nj,nj->n', np.take(values, vtx), wts)
    ret[np.any(wts < 0, axis=1)] = fill_value
    return ret
40
Jaime

Jaimeのソリューションに感謝します(重心計算がどのように行われるかを本当に理解していなくても...)

ここでは、2Dでの彼のケースから適応された例を見つけるでしょう:

import scipy.interpolate as spint
import scipy.spatial.qhull as qhull
import numpy as np

def interp_weights(xy, uv,d=2):
    tri = qhull.Delaunay(xy)
    simplex = tri.find_simplex(uv)
    vertices = np.take(tri.simplices, simplex, axis=0)
    temp = np.take(tri.transform, simplex, axis=0)
    delta = uv - temp[:, d]
    bary = np.einsum('njk,nk->nj', temp[:, :d, :], delta)
    return vertices, np.hstack((bary, 1 - bary.sum(axis=1, keepdims=True)))

def interpolate(values, vtx, wts):
    return np.einsum('nj,nj->n', np.take(values, vtx), wts)

m, n = 101,201
mi, ni = 1001,2001

[Y,X]=np.meshgrid(np.linspace(0,1,n),np.linspace(0,2,m))
[Yi,Xi]=np.meshgrid(np.linspace(0,1,ni),np.linspace(0,2,mi))

xy=np.zeros([X.shape[0]*X.shape[1],2])
xy[:,0]=Y.flatten()
xy[:,1]=X.flatten()
uv=np.zeros([Xi.shape[0]*Xi.shape[1],2])
uv[:,0]=Yi.flatten()
uv[:,1]=Xi.flatten()

values=np.cos(2*X)*np.cos(2*Y)

#Computed once and for all !
vtx, wts = interp_weights(xy, uv)
valuesi=interpolate(values.flatten(), vtx, wts)
valuesi=valuesi.reshape(Xi.shape[0],Xi.shape[1])
print "interpolation error: ",np.mean(valuesi-np.cos(2*Xi)*np.cos(2*Yi))  
print "interpolation uncertainty: ",np.std(valuesi-np.cos(2*Xi)*np.cos(2*Yi))  

イメージマッピングなどの画像変換を適用することが可能です。

新しい座標は反復ごとに変更されるため、同じ関数定義を使用することはできませんが、三角測量を一度だけ計算することはできます。

import scipy.interpolate as spint
import scipy.spatial.qhull as qhull
import numpy as np
import time

# Definition of the fast  interpolation process. May be the Tirangulation process can be removed !!
def interp_tri(xy):
    tri = qhull.Delaunay(xy)
    return tri


def interpolate(values, tri,uv,d=2):
    simplex = tri.find_simplex(uv)
    vertices = np.take(tri.simplices, simplex, axis=0)
    temp = np.take(tri.transform, simplex, axis=0)
    delta = uv- temp[:, d]
    bary = np.einsum('njk,nk->nj', temp[:, :d, :], delta)  
    return np.einsum('nj,nj->n', np.take(values, vertices),  np.hstack((bary, 1.0 - bary.sum(axis=1, keepdims=True))))

m, n = 101,201
mi, ni = 101,201

[Y,X]=np.meshgrid(np.linspace(0,1,n),np.linspace(0,2,m))
[Yi,Xi]=np.meshgrid(np.linspace(0,1,ni),np.linspace(0,2,mi))

xy=np.zeros([X.shape[0]*X.shape[1],2])
xy[:,1]=Y.flatten()
xy[:,0]=X.flatten()
uv=np.zeros([Xi.shape[0]*Xi.shape[1],2])
# creation of a displacement field
uv[:,1]=0.5*Yi.flatten()+0.4
uv[:,0]=1.5*Xi.flatten()-0.7
values=np.zeros_like(X)
values[50:70,90:150]=100.

#Computed once and for all !
tri = interp_tri(xy)
t0=time.time()
for i in range(0,100):
  values_interp_Qhull=interpolate(values.flatten(),tri,uv,2).reshape(Xi.shape[0],Xi.shape[1])
t_q=(time.time()-t0)/100

t0=time.time()
values_interp_griddata=spint.griddata(xy,values.flatten(),uv,fill_value=0).reshape(values.shape[0],values.shape[1])
t_g=time.time()-t0

print "Speed-up:", t_g/t_q
print "Mean error: ",(values_interp_Qhull-values_interp_griddata).mean()
print "Standard deviation: ",(values_interp_Qhull-values_interp_griddata).std()

私のラップトップでは、スピードアップは20〜40倍です!

それが誰かを助けることができることを願っています

4
Jeff Witz

高性能のデータ構造を提供するため、 Pandas を使用してみることができます。

補間方法がscipy補間のラッパーであることは事実ですが、構造が改善されていると、速度が向上する可能性があります。

_import pandas as pd;
wp = pd.Panel(randn(2, 5, 4));
wp.interpolate();
_

interpolate()は、 さまざまなメソッド を使用してPanelデータセットのNaN値を入力します。 Scipyよりも速いことを願っています。

機能しない場合(コードの並列バージョンを使用する代わりに)パフォーマンスを向上させる1つの方法があります: Cython を使用し、Cで小さなルーチンを実装してPythonコード内で使用します。 ここ これに関する例があります。

0
phyrox

私は同じ問題を抱えていました(グリッドデータは非常に遅く、グリッドは多くの補間で同じままです)そして私は解決策が好きでした ここで説明 主に理解と適用が非常に簡単であるためです。

LinearNDInterpolatorを使用しており、1回だけ計算する必要があるDelaunay三角形分割を渡すことができます。その投稿からコピーして貼り付けます(xdze2へのすべてのクレジット):

from scipy.spatial import Delaunay
from scipy.interpolate import LinearNDInterpolator

tri = Delaunay(mesh1)  # Compute the triangulation

# Perform the interpolation with the given values:
interpolator = LinearNDInterpolator(tri, values_mesh1)
values_mesh2 = interpolator(mesh2)

これにより、計算が約2倍高速化されます。

0
Waterkant