在Python中计算非零值的聚类数?

9

我的数据大致长这样:

a=[0,0,0,0,0,0,10,15,16,12,11,9,10,0,0,0,0,0,6,9,3,7,5,4,0,0,0,0,0,0,4,3,9,7,1]

本质上,非零数字之前有一堆零,我要计算通过零分开的非零数字组的数量。在上面的示例数据中,有3组非零数据,因此代码应该返回3。

  • 非零数之间的零的数量是可变的

有没有在Python中实现这个功能的好方法?(还可以使用Pandas和Numpy来帮助解析数据)


如果你有一个系列(或数据框),可以这样做:((ser!=0)&(ser.shift()==0)).sum() - JohnE
5个回答

8

如果我们有一个输入数组a,我们可以使用向量化的解决方案-

m = a!=0
out = (m[1:] > m[:-1]).sum() + m[0]

为了获得更好的性能,我们可以使用 np.count_nonzero。这个函数非常高效,可以非常快速地计算布尔值的数量,就像下面这样 -

out = np.count_nonzero(m[1:] > m[:-1]) + m[0] 

基本上,我们得到一个非零掩码并计算上升沿的数量。为了考虑第一个元素也可能是非零的情况,并且不会有任何上升沿,我们需要检查它并将其添加到总和中。

此外,请注意,如果输入的 a 是一个列表,我们需要使用 m = np.asarray(a)!=0

三个案例的示例运行 -

In [92]: a  # Case1 :Given sample
Out[92]: 
array([ 0,  0,  0,  0,  0,  0, 10, 15, 16, 12, 11,  9, 10,  0,  0,  0,  0,
        0,  6,  9,  3,  7,  5,  4,  0,  0,  0,  0,  0,  0,  4,  3,  9,  7,
        1])

In [93]: m = a!=0

In [94]: (m[1:] > m[:-1]).sum() + m[0]
Out[94]: 3

In [95]: a[0] = 7  # Case2 :Add a non-zero elem/group at the start

In [96]: m = a!=0

In [97]: (m[1:] > m[:-1]).sum() + m[0]
Out[97]: 4

In [99]: a[-2:] = [0,4] # Case3 :Add a non-zero group at the end

In [100]: m = a!=0

In [101]: (m[1:] > m[:-1]).sum() + m[0]
Out[101]: 5

4

您可以通过使用 itertools.groupby()列表推导式 表达式来实现:

>>> from itertools import groupby

>>> len([is_true for is_true, _ in groupby(a, lambda x: x!=0) if is_true])
3

如果使用的可迭代对象不容易转换为Numpy数组,那么这很好。 - David Z
不,我认为楼主不会有这样的情况。 - Divakar
哦,我指的是一般的“groupby”方法 - 实际上我甚至没有注意到你的错误。无论如何,我的观点是,既然问题涉及到了Numpy可用的情况下,向量化的解决方案更好,但这也是其他情况下值得知道的一个好方法。 - David Z

3

这是一个简单的Python解决方案,只需计算从0到非零的变化,通过跟踪先前的值(上升沿检测):

a=[0,0,0,0,0,0,10,15,16,12,11,9,10,0,0,0,0,0,6,9,3,7,5,4,0,0,0,0,0,0,4,3,9,7,1]

previous = 0
count = 0
for c in a:
    if previous==0 and c!=0:
        count+=1
    previous = c

print(count)  # 3

2
  • 使用np.concatenate在数组两侧填充零
  • 使用a == 0查找零的位置
  • 使用np.diff查找边界
  • 使用sum将找到的边界相加
  • 除以二,因为我们会找到比我们想要的多两倍

def nonzero_clusters(a):
    return int(np.diff(np.concatenate([[0], a, [0]]) == 0).sum() / 2)

演示

nonzero_clusters(
    [0,0,0,0,0,0,10,15,16,12,11,9,10,0,0,0,0,0,6,9,3,7,5,4,0,0,0,0,0,0,4,3,9,7,1]
)

3

nonzero_clusters([0, 1, 2, 0, 1, 2])

2

nonzero_clusters([0, 1, 2, 0, 1, 2, 0])

2

nonzero_clusters([1, 2, 0, 1, 2, 0, 1, 2])

3

时间控制
a = np.random.choice((0, 1), 100000)
代码

from itertools import groupby

def div(a):
    m = a != 0
    return (m[1:] > m[:-1]).sum() + m[0]

def pir(a):
    return int(np.diff(np.concatenate([[0], a, [0]]) == 0).sum() / 2)

def jean(a):
    previous = 0
    count = 0
    for c in a:
        if previous==0 and c!=0:
            count+=1
        previous = c
    return count

def moin(a):
    return len([is_true for is_true, _ in groupby(a, lambda x: x!=0) if is_true])

def user(a):
    return sum([1 for n in range (len (a) - 1) if not a[n] and a[n + 1]])

enter image description here


1
sum ([1 for n in range (len (a) - 1) if not a[n] and a[n + 1]])

如果第一个元素非零怎么办? - Divakar
@Divakar "本质上,非零数之前有一堆零" 这就是 OP 说的。 - user7342539

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接