假设我们有一个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
循环,而不是利用numpy
和scipy
的功能。
如果我们知道可能有相当少的连续零,我们可以使用基于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的字符串很长,最好查找这些字符串并将其作为块处理。但是如果大多数字符串都很短,这种重复的应用程序可能是最快的方法。