Python中的二维插值(环形插值)

5

我有一个角度数据,其域被包裹在 pi 弧度处 (即 0 = pi)。这些数据是二维的,其中一个维度表示角度。我需要以包裹的方式将这些数据插值到另一个网格中。

在一个维度上,np.interp 函数使用周期性的 kwarg 参数 (适用于 NumPy 1.10 及更高版本): http://docs.scipy.org/doc/numpy/reference/generated/numpy.interp.html

这正是我所需要的,但我需要它在二维中实现。我目前只是遍历数组中的列,并使用 np.interp,但这当然很慢。

有没有什么方法可以更快地实现这个目标?


你有研究过scipy.interpolate.interp2d吗? - James
1
是的,而且没有类似于句号的实现。你有具体的解决方法吗? - S E Clark
插值数据。创建一个函数,将φ和r的值映射到您的数据集中。 - James
1个回答

2

np.interp的工作原理解释

使用源代码,Luke!

np.interp的numpy文档使得在文档中查找源代码变得特别容易,因为它直接提供了链接和文档。让我们逐行分析这个函数。

首先,回忆一下参数:

"""
x : array_like
    The x-coordinates of the interpolated values.
xp : 1-D sequence of floats
    The x-coordinates of the data points, must be increasing if argument
    `period` is not specified. Otherwise, `xp` is internally sorted after
    normalizing the periodic boundaries with ``xp = xp % period``.
fp : 1-D sequence of floats
    The y-coordinates of the data points, same length as `xp`.
period : None or float, optional
    A period for the x-coordinates. This parameter allows the proper
    interpolation of angular x-coordinates. Parameters `left` and `right`
    are ignored if `period` is specified.
"""

在阅读本文时,让我们以三角波为例进行简单说明:

xp = np.array([-np.pi/2, -np.pi/4, 0, np.pi/4])
fp = np.array([0, -1, 0, 1])
x = np.array([-np.pi/8, -5*np.pi/8])  # Peskiest points possible }:)
period = np.pi

现在,在所有类型检查之后,我从源代码的 period != None 分支开始:
# normalizing periodic boundaries
x = x % period
xp = xp % period

这只是确保提供的所有xxp值都在0period之间。因此,由于周期为pi,但我们指定xxp-pi/2pi/2之间,这将通过将[-pi/2, 0)范围内的所有值加上pi来进行调整,以便它们实际上出现在pi/2之后。因此,我们的xp现在变为[pi/2, 3*pi/4, 0, pi/4]

asort_xp = np.argsort(xp)
xp = xp[asort_xp]
fp = fp[asort_xp]

这只是按递增顺序排序xp的过程。在执行前一步骤中的模运算后,特别需要进行此操作。因此,现在xp[0,pi/4,pi/2,3 * pi / 4]fp也相应地被洗牌, [0,1,0,-1]
xp = np.concatenate((xp[-1:]-period, xp, xp[0:1]+period))
fp = np.concatenate((fp[-1:], fp, fp[0:1]))
return compiled_interp(x, xp, fp, left, right)   # Paraphrasing a little

np.interp 执行线性插值。当尝试在 xp 中寻找两个点 ab 之间的插值时,它仅使用相应索引处的 fp 值,即 f(a)f(b) 的值。因此,np.interp 在最后一步所做的是将点 xp[-1] 放在数组前面,并将点 xp[0] 放在数组后面,但分别减去和加上一个周期。因此,您现在有一个新的 xp,看起来像这样:[-pi/4, 0, pi/4, pi/2, 3*pi/4, pi]。同样,fp[0]fp[-1] 已经连接在一起,因此 fp 现在为 [-1, 0, 1, 0, -1, 0]

请注意,在模运算之后,x也已经被带入了[0, pi]范围内,因此x现在是[7*pi/8, 3*pi/8]。这让你很容易地看出你将得到[-0.5, 0.5]

现在,针对你的二维情况:

假设你有一个网格和一些数值。让我们先把所有的数值都限制在[0, pi]之间,这样我们就不需要担心取模和混淆了。

xp = np.array([0, np.pi/4, np.pi/2, 3*np.pi/4])
yp = np.array([0, 1, 2, 3])
period = np.pi

# Put x on the 1st dim and y on the 2nd dim; f is linear in y
fp = np.array([0, 1, 0, -1])[:, np.newaxis] + yp[np.newaxis, :] 
# >>> fp
# array([[ 0,  1,  2,  3],
#        [ 1,  2,  3,  4],
#        [ 0,  1,  2,  3],
#        [-1,  0,  1,  2]])

我们现在知道,你只需要在数组前面添加xp[[-1]],在末尾添加xp[[0]],并调整周期即可。请注意,我使用了单例列表[-1][0]进行索引。这是一个技巧,可以确保维度被保留
xp = np.concatenate((xp[[-1]]-period, xp, xp[[0]]+period))
fp = np.concatenate((fp[[-1], :], fp, fp[[0], :]))

最后,您可以自由使用scipy.interpolate.interpn来实现您的结果。让我们获取所有yx = pi/8处的值:
from scipy.interpolate import interpn
interp_points = np.hstack(( (np.pi/8 * np.ones(4))[:, np.newaxis], yp[:, np.newaxis] ))
result = interpn((xp, yp), fp, interp_points)
# >>> result
# array([ 0.5,  1.5,  2.5,  3.5])
interp_points 必须指定为一个Nx2的点矩阵,其中第一维表示您想要插值的每个点,第二维给出该点的x和y坐标。有关详细说明,请参见this answer
如果您想获得范围[0, period]之外的值,则需要自行进行模数运算:
x = 21 * np.pi / 8
x_equiv = x % period   # Now within [0, period]
interp_points = np.hstack(( (x_equiv * np.ones(4))[:, np.newaxis], yp[:, np.newaxis] ))
result = interpn((xp, yp), fp, interp_points)
# >>> result
# array([-0.5,  0.5,  1.5,  2.5])

如果您想为一堆x和y值生成interp_points,请参考这个答案


PS:代码中可能有一些错别字,我会尽快检查并更正。 - Praveen
谢谢。关于将包装值连接到数据上的观点很好,我可能可以从中找出答案。但为了未来的插值器着想,您是否考虑清理一下,使其包含可工作的代码、测试相关示例(插值到[0, pi)之外的值等)? - S E Clark

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接