Numpy:如何在numpy数组的每一列中找到第一个非零值?

Mel*_*uce 20 python numpy

假设我有一个numpy数组的形式:

arr=numpy.array([[1,1,0],[1,1,0],[0,0,1],[0,0,0]])
Run Code Online (Sandbox Code Playgroud)

我想找到值为非零的第一个索引(对于每一列)的索引.

所以在这个例子中,我希望返回以下内容:

[0,0,2]
Run Code Online (Sandbox Code Playgroud)

我该怎么做?

Div*_*kar 44

首次出现的指数

np.argmax在非零的掩码上使用该轴(此处为列的零轴)来获取第一个matches(真值)的索引-

(arr!=0).argmax(axis=0)
Run Code Online (Sandbox Code Playgroud)

扩展到覆盖通用轴说明符,并且对于元素沿着该轴没有找到非零的情况,我们将有这样的实现 -

def first_nonzero(arr, axis, invalid_val=-1):
    mask = arr!=0
    return np.where(mask.any(axis=axis), mask.argmax(axis=axis), invalid_val)
Run Code Online (Sandbox Code Playgroud)

请注意,因为argmax()所有False值都返回0,所以如果invalid_val需要0,我们将直接使用最终输出mask.argmax(axis=axis).

样品运行 -

In [296]: arr    # Different from given sample for variety
Out[296]: 
array([[1, 0, 0],
       [1, 1, 0],
       [0, 1, 0],
       [0, 0, 0]])

In [297]: first_nonzero(arr, axis=0, invalid_val=-1)
Out[297]: array([ 0,  1, -1])

In [298]: first_nonzero(arr, axis=1, invalid_val=-1)
Out[298]: array([ 0,  0,  1, -1])
Run Code Online (Sandbox Code Playgroud)

扩展以涵盖所有比较操作

为了找到第一zeros,简单地使用arr==0作为mask在功能使用.对于等于某个值的第一个val,arr == val对于所有comparisons可能的情况,请使用等等.


最后一次出现的指数

为了找到最后的匹配某比较标准,我们需要沿该轴翻转,并使用使用的同样的想法argmax,然后通过从轴线长度偏移补偿翻转,如下所示-

def last_nonzero(arr, axis, invalid_val=-1):
    mask = arr!=0
    val = arr.shape[axis] - np.flip(mask, axis=axis).argmax(axis=axis) - 1
    return np.where(mask.any(axis=axis), val, invalid_val)
Run Code Online (Sandbox Code Playgroud)

样品运行 -

In [320]: arr
Out[320]: 
array([[1, 0, 0],
       [1, 1, 0],
       [0, 1, 0],
       [0, 0, 0]])

In [321]: last_nonzero(arr, axis=0, invalid_val=-1)
Out[321]: array([ 1,  2, -1])

In [322]: last_nonzero(arr, axis=1, invalid_val=-1)
Out[322]: array([ 0,  1,  1, -1])
Run Code Online (Sandbox Code Playgroud)

同样,comparisons通过使用相应的比较器来获取mask然后在列出的函数中使用,可以覆盖所有可能的情况.


Mar*_*coP 6

这个问题显然是二维的,可以通过对每一行应用一个找到第一个非零元素的函数来解决(与问题中完全相同)。

arr = np.array([[1,1,0],[1,1,0],[0,0,1],[0,0,0]])

def first_nonzero_index(array):
    """Return the index of the first non-zero element of array. If all elements are zero, return -1."""
    
    fnzi = -1 # first non-zero index
    indices = np.flatnonzero(array)
       
    if (len(indices) > 0):
        fnzi = indices[0]
        
    return fnzi

np.apply_along_axis(first_nonzero_index, axis=1, arr=arr)

# result
array([ 0,  0,  2, -1])
Run Code Online (Sandbox Code Playgroud)

解释

np.flatnonzero (array)方法(如 Henrik Koberg 的评论中所建议的)返回“在数组的扁平版本中非零的索引”。该函数计算这些索引并返回第一个(如果所有元素均为零,则返回 -1)。

apply_along_axis函数应用于沿给定轴的一维切片。这里,由于轴为 1,因此该函数应用于行。

如果我们可以假设输入数组的所有行都至少包含一个非零元素,则解决方案可以写成一行计算:

np.apply_along_axis(lambda a: np.flatnonzero(a)[0], axis=1, arr=arr)
Run Code Online (Sandbox Code Playgroud)

可能的变化

  • 如果我们对最后一个非零元素感兴趣,可以通过将函数中的索引[0]更改为索引[-1]来获得。
  • 为了按行获得第一个非零值,我们将 np.apply_along_axis 中的 axis=1 更改为 axis=0

原答案

这是一种替代方法numpy.argwhere,它返回数组非零元素的索引:

np.apply_along_axis(lambda a: np.flatnonzero(a)[0], axis=1, arr=arr)
Run Code Online (Sandbox Code Playgroud)

给出:

array = np.array([0,0,0,1,2,3,0,0])

nonzero_indx = np.argwhere(array).squeeze()
start, end = (nonzero_indx[0], nonzero_indx[-1])
print(array[start], array[end])

Run Code Online (Sandbox Code Playgroud)