【问题标题】:Fill zero values of 1d numpy array with last non-zero values用最后一个非零值填充 1d numpy 数组的零值
【发布时间】:2015-08-09 22:08:29
【问题描述】:

假设我们有一个 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 中发现了一个问题,询问类似或非常类似的内容。我没能找到它。 :-(

也许我错过了正确的搜索词,抱歉重复了。也许这只是我的想象......

【问题讨论】:

  • 如果您不介意使用pandas,请查看ffill 方法(或查看fillna 了解完整内容)。但是,numpy 中没有内置“前向填充”类型的功能。
  • 正如@JoeKington 提到的,pandas 中的fillna 会这样做。 Cython 源是pad_2d_inplace_ 函数here,特别是底部的内循环。该代码正是您在示例中编写的代码。
  • @JoeKington 谢谢!不错的功能!我仍然更愿意避免依赖pandas 这个...

标签: python numpy


【解决方案1】:

这是使用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])

然后我们只需用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])

注意:当你的数组的第一个条目为零时,请注意理解它的作用:

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

【讨论】:

  • 您的回答可能比我的要快——除非我的就地操作可能会节省时间。
  • 我自己尝试了similar approach,但这个至少还是一样好和更快。 :) 谢谢!
【解决方案2】:

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's 也很简洁,易于理解,而且似乎更快,所以我接受它:-)

【讨论】:

  • 非常好...还有一个使用np.repeat 的潜在解决方案,但要正确设置边界似乎有点棘手。它可能也没有我们的任何一个那么快。
【解决方案3】:

如果0s 只出现在 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])

我可以通过重复应用这个直到I 为空来处理你的arr

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 的字符串很长,最好查找这些字符串并将它们作为一个块处理。但如果大多数字符串都很短,那么这种重复申请可能是最快的路线。

【讨论】:

  • 不幸的是,我确实预计可能会有很多连续的0。我想过这个问题,但 for 循环并没有说服我...... :-/
猜你喜欢
  • 1970-01-01
  • 2017-10-24
  • 2016-11-06
  • 2020-06-02
  • 1970-01-01
  • 2018-06-23
  • 2020-10-20
  • 2018-10-05
  • 1970-01-01
相关资源
最近更新 更多