2016-04-15 28 views
1

我有一個相對較大的(〜3GB,300萬條目)的子列表列表,其中每個子列表包含一組標籤。這裏是一個非常簡單的例子:如何在Python列表中有效地計數共現

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 

這就像魅力,排序的,但它的實際數據集,其中有非常大的子表(30K〜總獨特的標記)超慢。任何python專業人士都知道我可以如何加速這件事?

+0

作爲第一次近似嘗試bruteforce並用['joblib.Parallel'](https://pythonhosted.org/joblib/parallel.html)替換第一個forloop。請注意,在這種情況下,您需要一個Counter per-list而不是全局列表。 –

+0

此外,您可能想要嘗試使用'line_profiler'來查看哪兩個塊(「Counter」調用或「co_occurences」更新更加昂貴)。 –

+0

爲什麼你排序和什麼版本的Python? –

回答

1

This might go faster。你必須測量。

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 
+0

方式更快。好的工作,@Rob。這讓我得到了我現在需要的東西。任何洞察爲什麼這個作品以及它呢? – Aaron

+0

不是線索。很高興它的工作。 –

1

我只是搞亂不期待任何東西更有效地結束了,但100000家貓,狗和魚,這是相當快,在0.07秒打卡,而不是1.25。

我試圖用更短的解決方案,結束了,但事實證明這種方式是最快的,即使它看起來非常簡單:)

occurances = {} 
for tags in tag_corpus: 
    for key in tags: 
     for key2 in tags: 
      try: 
       occurances[key][key2] += 1 
      except KeyError: 
       try: 
        occurances[key][key2] = 1 
       except KeyError: 
        occurances[key] = {key2: 1} 
+0

謝謝!有趣的解決方案,但問題是,初始化大量字符數爲零的字符完全打亂了內存佔用。對我想象中的小數據非常有效。 – Aaron

+0

啊,我沒有意識到你有很多不同的標籤,我認爲你有一個3米的類似標籤記錄的寵物例子。無論如何,如果你想再試一次,我希望能解決這個問題。我用'defaultdict'試了一下(注意到Padraic提到它),但它仍然減慢了太多,簡單的嘗試/除了似乎工作正常。 – Peter

+0

真棒,我會玩這更多的我期待。將讓你們知道,如果我進一步與另一種解決方案。 – Aaron

1

,你可以嘗試用defaultdict梳理避免在使用從彼得斯答案的邏輯開始初始化,運行時會顯著快:

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,一個計數器字典是沒有實際工作Ÿ最快的方法來獲得計數,但默認爲,但即使使用拉姆達也是快速的。

即使滾動自己計數功能會更快:

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的可能會走很長的路。

+0

真棒,我會玩這個更期待。將讓你們知道,如果我進一步與另一種解決方案。我還沒有玩過cython,但做這樣的事情讓我覺得這可能是一個好主意。 – Aaron

相關問題