我有一个列表列表,如下所示
[["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'])
答案 0 :(得分:3)
基于我对问题的理解,我认为这应该可行:
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表示存在一个单词,而使用{{1}则表示不存在一个单词}的sklearn
。
然后,我们可以看到,通过比较每一列中元素的值,可以确定两个句子在稀疏矩阵中的相似度,即曼哈顿距离。这意味着我们有一个最近邻居问题。
CountVectorizer
还提供了一个k维树类,我们可以使用它来为数据集中的每个点找到最近的两个邻居(因为一个点的最近邻居本身就是它)。然后,剩下的就是找到距离最小的邻居,我们可以用它来索引原始数组。
使用sklearn
,我在this page上的文本上测试了我的解决方案与blhsing解决方案的运行时间,从而使导入不在计时循环之内:
%%timeit
将句子的长度限制为20个单词以下:
# 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)
答案 1 :(得分:3)
一种更有效的方法来解决(em)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'])
答案 2 :(得分:0)
我的钓具。我用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))
功能:
它基于以下两个词典: wordDic 和 countDic 。
wordDic 键是所使用的每个“单词”,其值是找到该单词的索引的列表。
countDic 键是每个列表的索引,其值是可容纳多少
的字典countDic = {listInd:{otherLists:sharedAmount,...},...}
首先,它创建字典。然后,它会遍历每个列表一次并保存其中的单词。在第二个字典中的“共享单词”计数中增加一个后,它会将自己的索引添加到每个单词具有的索引列表中。
完成后,您将看到以下内容:
{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 = 1共享,元素与列表3 = 2共享。),(列表一:元素与列表2 = 1共享,元素与列表< em> 3 = 4 ),....}
在这种情况下,列表1与列表3共享的元素最多。该函数的其余部分只是简单地遍历字典并找到最大值。
我可能弄乱了我的解释。我认为最好先检查该功能是否比自己的功能更好,然后再尝试理解它。
我还注意到您大概只需将1添加到以前找到的列表中,而无需将该其他列表添加到当前正在测试的列表中。我看看是否可行
EDIT1:看来是这样。行:
try:
countDic[ind][sharedIndex] += 1
except KeyError:
countDic[ind][sharedIndex] = 1
可以被注释掉。
我希望这会有所帮助。