簡體   English   中英

如何在Python的嵌套列表中獲取具有最多共同元素的兩個列表

[英]How to get two lists that have the most elements in common in a nested list in Python

我有一個列表,如下所示

[["This", "is","a", "test"], ["test","something", "here"], ["cat", "dog", "fish"]]

我如何獲得具有最多單詞的兩個列表? 在這種情況下,它將是第一個和第二個列表,因為它們都具有共同的單詞test

我試圖通過找到兩個列表的每個組合的交集來解決這個問題,並跟蹤具有最多共同詞的組合。 然而,這種方法似乎效率低,例如100,000個列表。 我認為這將是(100,000選2)組合。 有更快的方法嗎?

這是我的代碼嘗試

from itertools import combinations

a = [["This", "is","a", "test"], ["test","something", "here"], ["cat", "dog", "fish"]]
c= combinations(a,2)


max = 0
for pair in c:
    intersection = set(pair[0]).intersection(pair[1]) 
    if len(intersection) > max:
        max = len(intersection)
        mostsimilar = pair

print mostsimilar

我的程序的輸出是我所期望的,但是在更大的測試用例上它非常慢

輸出:

(['This', 'is', 'a', 'test'], ['test', 'something', 'here'])

根據我對這個問題的理解,我認為這應該有效:

import numpy as np
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.neighbors import KDTree, DistanceMetric

data = np.array([' '.join(words) for words in [['This', 'is', 'a', 'test'], ['test', 'something', 'here'], ['cat', 'dog', 'fish']]])

vectorised = CountVectorizer(binary=True).fit_transform(data).todense()
metric = DistanceMetric.get_metric('manhattan')
kdtree = KDTree(vectorised, metric=metric)
distances, indices = [result[:, 1] for result in kdtree.query(vectorised, k=2, dualtree=True)]

nearest_distances = (distances == distances.min())
print(data[nearest_distances])

輸出:

['This is a test' 'test something here']

我以下列方式重新解決問題:

每個單詞(或句子) list可以表示為稀疏矩陣中的一行,其中特定列中的1表示單詞的存在,0表示不存在,使用sklearnCountVectorizer

然后,我們可以看到兩個句子的相似性,如稀疏矩陣中的行, 可以通過比較每列中元素的值來確定 ,這就是曼哈頓距離。 這意味着我們有一個最近鄰居的問題。

sklearn還提供了一個k維樹類,我們可以使用它來為數據集中的每個點找到最近的兩個鄰居(因為一個點的最近鄰居本身)。 然后,它仍然是找到具有最低距離的鄰居,我們可以使用它來索引原始數組。

使用%%timeit ,我測試了我的解決方案VS blhsing對上的文字解決方案的運行這個頁面 ,留下將定時環外進口:

# my solution
198 ms ± 1.6 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)  
# blhsing's solution
4.76 s ± 374 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)  

將句子長度限制在20字以下:

# my solution
3.2 ms ± 294 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
# blhsing's solution
6.08 ms ± 714 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

O(nxm)時間復雜度中解決此問題的更有效方法(其中n是列表的大小, m是子列表中的平均字數,因此如果m相對較小且相對於列表的大小,此解決方案將線性擴展到n )是迭代列表列表並構建一個seen字典,將子列表中的每個單詞映射到已找到的子列表的索引列表包含到目前為止的單詞,然后使用collections.Counter對給定列表中的單詞使用most_common方法查找最相似列表的索引:

from collections import Counter
seen = {}
most_common_pair = None
most_common_count = 0
for index, lst in enumerate(a):
    for common_index, common_count in Counter(
            i for word in lst for i in seen.get(word, ())).most_common(1):
        if common_count > most_common_count:
            most_common_count = common_count
            most_common_pair = a[common_index], lst
    for word in lst:
        seen.setdefault(word, []).append(index)

給定變量a樣本輸入列表, most_common_pair將變為:

(['This', 'is', 'a', 'test'], ['test', 'something', 'here'])

我的鏟球。 我用729個列表列表對它進行了測試,但它仍能快速運行。 我不確定它的速度有多快,如果老實說的話。 但它不使用集合。

在這里(它有一個測試,只需使用該功能)

a = [1,2,3,4,5,6,7,8,9]
b = [9,10,11,12,13,14,15]
c = [11,3,99]
d = [9,10,11,12,50,1]
ls = [a,b,c,d]


for i in range(100,3000,2):
    ls.append([i,i+1,i+2,i+3])


def f(ls):
    wordDic = {}
    countDic = {}
    ind = 0
    for t in range(len(ls)):
        countDic[t]={}
    for l in ls:
        for word in l:
            try:
                for sharedIndex in wordDic[word]: #For every list that we already know has this word
                    try:
                        countDic[sharedIndex][ind] += 1  
                        try:
                            countDic[ind][sharedIndex] += 1
                        except KeyError:
                            countDic[ind][sharedIndex] = 1
                    except KeyError:
                        countDic[sharedIndex][ind] = 1
                wordDic[word].append(ind)
            except KeyError:
                wordDic[word] = [ind]
        ind += 1

    mx = 0
    ans = -1
    ind = 0
    for sLs in countDic.values():
        for rLr in sLs:
            if mx < sLs[rLr]:
                ans = (ls[ind],ls[rLr])
            mx = max(mx,sLs[rLr])
        ind += 1
    return ans

print(f(ls))  

它能做什么:

它基於這兩個詞典: wordDiccountDic

wordDic鍵是每個使用的“單詞”,其值是找到所述單詞的索引的列表。

countDic鍵是每個列表的索引,其值是包含多少列表的字典

countDic = {listInd:{otherLists:sharedAmount,...},...}

首先,它創建了詞典。 然后它會遍歷每個列表一次並保存它所擁有的單詞。 在將第二個字典中的“共享單詞”數量加1后,它會將自己的索引添加到每個單詞所具有的索引列表中。

完成后你會有這樣的事情:

{0:{1:1,2:1,3:2},1:{2:1,3:4},2:{3:1},3:{1:3,0:1}}( [9,10,11,12,13,14,15],[9,10,11,12,50,1]]

讀作:

{(列表零:與列表1 = 1共享的元素,與2 = 1共享的元素,與列表3共享的元素= 2。),(列表一:與列表2共享的元素= 1,與列表3共享的元素= 4 ) ,...}

在這種情況下,列表1與列表3共享大多數元素與其他元素。 函數的其余部分只是通過字典並找到最大值。

我可能搞砸了我的解釋。 我認為最好檢查一下這個功能是否比你自己的功能更好,然后嘗試理解它。

我還注意到您可能只需要將1添加到以前找到的列表中,而不需要將其他列表添加到您當前正在測試的列表中。 我會看看是否有效

編輯1:看起來如此。 線條:

try:
    countDic[ind][sharedIndex] += 1
except KeyError:
    countDic[ind][sharedIndex] = 1

可以注釋掉。

我希望這有幫助。

暫無
暫無

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

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