矩形网格上的 Python 4D 线性插值

Posted

技术标签:

【中文标题】矩形网格上的 Python 4D 线性插值【英文标题】:Python 4D linear interpolation on a rectangular grid 【发布时间】:2012-12-16 16:37:37 【问题描述】:

我需要在 4 个维度(纬度、经度、高度和时间)中线性插入温度数据。 点的数量相当多(360x720x50x8),我需要一种快速的方法来计算数据范围内空间和时间任意点的温度。

我曾尝试使用scipy.interpolate.LinearNDInterpolator,但使用 Qhull 进行三角测量在矩形网格上效率低下,需要数小时才能完成。

通过阅读此SciPy ticket,解决方案似乎是使用标准interp1d 实现一个新的nd 插值器来计算更多的数据点,然后对新数据集使用“最近邻”方法。

但是,这又需要很长时间(几分钟)。

有没有一种快速的方法可以在 4 维的矩形网格上插入数据而无需花费几分钟的时间来完成?

我想过使用interp1d 4 次 而不 计算更高的点密度,但留给用户使用坐标调用,但我不知道如何这样做。

否则,我可以选择根据自己的需要编写自己的 4D 插值器吗?

这是我用来测试的代码:

使用scipy.interpolate.LinearNDInterpolator

import numpy as np
from scipy.interpolate import LinearNDInterpolator

lats = np.arange(-90,90.5,0.5)
lons = np.arange(-180,180,0.5)
alts = np.arange(1,1000,21.717)
time = np.arange(8)
data = np.random.rand(len(lats)*len(lons)*len(alts)*len(time)).reshape((len(lats),len(lons),len(alts),len(time)))

coords = np.zeros((len(lats),len(lons),len(alts),len(time),4))
coords[...,0] = lats.reshape((len(lats),1,1,1))
coords[...,1] = lons.reshape((1,len(lons),1,1))
coords[...,2] = alts.reshape((1,1,len(alts),1))
coords[...,3] = time.reshape((1,1,1,len(time)))
coords = coords.reshape((data.size,4))

interpolatedData = LinearNDInterpolator(coords,data)

使用scipy.interpolate.interp1d

import numpy as np
from scipy.interpolate import LinearNDInterpolator

lats = np.arange(-90,90.5,0.5)
lons = np.arange(-180,180,0.5)
alts = np.arange(1,1000,21.717)
time = np.arange(8)
data = np.random.rand(len(lats)*len(lons)*len(alts)*len(time)).reshape((len(lats),len(lons),len(alts),len(time)))

interpolatedData = np.array([None, None, None, None])
interpolatedData[0] = interp1d(lats,data,axis=0)
interpolatedData[1] = interp1d(lons,data,axis=1)
interpolatedData[2] = interp1d(alts,data,axis=2)
interpolatedData[3] = interp1d(time,data,axis=3)

非常感谢您的帮助!

【问题讨论】:

我对python一无所知,但你应该看看quadrilinear或quadricubic插值。 您对此有任何其他语言的想法吗?谢谢 在任何语言中实现单点的四线性插值应该相当容易。 不,他们不是。 3 个维度是恒定的(0.5、0.5、1),但高度维度并不总是在同一点采样,因此该轴上的网格不规则。 不完全是。我有关于纬度、经度、压力和时间的常规网格的温度数据。但是,我的目标是拥有一个可以这样调用的函数:getTemperature(lat,lon,ALT,time)。我所拥有的是与温度相同的网格上的 ALTITUDE 数据,因此具有相同的纬度、经度、压力、时间的规则网格。现在,我对该方法的实现会查找给定纬度、经度、时间的两个最接近高度值的压力指数。然后它使用这些高度值和两个压力指数在温度矩阵中构建 4D 超四面体并插值... 【参考方案1】:

在您链接的同一张票证中,有一个他们称之为张量积插值的示例实现,展示了嵌套递归调用interp1d 的正确方法。如果您为interp1d 选择默认的kind='linear' 参数,这相当于四线性插值。

虽然这可能已经足够好,但这不是线性插值,并且在插值函数中会有更高阶项,正如来自wikipedia entry on bilinear interpolation 的这张图片所示:

这对于您所追求的可能已经足够了,但是在某些应用程序中,三角化的、真正分段线性的插值是首选。如果你真的需要这个,有一种简单的方法可以解决 qhull 的缓慢问题。

一旦设置了LinearNDInterpolator,有两个步骤可以为给定点得出一个插值:

    找出点在哪个三角形(在您的例子中是 4D 超四面体)内,然后 使用相对于顶点的点的barycentric coordinates 作为权重进行插值。

你可能不想弄乱重心坐标,所以最好把它留给LinearNDInterpolator。但是你确实知道一些关于三角测量的事情。大多数情况下,因为你有一个规则的网格,所以在每个超立方体内,三角剖分将是相同的。因此,要插入单个值,您可以首先确定您的点在哪个子立方体中,使用该立方体的 16 个顶点构建一个 LinearNDInterpolator,并使用它来插入您的值:

from itertools import product

def interpolator(coords, data, point) :
    dims = len(point)
    indices = []
    sub_coords = []
    for j in xrange(dims) :
        idx = np.digitize([point[j]], coords[j])[0]
        indices += [[idx - 1, idx]]
        sub_coords += [coords[j][indices[-1]]]
    indices = np.array([j for j in product(*indices)])
    sub_coords = np.array([j for j in product(*sub_coords)])
    sub_data = data[list(np.swapaxes(indices, 0, 1))]
    li = LinearNDInterpolator(sub_coords, sub_data)
    return li([point])[0]

>>> point = np.array([12.3,-4.2, 500.5, 2.5])
>>> interpolator((lats, lons, alts, time), data, point)
0.386082399091

这不适用于矢量化数据,因为这需要为每个可能的子立方体存储一个LinearNDInterpolator,尽管它可能比对整个事物进行三角剖分更快,但它仍然会非常慢。

【讨论】:

如果网格随时间变化,可以使用这个吗?海拔点会随着时间的变化而变化……再次感谢您的帮助! 对符号的质疑:重心是 2d 中 3 个点、3d 中 4 个点、nd 中 n+1 个点的加权平均值;插入立方体或盒子的 4 8 ... 2^n 个角称为双线性、三线性 ... 多线性。 @Denis 你的第一个陈述是真的,第二个不是。 LinearNDInterpolator 将(超)立方体细分为不相交的(超)四面体平铺(超)立方体,是的,在对给定点进行插值时,它只会对(超)的 d + 1 顶点具有非零权重)四面体边界该点,但需要所有2**d 顶点以分段线性方式插入立方体内的任何点。除了多线性之外,还有更多使用2**d 顶点进行插值的方法,例如:hpl.hp.com/techreports/98/HPL-98-95.pdf 你不同意“插值 4 8 ... 2^n 一个立方体或盒子的角被称为bilinear, trilinear ?当然还有其他方法来加权 2^d角落,但所有 2^d 都很简单,这就是 interpn( mode="linear")map_coordinates 所做的。 @Denis 我同意双线性、三线性...使用正方形、立方体的 4、8... 顶点进行插值...但反之则不然:您可以使用 4 进行插值, 8... 一个正方形的顶点,立方体...并且没有双线性,三线性...插值。双线性、三线性可以说是最简单、最常见的方法,使用所有 4、8... 顶点来插入立方体中的每个值,但不是唯一的。【参考方案2】:

scipy.ndimage.map_coordinates 是一个很好的均匀网格快速插值器(所有盒子大小相同)。 请参阅 SO 上的multivariate-spline-interpolation-in-python-scipy 以获得清晰的描述。

对于不均匀的矩形网格,一个简单的包装器 Intergrid 映射/缩放非均匀网格到均匀网格, 然后做map_coordinates。 在像您这样的 4d 测试用例中,每个查询大约需要 1 微秒:

Intergrid: 1000000 points in a (361, 720, 47, 8) grid took 652 msec

【讨论】:

这对我来说似乎是显而易见的答案。我是否遗漏了map_coordinates 无法做到的当前接受的答案? @aaren, map_coordinates 只做统一网格;对于非均匀网格(OP 的问题),Jaime 提出了一种方法,intergrid 另一种方法。这是你的问题吗? 对不起,搞混了,我一次看的问题太多了!是的,不均匀性在这里很重要,map_coordinates 无法按原样处理。 我在非均匀网格上对 3D 数组(大小为 60*120*100)进行了一些测试,您的方法比 scipy.interpolate.interpnscipy.interpolate.RegularGridInterpolator 快大约 3 倍。跨度> @Jason,很高兴它有效,传播这个词。它现在在 PyPi,(2020 年 2 月)所以 pip install [--user] intergrid 应该可以工作。【参考方案3】:

对于非常相似的事情,我使用Scientific.Functions.Interpolation.InterpolatingFunction。

    import numpy as np
    from Scientific.Functions.Interpolation import InterpolatingFunction

    lats = np.arange(-90,90.5,0.5)
    lons = np.arange(-180,180,0.5)
    alts = np.arange(1,1000,21.717)
    time = np.arange(8)
    data = np.random.rand(len(lats)*len(lons)*len(alts)*len(time)).reshape((len(lats),len(lons),len(alts),len(time)))

    axes = (lats, lons, alts, time)
    f = InterpolatingFunction(axes, data)

您现在可以让用户使用坐标调用InterpolatingFunction

>>> f(0,0,10,3)
0.7085675631375401

InterpolatingFunction 具有很好的附加功能,例如集成和切片。

但是,我不确定插值是否是线性的。您必须查看模块源代码才能找到答案。

【讨论】:

【参考方案4】:

我打不开这个地址,找到了关于这个包的足够信息

【讨论】:

您能详细说明一下您在寻找什么包的信息吗? Scientific.Functions.Interpolation.InterpolatingFunction,大安推荐这个。我从 pypi 下载了这个包,但是包中没有任何有用的代码。请查看pypi.org/project/scientific 消息有点旧(7年前),所以链接不再有效,但看起来源在github上,可以在这里找到相同的类-github.com/khinsen/ScientificPython/blob/master/Scientific/…

以上是关于矩形网格上的 Python 4D 线性插值的主要内容,如果未能解决你的问题,请参考以下文章

二维大地电磁有限元数值模拟矩形+线性插值

使用 Akima 包线性插值:interp 用于非常不规则的网格

详解Python实现线性插值法

python线性插值解析

网格数据的快速插值

气象 python 二维线性插值