如何在Python中计算两个包含字符串的列表的Jaccard相似度?

21
我有两个包含用户名的列表,我想计算Jaccard相似度。这可行吗?
这个帖子展示了如何计算两个字符串之间的Jaccard相似度,但我想将其应用于两个列表,其中每个元素都是一个单词(例如,用户名)。
9个回答

38

最终我还是自己写出了解决方案:

def jaccard_similarity(list1, list2):
    intersection = len(list(set(list1).intersection(list2)))
    union = (len(set(list1)) + len(set(list2))) - intersection
    return float(intersection) / union

4
该函数将始终返回0.0。 - xyd
@xyd 对我来说完美无缺。你能解释一下吗? - Aventinus
值得注意的是,这个计算与@w2bo的答案不同,因为这个计算没有通过集合长度并集进行除法运算。 - Union find
这个答案是错误的。例如,jaccard_similarity([1], [0, 1]) -> 0.5jaccard_similarity([1, 1], [0, 1, 1]) -> 0.25,然而第二个应该比第一个更相似,或者至少相似程度应该不亚于第一个,这取决于你如何定义Jaccard相似度。 - Muhammed Hasan Celik
3
解决方案简单而优雅,但不是100%正确。您应该将相应的行更改为:union = (len(set(list1)) + len(set(list2))) - intersection - Amir

32

对于Python 3:

def jaccard_similarity(list1, list2):
    s1 = set(list1)
    s2 = set(list2)
    return float(len(s1.intersection(s2)) / len(s1.union(s2)))
list1 = ['dog', 'cat', 'cat', 'rat']
list2 = ['dog', 'cat', 'mouse']
jaccard_similarity(list1, list2)
>>> 0.5

对于Python2,请使用return len(s1.intersection(s2)) / float(len(s1.union(s2)))


4
这将会返回 0.0 作为结果。应修改返回语句如下:return float(len(s1.intersection(s2))) / float(len(s1.union(s2))) - Shalini Baranwal
对于Python2,请使用以下代码:return float(len(s1.intersection(s2))) / len(s1.union(s2)) - seralouk

14

@aventinus 我的声誉不足以在您的答案中添加评论,但为了更清晰地解释,您的解决方案测量了jaccard_similarity但函数的名称错误地命名为jaccard_distance,实际上应该是1-jaccard_similarity


1
谢谢你的建议!我不知道那个。我已经相应地编辑了答案。 - Aventinus

7
假设你的用户名不会重复,你可以使用相同的思路:
def jaccard(a, b):
    c = a.intersection(b)
    return float(len(c)) / (len(a) + len(b) - len(c))

list1 = ['dog', 'cat', 'rat']
list2 = ['dog', 'cat', 'mouse']
# The intersection is ['dog', 'cat']
# union is ['dog', 'cat', 'rat', 'mouse]
words1 = set(list1)
words2 = set(list2)
jaccard(words1, words2)
>>> 0.5

3

@Aventinus(我也无法评论):请注意,Jaccard 相似度是一种操作集合的方法,因此在分母部分也应该使用集合(而不是列表)。举个例子,jaccard_similarity('aa','ab')应该得出0.5

def jaccard_similarity(list1, list2):
    intersection = len(set(list1).intersection(list2))
    union = len(set(list1)) + len(set(list2)) - intersection

    return intersection / union

请注意,在交集操作中不需要先转换为列表。此外,在Python 3中无需转换为浮点数类型。

3
您可以使用Distance库。
#pip install Distance

import distance

distance.jaccard("decide", "resize")

# Returns
0.7142857142857143

这个答案描述了如何获取两个字符串之间的Jaccard相似度,而这不是这个问题所涉及的内容。 - Aventinus

2
这里是Simphile NLP文本相似度包的创建者。Simphile包含多个文本相似度方法,其中之一就是Jaccard方法。
在终端中安装该包:
pip install simphile

那么你的代码可能是这样的:
from simphile import jaccard_list_similarity

list_a = ['cat', 'cat', 'dog']
list_b = ['dog', 'dog', 'cat']

print(f"Jaccard Similarity: {jaccard_list_similarity(list_a, list_b)}")

输出结果为:
Jaccard Similarity: 0.5

请注意,此解决方案考虑了重复元素——这对于文本相似性非常关键;如果没有考虑重复元素,在上面的例子中,由于两个列表作为集合会缩减为 {'dog','cat'},因此会显示100%的相似度。

1
如果您想包含重复元素,可以使用Counter。它只是一个扩展的dict,应该相对快速。
from collections import Counter
def jaccard_repeats(a, b):
    """Jaccard similarity measure between input iterables,
    allowing repeated elements"""
    _a = Counter(a)
    _b = Counter(b)
    c = (_a - _b) + (_b - _a)
    n = sum(c.values())
    return n/(len(a) + len(b) - n)

list1 = ['dog', 'cat', 'rat', 'cat']
list2 = ['dog', 'cat', 'rat']
list3 = ['dog', 'cat', 'mouse']     

jaccard_repeats(list1, list3)      
>>> 0.75

jaccard_repeats(list1, list2) 
>>> 0.16666666666666666

jaccard_repeats(list2, list3)  
>>> 0.5

我认为这个解决方案在处理重复项方面不正确。然而,对于没有重复项的列表,它可以正常工作。 - AlessioX
我认为这是距离,所以如果想要相似度,应该从返回行中删除“1-”。 - Tedo Vrbanec

1
为了避免联合中元素的重复,并稍微提高速度,我建议:
def Jaccar_score(lista1, lista2):    
    inter = len(list(set(lista_1) & set(lista_2)))
    union = len(list(set(lista_1) | set(lista_2)))
    return inter/union

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接