用其前后值之间的线性插值替换 numpy 数组中的零

replace zeros in numpy array with linear interpolation between its preceding and succeeding values

假设我们有一个数组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%。没有我希望的那么好,但是由于代码将要 运行 几百万次,所以这可能是值得的。

你可以使用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 作为参数,您可以使用

您需要向 interp1d 提供一个没有 零的 y 数组 和一个跳过所述零的 x 数组。然后,对于插值,您必须为插值函数提供一个 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)

我认为您的实现有点不对劲。你想要的是更接近@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)