如何使用最近邻居插入高维numpy python数组

时间:2014-07-07 17:10:27

标签: python arrays numpy scipy

我使用scipy和numpy在python中编程,我有一个查找数据表(LUT),我这样访问:

self.lut_data[n_iter][m_iter][l_iter][k_iter][j_iter][i_iter] 

我得到* _iter索引对应于我保存在字典中的值数组。例如,i_iter索引对应于光的波长,所以我有一个标签字典和值可以得到:

labels['wavelength']

它将返回每个i_iter对应的波长数组。如果我将它用作直接查找,这非常有用。如果我想要lut_data在500 nm。我首先在标签['波长']中找到相应的索引并使用它来索引

lut_data[][][][][][wavelength_index]

我对其他尺寸做同样的事情,包括视角等等,它们对应于其他* _iters

我需要做的是在查找表中的值之间找到值,如果我事先不知道查找表的尺寸,我需要它才能工作。如果我这样做,那么我知道如何使用每个维度的循环来解决问题。但如果我不知道LUT有多少维度,那么我就不知道要嵌套多少个循环。

我认为我应该能够使用cKDTree来做到这一点,但我无法理解如何使它工作。我真的很感激一个看起来类似于我的结构的例子

由于

2 个答案:

答案 0 :(得分:1)

如果要从中插入完整的信息数组,则线性插值并不困难。它只是稍微耗费时间,但是如果你可以将你的阵列安装在RAM中,那只需几秒钟。

技巧是线性插值可以一次完成一个轴。所以,对于每个轴:

  • 找到最近的插值点
  • 找到这些点之间的相对距离( d = 0..1),例如如果您有540和550 nm,并且您希望数据为548 nm, d = 0.8。
  • 对所有轴重复此过程;每轮将维度数减少一个

像这样:

import numpy as np

def ndim_interp(A, ranges, p):
    # A: array with n dimensions
    # ranges: list of n lists or numpy arrays of values along each dimension
    # p: vector of values to find (n elements)

    # iterate through all dimensions
    for i in range(A.ndim):
        # check if we are overrange; if we are, use the edgemost values
        if p[i] <= ranges[i][0]:
            A = A[0]
            continue
        if p[i] >= ranges[i][-1]:
            A = A[-1]
            continue

        # find the nearest values
        right = np.searchsorted(ranges[i], p[i])
        left = right - 1

        # find the relative distance
        d = (p[i] - ranges[i][left]) / (ranges[i][right] - ranges[i][left])

        # calculate the interpolation
        A = (1 - d) * A[left] + d * A[right]            

    return A

举个例子:

# data axis points
arng = [1, 2, 3]
brng = [100, 200]
crng = [540, 550, 560]

# some data
A = np.array([
    [[1., 2., 3.], [2., 3., 4.]],
    [[0.5, 1.5, 2.], [1.5, 2.0, 3.0]],
    [[0., 0.5, 1.], [1., 1., 1.]]])

# lookup:
print ndim_interp(A, (arng, brng, crng), (2.3, 130., 542.))

如果你想做一些更复杂的事情(三次样条等),那么你可以使用scipy.ndimage.interpolation.map_coordinates。然后配方改变如下:

import numpy as np
import scipy.ndimage.interpolation

def ndim_interp(A, ranges, p):
    # A: array with n dimensions
    # ranges: list of n lists or numpy arrays of values along each dimension
    # p: vector of values to find (n elements)

    # calculate the coordinates into array positions in each direction
    p_arr = []
    # iterate through all dimensions
    for i in range(A.ndim):
        # check if we are overrange; if we are, use the edgemost values
        if p[i] <= ranges[i][0]:
            p_arr.append(0)
            continue
        if p[i] >= ranges[i][-1]:
            p_arr.append(A.shape[i] - 1)
            continue

        # find the nearest values to the left
        right = np.searchsorted(ranges[i], p[i])
        left = right - 1

        # find the relative distance
        d = (p[i] - ranges[i][left]) / (ranges[i][right] - ranges[i][left])

        # append the position
        p_arr.append(left + d)

    coords = np.array(p_arr).reshape(A.ndim, -1)
    return scipy.ndimage.interpolation.map_coordinates(A, coords, order=1, mode='nearest')[0]

当然,使用最简单的设置(order=1等于线性插值)没有意义,但即使是三次样条,编写自己的插值算法也不是那么简单。

当然,如果你的网格在所有方向上都是等间隔的,那么代码就更简单了,因为不需要先插入正确的位置(一个简单的除法就可以了)。

答案 1 :(得分:1)

scipy.interpolate.RegularGridInterpolator对于这个问题非常有用。虽然它仅适用于Scipy 0.14(截至目前的最新版本)。

如果你有*_iter个变量,你可以这样做:

from scipy.interpolate import RegularGridInterpolator

points = tuple([n_iter, m_iter, l_iter, k_iter, j_iter, i_iter])
interpolator = RegularGridInterpolator(points, lut_data, method='nearest')

或者您可以从词典中获取points

keys = ['k1', 'k2', 'k3', 'k4', 'k5', 'wavelength']
points = tuple([labels[key] for key in keys])

如果您有插值器,则可以使用其__call__方法进行插值。这基本上意味着您可以将您创建的类实例作为函数调用:

point_of interest = tuple([x1, x2, x3, x4, x5, some_wavelength])
interp_value = interpolator(point_of_interest)

插补器还允许一次插入多个值(即通过Numpy点阵列),如果您的代码需要这个值,这可能非常有效。