插值迁移指南#

此笔记本包含三组演示

注意:由于此笔记本展示了 interp2d 的用法(已标记为弃用),为了简单起见,我们将屏蔽弃用警告

import warnings
warnings.filterwarnings('ignore')

1. 如何从使用 interp2d 迁移#

interp2d 在 2D 正则网格上的插值和 2D 散点数据的插值之间无缝切换。切换基于 (展平) xyz 数组的长度。简而言之,对于正则网格,使用 scipy.interpolate.RectBivariateSpline;对于散点插值,使用 bisprep/bisplev 组合。下面我们将给出逐点迁移的示例,这应该能够完全保留 interp2d 的结果。

1.1 interp2d 在正则网格上#

我们从(略微修改的)文档字符串示例开始。

import numpy as np
import matplotlib.pyplot as plt
from scipy.interpolate import interp2d, RectBivariateSpline

x = np.arange(-5.01, 5.01, 0.25)
y = np.arange(-5.01, 7.51, 0.25)
xx, yy = np.meshgrid(x, y)
z = np.sin(xx**2 + 2.*yy**2)
f = interp2d(x, y, z, kind='cubic')

这是“规则网格”代码路径,因为

z.size == len(x) * len(y)
True

此外,请注意 x.size != y.size

x.size, y.size
(41, 51)

现在,让我们构建一个便利函数来构造插值器并绘制它。

def plot(f, xnew, ynew):
    fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(8, 4))
    znew = f(xnew, ynew)

    ax1.plot(x, z[0, :], 'ro-', xnew, znew[0, :], 'b-')

    im = ax2.imshow(znew)
    plt.colorbar(im, ax=ax2)

    plt.show()
    return znew

绘图

xnew = np.arange(-5.01, 5.01, 1e-2)
ynew = np.arange(-5.01, 7.51, 1e-2)
znew_i = plot(f, xnew, ynew)
../_images/3a7b065825f29536977bb90583ebe2955b4284ea143c43c973a45ca5aa58194c.png

替换:使用 RectBivariateSpline,结果相同#

注意转置:首先,在构造函数中,其次,您需要转置评估结果。这是为了撤消 interp2d 所做的转置。

r = RectBivariateSpline(x, y, z.T)

rt = lambda xnew, ynew: r(xnew, ynew).T
znew_r = plot(rt, xnew, ynew)
../_images/3a7b065825f29536977bb90583ebe2955b4284ea143c43c973a45ca5aa58194c.png
from numpy.testing import assert_allclose
assert_allclose(znew_i, znew_r, atol=1e-14)

1.2. interp2d 使用点的完整坐标(散点插值)#

在这里,我们展平了先前练习中的网格,以说明该功能。

xxr = xx.ravel()
yyr = yy.ravel()
zzr = z.ravel()

f = interp2d(xxr, yyr, zzr, kind='cubic')

请注意,这是“非规则网格”代码路径,用于散点数据,其中 len(x) == len(y) == len(z)

len(xxr) == len(yyr) == len(zzr)
True
xnew = np.arange(-5.01, 5.01, 1e-2)
ynew = np.arange(-5.01, 7.51, 1e-2)
znew_i = plot(f, xnew, ynew)
../_images/6b7785a23942e4663c80e24ef0d3e872b85af48794539181eef62d729af0bf7f.png

替换:直接使用 scipy.interpolate.bisplrep / scipy.interpolate.bisplev #

from scipy.interpolate import bisplrep, bisplev
tck = bisplrep(xxr, yyr, zzr, kx=3, ky=3, s=0)
# convenience: make up a callable from bisplev
ff = lambda xnew, ynew: bisplev(xnew, ynew, tck).T   # Note the transpose, to mimic what interp2d does

znew_b = plot(ff, xnew, ynew)
../_images/6b7785a23942e4663c80e24ef0d3e872b85af48794539181eef62d729af0bf7f.png
assert_allclose(znew_i, znew_b, atol=1e-15)

2. interp2d 的替代方案:规则网格#

对于新代码,推荐的替代方案是 RegularGridInterpolator。它是一个独立的实现,不基于 FITPACK。支持最近邻、线性插值和奇数阶张量积样条。

样条节点保证与数据点重合。

请注意,这里

  1. 元组参数是 (x, y)

  2. z 数组需要转置

  3. 关键词名称是method,而不是kind

  4. bounds_error 参数默认情况下为 True

from scipy.interpolate import RegularGridInterpolator as RGI

r = RGI((x, y), z.T, method='linear', bounds_error=False)

评估:创建一个二维网格。使用 indexing='ij' 和 sparse=True 来节省一些内存

xxnew, yynew = np.meshgrid(xnew, ynew, indexing='ij', sparse=True)

评估,注意元组参数

znew_reggrid = r((xxnew, yynew))
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(8, 4))

# Again, note the transpose to undo the `interp2d` convention
znew_reggrid_t = znew_reggrid.T

ax1.plot(x, z[0, :], 'ro-', xnew, znew_reggrid_t[0, :], 'b-')

im = ax2.imshow(znew_reggrid_t)
plt.colorbar(im, ax=ax2)
<matplotlib.colorbar.Colorbar at 0x7ff7d1c63610>
../_images/215cc4fd227836423e43eb2670d619492e66ba1062aa96bbc1d1575214187ac3.png

3. 散点二维线性插值:优先使用 LinearNDInterpolator 而不是 SmoothBivariateSplinebisplrep#

对于二维散点线性插值,SmoothBivariateSplinebiplrep 可能会发出警告,或者无法插值数据,或者生成具有远离数据点的节点的样条曲线。“相反,优先使用 LinearNDInterpolator,它基于通过 QHull 对数据进行三角剖分。

# TestSmoothBivariateSpline::test_integral
from scipy.interpolate import SmoothBivariateSpline, LinearNDInterpolator

x = np.array([1,1,1,2,2,2,4,4,4])
y = np.array([1,2,3,1,2,3,1,2,3])
z = np.array([0,7,8,3,4,7,1,3,4])

现在,使用基于 Qhull 三角剖分数据的线性插值

xy = np.c_[x, y]   # or just list(zip(x, y))
lut2 = LinearNDInterpolator(xy, z)

X = np.linspace(min(x), max(x))
Y = np.linspace(min(y), max(y))
X, Y = np.meshgrid(X, Y)

结果易于理解和解释

fig = plt.figure()
ax = fig.add_subplot(projection='3d')

ax.plot_wireframe(X, Y, lut2(X, Y))
ax.scatter(x, y, z,  'o', color='k', s=48)
<mpl_toolkits.mplot3d.art3d.Path3DCollection at 0x7ff7d1c3f690>
../_images/67c14437799091fbec73b82a962a6f52eca799845f54000d8c2b5e6356de45b7.png

请注意,bisplrep 做了一些不同的事情!它可能会将样条节点放置在数据之外。

为了说明,请考虑上一个示例中的相同数据

tck = bisplrep(x, y, z, kx=1, ky=1, s=0)

fig = plt.figure()
ax = fig.add_subplot(projection='3d')

xx = np.linspace(min(x), max(x))
yy = np.linspace(min(y), max(y))
X, Y = np.meshgrid(xx, yy)
Z = bisplev(xx, yy, tck)
Z = Z.reshape(*X.shape).T

ax.plot_wireframe(X, Y, Z, rstride=2, cstride=2)
ax.scatter(x, y, z,  'o', color='k', s=48)
<mpl_toolkits.mplot3d.art3d.Path3DCollection at 0x7ff7d1165750>
../_images/aa18187b13afd0e38348ece65baf852ed25c2b10f3bef63ac6cd194e2c08dc06.png

此外,SmoothBivariateSpline 无法插值数据。同样,使用上一个示例中的相同数据。

lut = SmoothBivariateSpline(x, y, z, kx=1, ky=1, s=0)

fig = plt.figure()
ax = fig.add_subplot(projection='3d')

xx = np.linspace(min(x), max(x))
yy = np.linspace(min(y), max(y))
X, Y = np.meshgrid(xx, yy)

ax.plot_wireframe(X, Y, lut(xx, yy).T, rstride=4, cstride=4)
ax.scatter(x, y, z,  'o', color='k', s=48)
<mpl_toolkits.mplot3d.art3d.Path3DCollection at 0x7ff7d1087690>
../_images/7672ec50c284cd45b388cab58941281852577dd3282dc72b1e1222797b438238.png

请注意,SmoothBivariateSplinebisplrep 的结果都存在伪影,而 LinearNDInterpolator 的结果则没有。此处说明的问题是在线性插值中报告的,但是 FITPACK 节点选择机制不能保证避免更高阶(例如三次)样条曲面的任何这些问题。