簡體   English   中英

Python計算元組列表中的元組並發

[英]Python calculate co-occurrence of tuples in list of lists of tuples

我有很多元組列表,例如

actions  = [ [('d', 'r'), ... ('c', 'e'),('', 'e')],
             [('r', 'e'), ... ('c', 'e'),('d', 'r')],
                                    ... , 
             [('a', 'b'), ... ('c', 'e'),('c', 'h')]
           ]

我想找到元組的同時出現。

我已經嘗試過從這個問題中得出結論,但是被接受的答案太慢了。 例如,在具有1494個元組列表的列表中,得到的字典大小為18225703,花了幾個小時才能運行2個元組共現。 因此,由於我的清單更大,所以簡單的排列和計數似乎不是答案。

我希望輸出會提取出最常見的對(2)或更多(最多3,4,5)元組。 以上一個列表為例:

('c', 'e'),('d', 'r') 

在搜索對時會很常見,因為它們經常同時出現。 是否有一種有效的方法來實現這一目標?

我認為沒有希望有更快的算法:您必須計算組合來計算它們。 但是,如果您對共現閾值不感興趣,可以降低算法的復雜度。 在這兩種情況下,都希望減少空間復雜度。

讓我們舉一個小例子:

>>> actions  = [[('d', 'r'), ('c', 'e'),('', 'e')],
...             [('r', 'e'), ('c', 'e'),('d', 'r')],
...             [('a', 'b'), ('c', 'e'),('c', 'h')]]

一般答案

對於大量列表, 此答案可能是最好的,但是您可以避免創建中間列表。 首先,在所有現有的元素對上創建一個可迭代的元素(在您的情況下,元素也是對的,但這無關緊要):

>>> import itertools
>>> it = itertools.chain.from_iterable(itertools.combinations(pair_list, 2) for pair_list in actions)

如果要查看結果,則必須使用可迭代的:

>>> list(it)
[(('d', 'r'), ('c', 'e')), (('d', 'r'), ('', 'e')), (('c', 'e'), ('', 'e')), (('r', 'e'), ('c', 'e')), (('r', 'e'), ('d', 'r')), (('c', 'e'), ('d', 'r')), (('a', 'b'), ('c', 'e')), (('a', 'b'), ('c', 'h')), (('c', 'e'), ('c', 'h'))]

再算上排序對(用新鮮的it !)

>>> it = itertools.chain.from_iterable(itertools.combinations(pair_list, 2) for pair_list in actions)
>>> from collections import Counter
>>> c = Counter((a,b) if a<=b else (b,a) for a,b in it)
>>> c
Counter({(('c', 'e'), ('d', 'r')): 2, (('', 'e'), ('d', 'r')): 1, (('', 'e'), ('c', 'e')): 1, (('c', 'e'), ('r', 'e')): 1, (('d', 'r'), ('r', 'e')): 1, (('a', 'b'), ('c', 'e')): 1, (('a', 'b'), ('c', 'h')): 1, (('c', 'e'), ('c', 'h')): 1})
>>> c.most_common(2)
[((('c', 'e'), ('d', 'r')), 2), ((('', 'e'), ('d', 'r')), 1)]

至少在空間方面,此解決方案應該是有效的,因為一切都是懶惰的,並且Counter的元素數是同一列表中元素的組合數,即最多N(N-1)/2 ,其中N是所有列表中不同元素的數量(“最多”是因為某些元素從不“相遇”,因此不會發生任何組合)。

時間復雜度為O(M . L^2) ,其中M是列表數, L是最大列表的大小。

並發數閾值

我假設列表中的所有元素都是不同的。 關鍵思想是, 如果一個元素僅出現在一個列表中,則該元素在此游戲中完全沒有機會擊敗任何人 :它將與所有鄰居共同出現1 ,與其他列表中的元素同時出現0次。 如果有很多“孤兒”,則在處理組合時將其刪除可能會很有用:

>>> d = Counter(itertools.chain.from_iterable(actions))
>>> d
Counter({('c', 'e'): 3, ('d', 'r'): 2, ('', 'e'): 1, ('r', 'e'): 1, ('a', 'b'): 1, ('c', 'h'): 1})
>>> orphans = set(e for e, c in d.items() if c <= 1)
>>> orphans
{('a', 'b'), ('r', 'e'), ('c', 'h'), ('', 'e')}

現在,嘗試相同的算法:

>>> it = itertools.chain.from_iterable(itertools.combinations((p for p in pair_list if p not in orphans), 2) for pair_list in actions)
>>> c = Counter((a,b) if a<=b else (b,a) for a,b in it)
>>> c
Counter({(('c', 'e'), ('d', 'r')): 2})

注意理解:括號中沒有括號。

如果您在N個元素的列表中有K個孤兒,則該列表的時間復雜度將從N(N-1)/2降至(NK)(NK-1)/2 ,即(如果我沒有記錯! ) K.(2N-K-1)組合較少。

這可以概括為:如果一個元素出現在兩個或更少的列表中,則該元素與其他元素最多同時出現2次,依此類推。

如果仍然很慢,請切換到較快的語言。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM