我有两个2D数组,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)
但是,我认为如果数组大小太大,这种方法由于循环而效率低且速度慢。像这样插入多维数组的最快方法是什么?有没有办法在没有循环的情况下执行线性和三次插值?感谢。
答案 0 :(得分:11)
你提出的方法确实有一个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
根据Dave的回答, interp_1
是代码的优化版本。 interp_2
是线性插值的矢量化实现,可以避免任何python循环。编写类似这样的东西需要对numpy中的广播和索引有充分的了解,而且有些事情将比interp1d
更不优化。一个主要的例子就是找到一个插入值的bin:interp1d
一旦找到bin,肯定会先摆脱循环,上面的函数是将值与所有bin进行比较。
因此,结果将非常依赖于n_i
和n_j
是什么,甚至是要插入值的数组z
的长度。如果n_j
很小且n_i
很大,那么您应该期望interp_2
和interp_1
的优势,如果相反的话。较小的z
应该是interp_2
的优势,较长的interp_1
优先。{/ p>
我实际上已经为各种n_i
和n_j
计算了两种方法,z
形状为(5,)
和(50,)
,以下是图表:
因此,对于z
形状(5,)
,您似乎应该interp_2
n_j < 1000
,interp_1
。z
。毫不奇怪,形状(50,)
的{{1}}的阈值不同,现在约为n_j < 100
。似乎很有可能得出结论,如果n_j * len(z) > 5000
你应该坚持使用你的代码,但如果不是,你应该将它改为像interp_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()
答案 1 :(得分:3)
一个优化就是像这样分配结果数组:
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(...)
的调用中。
答案 2 :(得分:0)
您是否尝试过使用scipy
中的bivariate interpolations?