如何有效地统计python列表列表中的共现

如何有效地统计python列表列表中的共现,python,counter,Python,Counter,我有一个相对较大的子列表(~3GB,300多万个条目),其中每个子列表包含一组标记。下面是一个非常简单的示例: tag_corpus = [['cat', 'fish'], ['cat'], ['fish', 'dog', 'cat']] unique_tags = ['dog', 'cat', 'fish'] co_occurences = {key:Counter() for key in unique_tags} for tags in tag_corpus: tall

我有一个相对较大的子列表(~3GB,300多万个条目),其中每个子列表包含一组标记。下面是一个非常简单的示例:

tag_corpus = [['cat', 'fish'], ['cat'], ['fish', 'dog', 'cat']]  

unique_tags = ['dog', 'cat', 'fish'] 
co_occurences = {key:Counter() for key in unique_tags}

for tags in tag_corpus: 
    tallies = Counter(tags)
    for key in tags: 
        co_occurences[key] = co_occurences[key] + tallies
这有点像charm,但在实际数据集上速度非常慢,它有非常大的子列表(总共约30K个唯一标记)。任何python专业人士都知道我如何加速这件事吗

这可能会更快。你得测量一下

from collections import Counter
from collections import defaultdict

tag_corpus = [['cat', 'fish'], ['cat'], ['fish', 'dog', 'cat']]

co_occurences = defaultdict(Counter)
for tags in tag_corpus:
    for key in tags:
        co_occurences[key].update(tags)
unique_tags = sorted(co_occurences)

print co_occurences
print unique_tags

我只是在胡思乱想,没想到会有更高效的结果,但对于100000只猫、狗和鱼,这要快得多,计时时间是0.07秒,而不是1.25秒

我试图以一个更短的解决方案结束,但结果证明这种方法是最快的,即使它看起来非常简单:)


您可以尝试结合defaultdict,以避免在开始时使用Peters answer中的逻辑进行初始化,运行时将大大加快:

In [35]: %%timeit
co_occurences = defaultdict(Counter)
for tags in tag_corpus:
    for key in tags:
        co_occurences[key].update(tags)
   ....: 

1 loop, best of 3: 513 ms per loop

In [36]: %%timeit
occurances = {k1: defaultdict(int) for k1 in unique_tags}
for tags in tag_corpus:
    for key in tags:
        for key2 in tags:
            occurances[key][key2] += 1
   ....: 
10 loops, best of 3: 65.7 ms per loop

In [37]: %%timeit
   ....: co_occurences = {key:Counter() for key in unique_tags}
   ....: for tags in tag_corpus: 
   ....:     tallies = Counter(tags)
   ....:     for key in tags: 
   ....:         co_occurences[key] = co_occurences[key] + tallies
   ....: 
 1 loop, best of 3: 1.13 s per loop
    In [38]: %%timeit
   ....: occurances = defaultdict(lambda: defaultdict(int))
   ....: for tags in tag_corpus:
   ....:     for key in tags:
   ....:         for key2 in tags:
   ....:             occurances[key][key2] += 1
   ....: 
10 loops, best of 3: 66.5 ms per loop
至少在python2中,计数器dict实际上并不是获取计数的最快方法,然而,defaultdict即使使用lambda也很快

即使滚动您自己的计数功能也会更快:

def count(x):
    d = defaultdict(int)
    for ele in x:
        d[ele] += 1
    return d 
没有最快的速度快,但仍然很好:

In [42]: %%timeit
   ....: co_occurences = {key: defaultdict(int) for key in unique_tags}
   ....: for tags in tag_corpus:
   ....:     tallies = count(tags)
   ....:     for key in tags:
   ....:         for k, v in tallies.items():
   ....:             co_occurences[key][k] += v
   ....: 

10 loops, best of 3: 164 ms per loop

如果你想要更多的加速,一点cython可能会有很大的帮助。

作为第一个近似值,尝试使用bruteforce并将第一个forloop替换为。请注意,在这种情况下,您希望每个列表都有一个计数器,而不是一个全局计数器。此外,您可能希望尝试
line\u profiler
以查看这两个块中的哪个块(
Counter
call或
co\u occurrences
update更昂贵)。您为什么要排序以及python的哪个版本?@padraiccnningham:good point,排序是不必要的。这只是我之前采取的方法的产物。2.7@SergeiLebedev:好主意。这将使计算时间减少大约作业使用的CPU数量,对吗?谢谢!有趣的解决方案,但问题是初始化数量为零的大型dict会完全破坏内存占用。我想对于小数据来说效果不错。啊,我不知道你有很多不同的标签,比如宠物,我以为你有3百万条类似标签的记录啊哈。不管怎样,如果你想再试一次,我希望已经解决了这个问题。我用
defaultdict
(刚刚注意到Padraic提到了它),但它还是太慢了,一次简单的尝试/例外似乎效果不错。太棒了,我会继续玩这个,我期待更多。如果我有其他解决方案,我会让你们知道的。快多了。干得好,罗伯。这就是我现在需要的。有没有什么洞察到为什么它能这么好地工作?太棒了,我会继续玩下去。如果我有其他解决方案,我会告诉你们的。我还没有和cython一起玩过,但这样做让我觉得这可能是个好主意。
In [42]: %%timeit
   ....: co_occurences = {key: defaultdict(int) for key in unique_tags}
   ....: for tags in tag_corpus:
   ....:     tallies = count(tags)
   ....:     for key in tags:
   ....:         for k, v in tallies.items():
   ....:             co_occurences[key][k] += v
   ....: 

10 loops, best of 3: 164 ms per loop