web-dev-qa-db-ja.com

ランダムな非構造化データの3Dスプライン/スムーズ補間が必要な場合はどうすればよいですか?

私はこれに触発されました answer @Jamesによって、griddatamap_coordinatesが使用される可能性があります。以下の例では2Dデータを示していますが、私の興味は3Dです。 griddataは1Dと2Dのスプラインのみを提供し、3D以上の線形補間に制限されていることに気付きました(おそらく非常に理由があります)。ただし、map_coordinatesは、高次(区分的線形よりも滑らか)補間を使用する3Dでは問題ないようです。

私の主な質問:3Dでランダムな非構造化データ(map_coordinatesを使用できない場合)がある場合、区分的よりもスムーズにする方法はありますか? NumPy SciPyユニバース内、または少なくともその近くでの賢明な線形補間?

私の2番目の質問:3Dのスプラインは実装が難しいか面倒なのでgriddataで利用できませんか、それとも根本的な問題がありますか?

以下の画像と恐ろしいpythonは、griddataとmap_coordinatesを使用できる方法と使用できない方法についての私の現在の理解を示しています。補間は太い黒い線に沿って行われます。

構造化データ:

interpolation of structured data

[〜#〜] un [〜#〜]構造化データ:

interpolation of unstructured data

恐ろしいPython:

import numpy as np
import matplotlib.pyplot as plt

def g(x, y):
    return np.exp(-((x-1.0)**2 + (y-1.0)**2))

def findit(x, X):  # or could use some 1D interpolation
    fraction = (x - X[0]) / (X[-1]-X[0])
    return fraction * float(X.shape[0]-1)

nth, nr = 12, 11
theta_min, theta_max = 0.2, 1.3
r_min,     r_max     = 0.7, 2.0

theta = np.linspace(theta_min, theta_max, nth)
r     = np.linspace(r_min,     r_max,     nr)

R, TH = np.meshgrid(r, theta)
Xp, Yp  = R*np.cos(TH), R*np.sin(TH)
array = g(Xp, Yp)

x, y = np.linspace(0.0, 2.0, 200), np.linspace(0.0, 2.0, 200)
X, Y = np.meshgrid(x, y)
blob = g(X, Y)

xtest = np.linspace(0.25, 1.75, 40)
ytest = np.zeros_like(xtest) + 0.75

rtest = np.sqrt(xtest**2 + ytest**2)
thetatest = np.arctan2(xtest, ytest)

ir = findit(rtest, r)
it = findit(thetatest, theta)

plt.figure()

plt.subplot(2,1,1)

plt.scatter(100.0*Xp.flatten(), 100.0*Yp.flatten())
plt.plot(100.0*xtest, 100.0*ytest, '-k', linewidth=3)
plt.hold
plt.imshow(blob, Origin='lower', cmap='gray')

plt.text(5, 5, "don't use jet!", color='white')


exact = g(xtest, ytest)

import scipy.ndimage.interpolation as spndint
ndint0 = spndint.map_coordinates(array, [it, ir], order=0)
ndint1 = spndint.map_coordinates(array, [it, ir], order=1)
ndint2 = spndint.map_coordinates(array, [it, ir], order=2)

import scipy.interpolate as spint
points = np.vstack((Xp.flatten(), Yp.flatten())).T   # could use np.array(Zip(...))
grid_x = xtest
grid_y = np.array([0.75])

g0 = spint.griddata(points, array.flatten(), (grid_x, grid_y), method='nearest')
g1 = spint.griddata(points, array.flatten(), (grid_x, grid_y), method='linear')
g2 = spint.griddata(points, array.flatten(), (grid_x, grid_y), method='cubic')


plt.subplot(4,2,5)

plt.plot(exact, 'or')
#plt.plot(ndint0)
plt.plot(ndint1)
plt.plot(ndint2)
plt.title("map_coordinates")

plt.subplot(4,2,6)

plt.plot(exact, 'or')
#plt.plot(g0)
plt.plot(g1)
plt.plot(g2)
plt.title("griddata")

plt.subplot(4,2,7)

#plt.plot(ndint0 - exact)
plt.plot(ndint1 - exact)
plt.plot(ndint2 - exact)
plt.title("error map_coordinates")

plt.subplot(4,2,8)

#plt.plot(g0 - exact)
plt.plot(g1 - exact)
plt.plot(g2 - exact)
plt.title("error griddata")

plt.show()


seed_points_Rand = 2.0 * np.random.random((400, 2))
rr = np.sqrt((seed_points_Rand**2).sum(axis=-1))
thth = np.arctan2(seed_points_Rand[...,1], seed_points_Rand[...,0])
isinside = (rr>r_min) * (rr<r_max) * (thth>theta_min) * (thth<theta_max)
points_Rand = seed_points_Rand[isinside]

Xprand, Yprand = points_Rand.T  # unpack
array_Rand = g(Xprand, Yprand)

grid_x = xtest
grid_y = np.array([0.75])

plt.figure()

plt.subplot(2,1,1)

plt.scatter(100.0*Xprand.flatten(), 100.0*Yprand.flatten())
plt.plot(100.0*xtest, 100.0*ytest, '-k', linewidth=3)
plt.hold
plt.imshow(blob, Origin='lower', cmap='gray')
plt.text(5, 5, "don't use jet!", color='white')


g0Rand = spint.griddata(points_Rand, array_Rand.flatten(), (grid_x, grid_y), method='nearest')
g1Rand = spint.griddata(points_Rand, array_Rand.flatten(), (grid_x, grid_y), method='linear')
g2Rand = spint.griddata(points_Rand, array_Rand.flatten(), (grid_x, grid_y), method='cubic')

plt.subplot(4,2,6)

plt.plot(exact, 'or')
#plt.plot(g0Rand)
plt.plot(g1Rand)
plt.plot(g2Rand)
plt.title("griddata")


plt.subplot(4,2,8)

#plt.plot(g0Rand - exact)
plt.plot(g1Rand - exact)
plt.plot(g2Rand - exact)
plt.title("error griddata")

plt.show()
15
uhoh

良い質問! (そして素敵なプロット!)

非構造化データの場合は、非構造化データ用の関数に戻す必要があります。 griddataは1つのオプションですが、間に線形補間を使用した三角形分割を使用します。これにより、三角形の境界に「ハード」エッジが生じます。

スプラインは放射基底関数です。 scipyの用語では、 scipy.interpolate.Rbf が必要です。立方スプラインではfunction="linear"またはfunction="thin_plate"を使用することをお勧めしますが、立方も使用できます。 (3次スプラインは、線形スプラインまたは薄板スプラインと比較して、「オーバーシュート」の問題を悪化させます。)

注意点の1つは、放射基底関数のこの特定の実装では、データセット内のすべてのポイントが常に使用されることです。これは最も正確でスムーズなアプローチですが、入力観測点の数が増えるとスケーリングが不十分になります。これを回避する方法はいくつかありますが、状況はさらに複雑になります。別の質問に残しておきます。

とにかく、ここに簡単な例があります。ランダムデータを生成し、それを通常のグリッド上のポイントで補間します。 (入力は通常のグリッド上になく、補間されたポイントもそうである必要はないことに注意してください。)

import numpy as np
import scipy.interpolate
import matplotlib.pyplot as plt
np.random.seed(1977)

x, y, z = np.random.random((3, 10))

interp = scipy.interpolate.Rbf(x, y, z, function='thin_plate')

yi, xi = np.mgrid[0:1:100j, 0:1:100j]

zi = interp(xi, yi)

plt.plot(x, y, 'ko')
plt.imshow(zi, extent=[0, 1, 1, 0], cmap='Gist_earth')
plt.colorbar()

plt.show()

enter image description here


スプラインタイプの選択

スプラインのタイプとして"thin_plate"を選択しました。入力観測点の範囲は0から1です(これらはnp.random.randomによって作成されます)。補間された値が1をわずかに上回り、ゼロをはるかに下回ることに注意してください。これが「オーバーシュート」です。

enter image description here


線形スプラインはオーバーシュートを完全に回避しますが、「ブルズアイ」パターンになります(ただし、IDWメソッドほど深刻ではありません)。たとえば、これは線形動径基底関数で補間されたまったく同じデータです。補間された値が1を超えたり0を下回ったりすることは決してないことに注意してください。

enter image description here


高次スプラインは、データの傾向をより連続的にしますが、よりオーバーシュートします。デフォルトの"multiquadric"は薄板スプラインにかなり似ていますが、物事が少し連続的になり、オーバーシュートが少し悪化します。

enter image description here


ただし、"cubic"(3次)などのさらに高次のスプラインに移動すると、次のようになります。

enter image description here

および"quintic"(5次)

enter image description here

入力データを少しでも超えて移動するとすぐに、不合理な結果になってしまう可能性があります。


とにかく、ランダムデータのさまざまな動径基底関数を比較する簡単な例を次に示します。

import numpy as np
import scipy.interpolate
import matplotlib.pyplot as plt
np.random.seed(1977)

x, y, z = np.random.random((3, 10))
yi, xi = np.mgrid[0:1:100j, 0:1:100j]

interp_types = ['multiquadric', 'inverse', 'gaussian', 'linear', 'cubic',
                'quintic', 'thin_plate']

for kind in interp_types:
    interp = scipy.interpolate.Rbf(x, y, z, function=kind)
    zi = interp(xi, yi)

    fig, ax = plt.subplots()
    ax.plot(x, y, 'ko')
    im = ax.imshow(zi, extent=[0, 1, 1, 0], cmap='Gist_earth')
    fig.colorbar(im)
    ax.set(title=kind)
    fig.savefig(kind + '.png', dpi=80)

plt.show()
12
Joe Kington