计算两个列表的相似度

时间:2011-07-15 15:45:55

标签: python algorithm

我有两个清单:

例如。 a = [1,8,3,9,4,9,3,8,1,2,3] 和 b = [1,8,1,3,9,4,9,3,8,1,2,3]

两者都包含整数。整体背后没有任何意义(例如,1不是'更接近'3而不是8')。

我正在尝试设计一种算法来计算两个ORDERED列表之间的相似性。 Ordered是关键字就在这里(所以我不能只取两个列表的集合并计算它们的set_difference百分比)。有时数字会重复(例如上面的3,8和9,我不能忽略重复)。

在上面的例子中,我打电话的函数会告诉我a和b的相似度约为90%。我怎样才能做到这一点?编辑距离是我想到的。我知道如何使用字符串,但我不知道如何使用它与一个整数列表。谢谢!

7 个答案:

答案 0 :(得分:22)

您可以使用difflib模块

  

比率()
  将序列相似性的度量返回为[0,1]范围内的浮点数。

给出了:

 >>> s1=[1,8,3,9,4,9,3,8,1,2,3]
 >>> s2=[1,8,1,3,9,4,9,3,8,1,2,3]
 >>> sm=difflib.SequenceMatcher(None,s1,s2)
 >>> sm.ratio()
 0.9565217391304348

答案 1 :(得分:12)

听起来,编辑(或Levenshtein)距离恰好是工作的正确工具。

这是一个可以在整数列表上使用的Python实现:http://hetland.org/coding/python/levenshtein.py

使用该代码,levenshtein([1,8,3,9,4,9,3,8,1,2,3], [1,8,1,3,9,4,9,3,8,1,2,3])会返回1,这是编辑距离。

考虑到编辑距离和两个数组的长度,计算“百分比相似度”指标应该是非常简单的。

答案 2 :(得分:3)

如果值没有任何特殊含义,只需使用相同的算法计算字符串上的编辑距离。

答案 3 :(得分:3)

解决此问题的一种方法是使用histogram。作为示例(使用numpy进行演示):

In []: a= array([1,8,3,9,4,9,3,8,1,2,3])
In []: b= array([1,8,1,3,9,4,9,3,8,1,2,3])

In []: a_c, _= histogram(a, arange(9)+ 1)
In []: a_c
Out[]: array([2, 1, 3, 1, 0, 0, 0, 4])

In []: b_c, _= histogram(b, arange(9)+ 1)
In []: b_c
Out[]: array([3, 1, 3, 1, 0, 0, 0, 4])

In []: (a_c- b_c).sum()
Out[]: -1

现在有很多方法可以利用a_cb_c

(看似)最简单的相似性度量是:

In []: 1- abs(-1/ 9.)
Out[]: 0.8888888888888888

其次是:

In []: norm(a_c)/ norm(b_c)
Out[]: 0.92796072713833688

In []: a_n= (a_c/ norm(a_c))[:, None]
In []: 1- norm(b_c- dot(dot(a_n, a_n.T), b_c))/ norm(b_c)
Out[]: 0.84445724579043624

因此,您需要更具体地找出适合您目的的最合适的相似性度量。

答案 4 :(得分:0)

除非我忽略了这一点。

from __future__ import division

def similar(x,y):
    si = 0
    for a,b in zip(x, y):
        if a == b:
            si += 1
    return (si/len(x)) * 100


if __name__ in '__main__':
    a = [1,8,3,9,4,9,3,8,1,2,3] 
    b = [1,8,1,3,9,4,9,3,8,1,2,3]
    result = similar(a,b)
    if result is not None:
        print "%s%s Similar!" % (result,'%')

答案 5 :(得分:0)

很久以前,我已经为类似的任务实施了一些东西。现在,我只有a blog entry for that。这很简单:你必须计算两个序列的pdf,然后它会找到pdf的图形表示覆盖的公共区域。

很抱歉链接上的图像损坏,我当时使用的外部服务器已经死了。

目前,针对您的问题,代码转换为

def overlap(pdf1, pdf2):
    s = 0
    for k in pdf1:
        if pdf2.has_key(k):
            s += min(pdf1[k], pdf2[k])
    return s

def pdf(l):
    d = {}
    s = 0.0
    for i in l:
        s += i
        if d.has_key(i):
            d[i] += 1
        else:
            d[i] = 1
    for k in d:
        d[k] /= s
    return d

def solve():
    a = [1, 8, 3, 9, 4, 9, 3, 8, 1, 2, 3]
    b = [1, 8, 1, 3, 9, 4, 9, 3, 8, 1, 2, 3]
    pdf_a = pdf(a)
    pdf_b = pdf(b)
    print pdf_a
    print pdf_b
    print overlap(pdf_a, pdf_b)
    print overlap(pdf_b, pdf_a)

if __name__ == '__main__':
    solve()

不幸的是,它给出了一个意想不到的答案,只有0.212292609351

答案 6 :(得分:0)

@kraymer 提出的解决方案不适用于

s1=[1,2,3,4,5,6,7,8,9,10]
s2=[2,1,3,4,5,6,7,8,9,9]  

因为它返回 0.8,即使有 3 个不同的元素而不是 2 个。

解决方法可能是:

def find_percentage_agreement(s1, s2):
    assert len(s1)==len(s2), "Lists must have the same shape"
    nb_agreements = 0  # initialize counter to 0
    for idx, value in enumerate(s1):
        if s2[idx] == value:
            nb_agreements += 1

    percentage_agreement = nb_agreements/len(s1)

    return percentage_agreement

返回预期结果:

>>> s1=[1,2,3,4,5,6,7,8,9,10]
>>> s2=[2,1,3,4,5,6,7,8,9,9]
>>> find_percentage_agreement(s1, s2)
0.7