使用最后的非零值填充1d numpy数组的零值

时间:2015-05-27 17:07:46

标签: python numpy

我们假设我们有一个填充了int个值的1d numpy数组。并且让我们说其中一些是0

有没有办法,使用numpy数组的幂,用找到的最后一个非零值填充所有0值?

例如:

arr = np.array([1, 0, 0, 2, 0, 4, 6, 8, 0, 0, 0, 0, 2])
fill_zeros_with_last(arr)
print arr

[1 1 1 2 2 4 6 8 8 8 8 8 2]

一种方法是使用此功能:

def fill_zeros_with_last(arr):
    last_val = None # I don't really care about the initial value
    for i in range(arr.size):
        if arr[i]:
            last_val = arr[i]
        elif last_val is not None:
            arr[i] = last_val

但是,这是使用原始python for循环而不是利用numpyscipy次幂。

如果我们知道可以使用相当少量的连续零,我们可以使用基于numpy.roll的内容。问题是连续零的数量可能很大......

有什么想法吗?或者我们应该直接进入Cython

声明:

我会说很久以前我在stackoverflow中发现了一个问题,问这样或类似的东西。我无法找到它。 : - (

也许我错过了正确的搜索字词,对不起那些副本。也许这只是我的想象......

3 个答案:

答案 0 :(得分:16)

以下是使用np.maximum.accumulate

的解决方案
def fill_zeros_with_last(arr):
    prev = np.arange(len(arr))
    prev[arr == 0] = 0
    prev = np.maximum.accumulate(prev)
    return arr[prev]

我们构造一个数组prev,其长度与arr相同,并且prev[i] i之前的最后一个非零条目的索引 arr的条目。例如,如果:

>>> arr = np.array([1, 0, 0, 2, 0, 4, 6, 8, 0, 0, 0, 0, 2])

然后prev看起来像:

array([ 0,  0,  0,  3,  3,  5,  6,  7,  7,  7,  7,  7, 12])

然后我们只需使用arr索引prev,我们就可以获得结果。测试:

>>> arr = np.array([1, 0, 0, 2, 0, 4, 6, 8, 0, 0, 0, 0, 2])
>>> fill_zeros_with_last(arr)
array([1, 1, 1, 2, 2, 4, 6, 8, 8, 8, 8, 8, 2])

注意:当数组的第一个条目为零时,请小心理解这是做什么的:

>>> fill_zeros_with_last(np.array([0,0,1,0,0]))
array([0, 0, 1, 1, 1])

答案 1 :(得分:4)

受到jme的回答以及Bas Swinckels'的启发(在链接的问题中),我想出了一个不同的numpy函数组合:

def fill_zeros_with_last(arr, initial=0):
     ind = np.nonzero(arr)[0]
     cnt = np.cumsum(np.array(arr, dtype=bool))
     return np.where(cnt, arr[ind[cnt-1]], initial)

我认为它简洁而且有效,所以我将它张贴在这里作为记录。尽管如此,jme还简洁易懂,似乎更快,所以我接受它: - )

答案 2 :(得分:1)

如果0仅以1为字符串,则nonzero的使用可能有效:

In [266]: arr=np.array([1,0,2,3,0,4,0,5])
In [267]: I=np.nonzero(arr==0)[0]
In [268]: arr[I] = arr[I-1]
In [269]: arr
Out[269]: array([1, 1, 2, 3, 3, 4, 4, 5])

我可以反复应用此arr,直到I为空。

In [286]: arr = np.array([1, 0, 0, 2, 0, 4, 6, 8, 0, 0, 0, 0, 2])

In [287]: while True:
   .....:     I=np.nonzero(arr==0)[0]
   .....:     if len(I)==0: break
   .....:     arr[I] = arr[I-1]
   .....:     

In [288]: arr
Out[288]: array([1, 1, 1, 2, 2, 4, 6, 8, 8, 8, 8, 8, 2])

如果0的字符串很长,那么查找这些字符串并将它们作为块处理可能会更好。但如果大多数字符串都很短,那么重复应用可能是最快的路径。