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



假设我们有一个1d numpy数组,其中填充了一些int值。假设其中一些是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上发现了一个类似的问题,或者非常类似的问题。我没能找到。: - (也许我错过了正确的搜索条件,对不起,那么重复。也许这只是我的想象……

以下是使用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]

构造一个与arr长度相同的数组prev,其中prev[i]arr i第1个表项之前的最后一个非零表项的索引。例如:

>>> 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])

然后用prev索引arr,就得到了结果。一个测试:

>>> 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])

注意:当数组的第一个元素为0时,要仔细理解这是怎么做的:

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

受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的代码也简洁易懂,而且似乎更快,所以我接受它:-)

如果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的字符串很长,最好查找这些字符串并将其作为块处理。但是如果大多数字符串都很短,这种重复的应用程序可能是最快的方法。

最新更新