在Python列表中计算标记之间出现的次数

5

我将使用numpy数组中的布尔值来进行计数,并且想要计算在False之间出现了多少次True。

例如,对于一个样本列表:

b_List = [T,T,T,F,F,F,F,T,T,T,F,F,T,F] 

应该生成。
ml = [3,3,1]

我的初步尝试是尝试这个代码片段:

i = 0
ml = []
for el in b_List:
  if (b_List):
    i += 1
  ml.append(i)
  i = 0

但是它会为b_List中的每个F在ml中追加元素。
编辑
感谢大家的回答。不幸的是,我不能接受所有答案都是正确的。我已经接受了Akavall的答案,因为他参考了我的初始尝试(现在我知道我做错了什么),还比较了Mark和Ashwini的帖子。
请不要将被接受的解决方案视为定义答案,因为其他建议都介绍了同样有效的替代方法。

对于 NumPy 数组,你几乎肯定应该使用像 Ashwini Chaudhary 提供的向量化解决方案。 - Mark Dickinson
3个回答

5

itertools.groupby 提供了一种简单的方法来实现这一点:

>>> import itertools
>>> T, F = True, False
>>> b_List = [T,T,T,F,F,F,F,T,T,T,F,F,T,F]
>>> [len(list(group)) for value, group in itertools.groupby(b_List) if value]
[3, 3, 1]

看起来很接近,但是它应该排除第一个True值,因为它不在“Falses之间”,对吗? - bcollins
1
@bcollins:从OP给出的示例来看,似乎应该包括那些True值。 - Mark Dickinson
2
结果发现这基本上是与Alex Martelli在一个几乎重复的问题上给出的答案重复了。(除了Alex用sum(group)代替了len(list(group)),这样更简洁一些。) - Mark Dickinson

4

使用 NumPy

>>> import numpy as np
>>> a = np.array([ True,  True,  True, False, False, False, False,  True,  True, True, False, False,  True, False], dtype=bool)
>>> np.diff(np.insert(np.where(np.diff(a)==1)[0]+1, 0, 0))[::2]
array([3, 3, 1])

>>> a = np.array([True, False, False, True, True, False, False, True, False])
>>> np.diff(np.insert(np.where(np.diff(a)==1)[0]+1, 0, 0))[::2]
array([1, 2, 1])

虽然不能说这是最好的NumPy解决方案,但它仍然比itertools.groupby更快:

>>> lis = [ True,  True,  True, False, False, False, False,  True,  True, True, False, False,  True, False]*1000
>>> a = np.array(lis)
>>> %timeit [len(list(group)) for value, group in groupby(lis) if value]
100 loops, best of 3: 9.58 ms per loop
>>> %timeit np.diff(np.insert(np.where(np.diff(a)==1)[0]+1, 0, 0))[::2]
1000 loops, best of 3: 1.4 ms per loop

>>> lis = [ True,  True,  True, False, False, False, False,  True,  True, True, False, False,  True, False]*10000
>>> a = np.array(lis)
>>> %timeit [len(list(group)) for value, group in groupby(lis) if value]
1 loops, best of 3: 95.5 ms per loop
>>> %timeit np.diff(np.insert(np.where(np.diff(a)==1)[0]+1, 0, 0))[::2]
100 loops, best of 3: 14.9 ms per loop

正如 @justhalf 和 @Mark Dickinson 在评论中指出的那样,上述代码在某些情况下不起作用,因此您需要首先在两端附加 False

In [28]: a                                                                                        
Out[28]: 
array([ True,  True,  True, False, False, False, False,  True,  True,
        True, False, False,  True, False], dtype=bool)

In [29]: np.diff(np.where(np.diff(np.hstack([False, a, False])))[0])[::2]
Out[29]: array([3, 3, 1])

@MarkDickinson 我对NumPy非常陌生,所以我相信这个可以用更好的方式完成。如果你找到更好的方法,请发表回答。;-) - Ashwini Chaudhary
这可能不完全正确。如果将TrueFalse交换,np.diff(a)将给出相同的数组,但答案应该会改变(在大多数情况下)。 - justhalf
@justhalf:是的,如果序列以“False”开头,我认为这种方法不太适用。那么可以考虑使用类似这样的代码:np.diff(np.where(np.diff(np.insert(a,0,False))==1)[0])[::2] - Mark Dickinson
不行,如果最后一个是 True 的话就会失败。在我看来,正确的解决方案是在进行第一次 diff 之前,在开头和结尾都加上一个 False - Mark Dickinson
2
混淆您的答案和Alex Martelli在这个问题上的答案,我建议:np.diff(np.where(np.diff(np.hstack([False, a, False])))[0])[::2] - Mark Dickinson

2

你的原始尝试存在一些问题:


i = 0
ml = []
for el in b_List:
    if (b_List): # b_list is a list and will evaluate to True
                 # unless you have an empty list, you want if (el)
        i += 1
    ml.append(i) # even if the above line was correct you still get here
                 # on every iteration, and you don't want that
    i = 0

你可能需要类似这样的东西:
def count_Trues(b_list):
    i = 0
    ml = []
    prev = False
    for el in b_list:
        if el:
            i += 1
            prev = el
        else:
            if prev is not el:
                ml.append(i)
                i = 0
            prev = el
    if el:
        ml.append(i)
    return m

结果:

>>> T, F = True, False
>>> b_List = [T,T,T,F,F,F,F,T,T,T,F,F,T,F] 
>>> count_Trues(b_List)
[3, 3, 1]
>>> b_List.extend([T,T])
>>> count_Trues(b_List)
[3, 3, 1, 2]
>>> b_List.extend([F])
>>> count_Trues(b_List)
[3, 3, 1, 2]

这个解决方案运行得非常快:

In [5]: T, F = True, False

In [6]: b_List = [T,T,T,F,F,F,F,T,T,T,F,F,T,F] 

In [7]: new_b_List = b_List * 100

In [8]: import numpy as np

# Ashwini Chaudhary's Solution
In [9]: %timeit np.diff(np.insert(np.where(np.diff(new_b_List)==1)[0]+1, 0, 0))[::2]
1000 loops, best of 3: 299 us per loop

In [11]: %timeit count_Trues(new_b_List)
1000 loops, best of 3: 130 us per loop

In [12]: new_b_List = b_List * 1000

# Ashwini Chaudhary's Solution 
In [13]: %timeit np.diff(np.insert(np.where(np.diff(new_b_List)==1)[0]+1, 0, 0))[::2]
100 loops, best of 3: 2.25 ms per loop

In [14]: %timeit count_Trues(new_b_List)
100 loops, best of 3: 1.33 ms per loop

为了更公平的比较,你应该将 new_b_List 改为 NumPy 数组而不是 Python 列表。 - Mark Dickinson

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