在numpy数组中使用其前一个值和后一个值之间的线性插值替换零

时间:2017-08-21 10:10:54

标签: python numpy scipy interpolation benchmarking

假设我们有一个数组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重新构建原始数组。我也尝试了this question的解决方案,但我也遇到了与该解决方案完全相同的问题。

更新

我使用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%左右。不如我希望的那么好,但由于代码将运行数百万次,所以可能值得付出努力。

3 个答案:

答案 0 :(得分:3)

你需要提供interp1d一个y-array 而不用零和一个跳过所述零的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)

答案 1 :(得分:2)

我认为您的实施有点偏差。你想要的是更接近@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)

答案 2 :(得分:1)

您可以使用pandas interpolate功能:

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