Python 如何使用scipy执行二维插值?
本问答旨在作为关于使用scipy的二维(和多维)插值的规范(-ish)。经常会有关于各种多维插值方法的基本语法的问题,我希望也能澄清这些问题。 我有一组分散的二维数据点,我想将它们绘制成一个漂亮的曲面,最好使用Python 如何使用scipy执行二维插值?,python,scipy,interpolation,Python,Scipy,Interpolation,本问答旨在作为关于使用scipy的二维(和多维)插值的规范(-ish)。经常会有关于各种多维插值方法的基本语法的问题,我希望也能澄清这些问题。 我有一组分散的二维数据点,我想将它们绘制成一个漂亮的曲面,最好使用matplotlib.pyplot中的contourf或plot\u surface。如何使用scipy将二维或多维数据插值到网格 我找到了scipy.interpolate子包,但在使用interp2d或bisplrep或griddata或rbfipolator(或旧的Rbf)时,我总是
matplotlib.pyplot
中的contourf
或plot\u surface
。如何使用scipy将二维或多维数据插值到网格
我找到了
scipy.interpolate
子包,但在使用interp2d
或bisplrep
或griddata
或rbfipolator
(或旧的Rbf
)时,我总是会出错。这些方法的正确语法是什么?免责声明:我在写这篇文章时主要考虑了语法方面的考虑和一般行为。我不熟悉所描述的方法的内存和CPU方面,而我的目的是在那些有合理的小数据集的人,这样插值的质量可以是主要考虑的方面。我知道,在处理非常大的数据集时,性能更好的方法(即griddata
和rbfipolator
没有邻居
关键字参数)可能不可行
请注意,此答案使用中引入的新类。有关遗留类,请参见
我将比较三种多维插值方法(/样条,和)。我将使它们服从两种插值任务和两种基本函数(从中插值的点)。具体示例将演示二维插值,但可行的方法适用于任意维。每种方法都提供各种插值;在所有情况下,我都会使用三次插值(或接近1的东西)。需要注意的是,无论何时使用插值,都会引入与原始数据相比的偏差,并且使用的特定方法会影响最终产生的工件。始终注意这一点,并负责任地插入
这两个插值任务将是
[x,y]上)将
平滑友好的函数:cos(pi*x)*sin(pi*y)
;范围在[-1,1]
一个邪恶的(特别是非连续的)函数:x*y/(x^2+y^2)
,在原点附近的值为0.5;范围在[-0.5,0.5]
以下是它们的外观:
我将首先演示这三种方法在这四个测试下的行为,然后详细介绍这三种方法的语法。如果您知道应该从方法中得到什么,您可能不想浪费时间学习它的语法(看看您,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
rng = np.random.default_rng()
x_scattered, y_scattered = rng.random((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]
的网格,以实现平滑测试功能:
尽管这是一项简单的任务,但输出之间已经存在细微的差异。乍一看,这三种产出都是合理的。根据我们对底层函数的先验知识,有两个特性需要注意:中间的griddata
最扭曲数据。注意图的y==-1
边界(距离x
标签最近):函数应严格为零(因为y==-1
是平滑函数的节点线),但griddata
的情况并非如此。还要注意图的x==-1
边界(后面,左边):基础函数在[-1,-0.5]
处有一个局部最大值(意味着边界附近的零梯度),但是griddata
输出清楚地显示了该区域的非零梯度。效果是微妙的,但它仍然是一种偏见
邪恶函数与上采样
更难的任务是对邪恶函数执行上采样:
三种方法之间开始出现明显的差异。查看曲面图,在interp2d
的输出中出现明显的伪极值(请注意绘制曲面右侧的两个凸起)。而griddata
和rbf插值器
乍一看似乎产生了类似的结果,在[0.4,-0.4]
附近产生了基础函数中缺少的局部极小值
然而,rbf插值器
有一个关键的优势:它尊重基础函数的对称性(当然,样本网格的对称性也使之成为可能)。griddata
的输出破坏了采样点的对称性,而在平滑情况下,采样点的对称性已经很弱
光滑函数与散乱数据
通常需要对分散的数据执行插值。因此,我希望这些测试更加重要。如上所示,在感兴趣的域中伪均匀地选择样本点。在实际场景中,每个测量可能会有额外的噪声,并且应该考虑是否将原始数据插入到初始信息中是有意义的。
# 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
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
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)
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)