将 Numpy 数组中的零替换为其前面和后续值之间的线性插值



假设我们有一个数组a = np.array([1,2,0,4,0,5,0,0,11]),我们怎么能得到:

array([ 1,  2,  3,  4,  4.5,  5,  7,  9, 11])

我尝试的是:

from scipy.interpolate import interp1d
a = np.array([1,2,0,4,0,5,0,0,11])
b = a[np.nonzero(a)]
brange = np.arange(b.shape[0])
interp = interp1d(brange, b)

这似乎完成了查找中间值的实际工作。例如:

print (interp(1), interp(1.5), interp(2), interp(2.5), interp(3))
#out: 2.0 3.0 4.0 4.5 5.0

但是我不知道如何从interp重建我的原始数组。我也尝试了这个问题的解决方案,但我对该解决方案也有完全相同的问题。

更新:

我使用 numpy 和 pandas 对这两种解决方案进行了快速基准测试,结果如下:

y = np.array([1,2,0,4,0,5,0,0,11])
def test1(y):
x = np.arange(len(y))
idx = np.nonzero(y)
interp = interp1d(x[idx],y[idx])
return interp(x)
def test2(y):
s = pd.Series(y)
s.interpolate(inplace=True)
return s.values
%timeit t1 = test1(y)
%timeit t2 = test2(y)
139 µs ± 1.62 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
158 µs ± 2.01 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

速度提高约 12%。没有我希望的那么好,但由于代码将运行数百万次,因此可能值得付出努力。

你需要interp1d馈送一个没有零的 y 数组和一个跳过所述零的 x 数组。然后,对于插值,您必须为插值函数提供一个 x 数组,该数组包含所有原始 x 值以及您希望发生插值的值。在您的情况下,由于您有一个现成的、等间距的向量,您只需使用np.arange来生成 x 值和用于过滤掉零的np.where

下面是一个示例代码:

import numpy as np
from scipy.interpolate import interp1d
y = np.array([1,2,0,4,0,5,0,0,11])
xnew = np.arange(len(y))
zero_idx = np.where(y==0)
xold = np.delete(xnew,zero_idx)
yold = np.delete(y, zero_idx)
print('before')
print(xold)
print(yold)
f = interp1d(xold,yold)
ynew = f(xnew)
print()
print('after')
print(xnew)
print(ynew)

结果如下所示:

before
[0 1 3 5 8]
[ 1  2  4  5 11]
after
[0 1 2 3 4 5 6 7 8]
[  1.    2.    3.    4.    4.5   5.    7.    9.   11. ]

编辑

其实你不需要np.delete,你只需要使用切片:

y = np.array([1,2,0,4,0,5,0,0,11])
x = np.arange(len(y))
idx = np.where(y!=0)        #or np.nonzero(y) -- thanks DanielF
f = interp1d(x[idx],y[idx])
ynew = f(x)

您可以使用pandasinterpolate函数:

import pandas as pd
import numpy as np
a = pd.Series([1,2,0,4,0,5,0,0,11])
a.replace(0, np.NaN, inplace=True)
a.interpolate()
0     1.0
1     2.0
2     3.0
3     4.0
4     4.5
5     5.0
6     7.0
7     9.0
8    11.0

另外:a.interpolate().values会给你值数组。

# output: array([  1. ,   2. ,   3. ,   4. ,   4.5,   5. ,   7. ,   9. ,  11. ])

另外:interpolateinplace作为您可以使用的参数

我认为你的实现有点不对劲。 你想要的是更接近@Thomas想出的东西:

y = np.array([1,2,0,4,0,5,0,0,11])
idx = np.nonzero(y)
interp = interp1d(x[idx],y[idx])
x = np.arange(len(y))
ynew = interp(x)

如果你想从interp重建你的原始数组,你只需要使用.x.y参数。

a_ = np.zeros(interp.x[-1] + 1)
a_[interp.x] = interp.y

当然,这将删除原始a中的任何尾随零,因为插值中不会保留a.size。 如果将它们保留在其他地方(例如ynew.shape(,则可以改为初始化a_ = np.zeros_like(ynew)

最新更新