获取与 itertools.groupby 中的值匹配的键数
Get number of keys matching a value in itertools.groupby
我有二进制值列表,我正在尝试获取每个列表中连续 1 的组数。
这里有几个例子:
[0, 0, 0, 0, 0, 0, 0, 0] -> 0
[1, 1, 1, 1, 1, 1, 1, 1] -> 1
[0, 1, 1, 1, 1, 0, 0, 0] -> 1
[0, 1, 1, 1, 0, 0, 1, 0] -> 2
我使用 itertools.groupby() 将列表分成组,这让我得到了一个带有键和组的迭代器,但我不太清楚如何具体获取 1 组的数量.
显然,我可以遍历键并使用 if 语句进行计数,但我确信有更好的方法。
在写题的过程中,我找到了以下解决方案(回想起来很明显)。
run_count = sum(k == 1 for k, g in itertools.groupby(labels_sample))
我不确定它是否是最好的,但它确实有效。
在这种特定情况下,有键 0
和 1
,您可以省略 k == 1
检查并在总和中包含零。
sum(k for k, _ in groupby([0, 1, 1, 1, 0, 0, 1, 0]))
-> 2
不是 groupby
,但为了可能回答“更好的方法”,这似乎更快:
def count_groups_of_ones(lst):
it = iter(lst)
count = 0
while 1 in it:
count += 1
0 in it
return count
您的四个小列表的基准测试结果:
3.72 ms with_groupby
1.76 ms with_in_iterator
以及更长的列表(您的列表乘以 1000):
984.32 ms with_groupby
669.11 ms with_in_iterator
基准代码(Try it online!):
def with_groupby(lst):
return sum(k for k, _ in groupby(lst))
def with_in_iterator(lst):
it = iter(lst)
count = 0
while 1 in it:
count += 1
0 in it
return count
from timeit import repeat
from itertools import groupby
from collections import deque
from operator import itemgetter, countOf
funcs = [
with_groupby,
with_in_iterator,
]
def benchmark(lists, number):
print('lengths:', *map(len, lists))
for _ in range(3):
for func in funcs:
t = min(repeat(lambda: deque(map(func, lists), 0), number=number)) / number
print('%6.2f ms ' % (t * 1e6), func.__name__)
print()
lists = [
[0, 0, 0, 0, 0, 0, 0, 0],
[1, 1, 1, 1, 1, 1, 1, 1],
[0, 1, 1, 1, 1, 0, 0, 0],
[0, 1, 1, 1, 0, 0, 1, 0],
]
for func in funcs:
print(*map(func, lists))
benchmark(lists, 10000)
benchmark([lst * 1000 for lst in lists], 40)
另一个更通用的选项:
def count_groups(lst, value):
start = object()
return sum((a is start or a != value) and b == value for a, b in zip([start] + lst, lst))
count_groups([0, 1, 1, 1, 0, 0, 1, 0], 1) # 2
如果优化长列表的速度,请尝试调整 :
def count_groups(lst, value):
return np.diff(np.array(lst) == value, prepend=False, append=False).sum() // 2
我有二进制值列表,我正在尝试获取每个列表中连续 1 的组数。
这里有几个例子:
[0, 0, 0, 0, 0, 0, 0, 0] -> 0
[1, 1, 1, 1, 1, 1, 1, 1] -> 1
[0, 1, 1, 1, 1, 0, 0, 0] -> 1
[0, 1, 1, 1, 0, 0, 1, 0] -> 2
我使用 itertools.groupby() 将列表分成组,这让我得到了一个带有键和组的迭代器,但我不太清楚如何具体获取 1 组的数量.
显然,我可以遍历键并使用 if 语句进行计数,但我确信有更好的方法。
在写题的过程中,我找到了以下解决方案(回想起来很明显)。
run_count = sum(k == 1 for k, g in itertools.groupby(labels_sample))
我不确定它是否是最好的,但它确实有效。
在这种特定情况下,有键 0
和 1
,您可以省略 k == 1
检查并在总和中包含零。
sum(k for k, _ in groupby([0, 1, 1, 1, 0, 0, 1, 0]))
-> 2
不是 groupby
,但为了可能回答“更好的方法”,这似乎更快:
def count_groups_of_ones(lst):
it = iter(lst)
count = 0
while 1 in it:
count += 1
0 in it
return count
您的四个小列表的基准测试结果:
3.72 ms with_groupby
1.76 ms with_in_iterator
以及更长的列表(您的列表乘以 1000):
984.32 ms with_groupby
669.11 ms with_in_iterator
基准代码(Try it online!):
def with_groupby(lst):
return sum(k for k, _ in groupby(lst))
def with_in_iterator(lst):
it = iter(lst)
count = 0
while 1 in it:
count += 1
0 in it
return count
from timeit import repeat
from itertools import groupby
from collections import deque
from operator import itemgetter, countOf
funcs = [
with_groupby,
with_in_iterator,
]
def benchmark(lists, number):
print('lengths:', *map(len, lists))
for _ in range(3):
for func in funcs:
t = min(repeat(lambda: deque(map(func, lists), 0), number=number)) / number
print('%6.2f ms ' % (t * 1e6), func.__name__)
print()
lists = [
[0, 0, 0, 0, 0, 0, 0, 0],
[1, 1, 1, 1, 1, 1, 1, 1],
[0, 1, 1, 1, 1, 0, 0, 0],
[0, 1, 1, 1, 0, 0, 1, 0],
]
for func in funcs:
print(*map(func, lists))
benchmark(lists, 10000)
benchmark([lst * 1000 for lst in lists], 40)
另一个更通用的选项:
def count_groups(lst, value):
start = object()
return sum((a is start or a != value) and b == value for a, b in zip([start] + lst, lst))
count_groups([0, 1, 1, 1, 0, 0, 1, 0], 1) # 2
如果优化长列表的速度,请尝试调整
def count_groups(lst, value):
return np.diff(np.array(lst) == value, prepend=False, append=False).sum() // 2