7

我有两个二维数组 x(ni, nj) 和 y(ni,nj),我需要在一个轴上进行插值。我想为每个 ni 沿最后一个轴进行插值。

我写

import numpy as np
from scipy.interpolate import interp1d

z = np.asarray([200,300,400,500,600])
out = []
for i in range(ni):
    f = interp1d(x[i,:], y[i,:], kind='linear')
    out.append(f(z))
out = np.asarray(out)

但是,我认为如果数组大小太大,由于循环,这种方法效率低下且速度慢。像这样插入多维数组的最快方法是什么?有没有办法在没有循环的情况下执行线性和三次插值?谢谢。

4

2 回答 2

14

您提出的方法确实有一个 python 循环,因此对于较大的值ni它会变慢。就是说,除非你打算拥有大的,否则你ni不应该太担心。

我使用以下代码创建了示例输入数据:

def sample_data(n_i, n_j, z_shape) :
    x = np.random.rand(n_i, n_j) * 1000
    x.sort()
    x[:,0] = 0
    x[:, -1] = 1000
    y = np.random.rand(n_i, n_j)
    z = np.random.rand(*z_shape) * 1000
    return x, y, z

并用这两个版本的线性插值对它们进行了测试:

def interp_1(x, y, z) :
    rows, cols = x.shape
    out = np.empty((rows,) + z.shape, dtype=y.dtype)
    for j in xrange(rows) :
        out[j] =interp1d(x[j], y[j], kind='linear', copy=False)(z)
    return out

def interp_2(x, y, z) :
    rows, cols = x.shape
    row_idx = np.arange(rows).reshape((rows,) + (1,) * z.ndim)
    col_idx = np.argmax(x.reshape(x.shape + (1,) * z.ndim) > z, axis=1) - 1
    ret = y[row_idx, col_idx + 1] - y[row_idx, col_idx]
    ret /= x[row_idx, col_idx + 1] - x[row_idx, col_idx]
    ret *= z - x[row_idx, col_idx]
    ret += y[row_idx, col_idx]
    return ret

interp_1是您的代码的优化版本,遵循 Dave 的回答。interp_2是线性插值的矢量化实现,它避免了任何 python 循环。编写这样的代码需要对 numpy 中的广播和索引有充分的了解,而且有些事情的优化程度会低于实际interp1d情况。一个典型的例子是找到插入值的 bin:interp1d一旦找到 bin,肯定会提前跳出循环,上面的函数是将值与所有 bin 进行比较。

所以结果将非常依赖于什么n_in_j是什么,甚至你z的插值数组有多长。如果n_j是小和n_i大,您应该期望从 中获得优势,如果相反interp_2,则从 中获得优势。interp_1较小的z应该是一个优势interp_2,较长的interp_1

实际上,我已经用各种n_i和对这两种方法进行了计时n_j,对于z形状(5,)(50,),以下是图表:

在此处输入图像描述

在此处输入图像描述

因此,您似乎应该随时随地z与其他地方一起去。毫不奇怪,门槛是不同的形状,现在左右。似乎很容易得出结论,您应该坚持使用您的代码 if ,但如果不是,请将其更改为类似上面的内容,但该声明中有大量的推断!如果您想自己进一步试验,这里是我用来生成图表的代码。(5,)interp_2n_j < 1000interp_1z(50,)n_j < 100n_j * len(z) > 5000interp_2

n_s = np.logspace(1, 3.3, 25)
int_1 = np.empty((len(n_s),) * 2)
int_2 = np.empty((len(n_s),) * 2)
z_shape = (5,)

for i, n_i in enumerate(n_s) :
    print int(n_i)
    for j, n_j in enumerate(n_s) :
        x, y, z = sample_data(int(n_i), int(n_j), z_shape)
        int_1[i, j] = min(timeit.repeat('interp_1(x, y, z)',
                                        'from __main__ import interp_1, x, y, z',
                                        repeat=10, number=1))
        int_2[i, j] = min(timeit.repeat('interp_2(x, y, z)',
                                        'from __main__ import interp_2, x, y, z',
                                        repeat=10, number=1))

cs = plt.contour(n_s, n_s, np.transpose(int_1-int_2))
plt.clabel(cs, inline=1, fontsize=10)
plt.xlabel('n_i')
plt.ylabel('n_j')
plt.title('timeit(interp_2) - timeit(interp_1), z.shape=' + str(z_shape))
plt.show()
于 2013-01-28T23:05:22.667 回答
2

一种优化是像这样分配结果数组一次:

import numpy as np
from scipy.interpolate import interp1d

z = np.asarray([200,300,400,500,600])
out = np.zeros( [ni, len(z)], dtype=np.float32 ) 
for i in range(ni):
    f = interp1d(x[i,:], y[i,:], kind='linear')
    out[i,:]=f(z)

这将为您节省一些在您的实现中发生的内存复制,这发生在对out.append(...).

于 2013-01-28T18:38:14.393 回答