2个数字列表之间的余弦相似性

我需要计算两个列表之间的余弦相似度 ,比如列表1是dataSetI ,列表2是dataSetII 。 我不能使用任何东西,如numpy或统计模块。 我必须使用通用模块(math等)(尽可能less的模块,以减less时间)。

假设dataSetI[3, 45, 7, 2] dataSetI [3, 45, 7, 2]dataSetII[2, 54, 13, 15] dataSetII [2, 54, 13, 15] 。 列表的长度总是相等的。

当然,余弦的相似度在0和1之间,为了这个目的,它将被舍入到format(round(cosine, 3))小数第三或第四位。

提前非常感谢您的帮助。

你应该试试SciPy 。 它有一些有用的科学例程,例如“数值计算积分,解微分方程,优化和稀疏matrix的例程”。 它使用超快优化的NumPy进行数字处理。 看到这里安装。

请注意,spatial.distance.cosine会计算距离 ,而不是相似度。 所以,你必须减去1的值来得到相似度

 from scipy import spatial dataSetI = [3, 45, 7, 2] dataSetII = [2, 54, 13, 15] result = 1 - spatial.distance.cosine(dataSetI, dataSetII) 

您可以使用cosine_similarity函数formssklearn.metrics.pairwise 文档

 In [23]: from sklearn.metrics.pairwise import cosine_similarity In [24]: cosine_similarity([1, 0, -1], [-1,-1, 0]) Out[24]: array([[-0.5]]) 

我不认为这里的performance很重要,但我无法抗拒。 zip()函数完全复制两个向量(实际上是更多的matrix转置),只是为了以“Pythonic”顺序获取数据。 时间紧迫的实施会很有趣:

 import math def cosine_similarity(v1,v2): "compute cosine similarity of v1 to v2: (v1 dot v2)/{||v1||*||v2||)" sumxx, sumxy, sumyy = 0, 0, 0 for i in range(len(v1)): x = v1[i]; y = v2[i] sumxx += x*x sumyy += y*y sumxy += x*y return sumxy/math.sqrt(sumxx*sumyy) v1,v2 = [3, 45, 7, 2], [2, 54, 13, 15] print(v1, v2, cosine_similarity(v1,v2)) Output: [3, 45, 7, 2] [2, 54, 13, 15] 0.972284251712 

这个过程像C一样一次一个地提取元素,但是没有大量的数组拷贝,并且在一个for循环中获取所有重要的东西,并且使用一个平方根。

ETA:更新的打印呼叫是一项function。 (原来是Python 2.7,而不是3.3,当前运行在Python 2.7下, from __future__ import print_function语句。)输出是相同的,无论哪种方式。

3.0GHz Core 2 Duo上的CPYthon 2.7.3:

 >>> timeit.timeit("cosine_similarity(v1,v2)",setup="from __main__ import cosine_similarity, v1, v2") 2.4261788514654654 >>> timeit.timeit("cosine_measure(v1,v2)",setup="from __main__ import cosine_measure, v1, v2") 8.794677709375264 

所以,在这种情况下,和声方式快了3.6倍。

 import math from itertools import izip def dot_product(v1, v2): return sum(map(lambda x: x[0] * x[1], izip(v1, v2))) def cosine_measure(v1, v2): prod = dot_product(v1, v2) len1 = math.sqrt(dot_product(v1, v1)) len2 = math.sqrt(dot_product(v2, v2)) return prod / (len1 * len2) 

计算完成后,可以将其舍入

 cosine = format(round(cosine_measure(v1, v2), 3)) 

如果你想真的很短,你可以使用这一行:

 from math import sqrt from itertools import izip def cosine_measure(v1, v2): return (lambda (x, y, z): x / sqrt(y * z))(reduce(lambda x, y: (x[0] + y[0] * y[1], x[1] + y[0]**2, x[2] + y[1]**2), izip(v1, v2), (0, 0, 0))) 

另一个版本只基于numpy

 from numpy import dot from numpy.linalg import norm cos_sim = dot(a, b)/(norm(a)*norm(b)) 

我在问题的几个答案基础上做了一个基准 ,下面的片段被认为是最好的select:

 def dot_product2(v1, v2): return sum(map(operator.mul, v1, v2)) def vector_cos5(v1, v2): prod = dot_product2(v1, v2) len1 = math.sqrt(dot_product2(v1, v1)) len2 = math.sqrt(dot_product2(v2, v2)) return prod / (len1 * len2) 

结果让我吃惊的是基于scipy的实现并不是最快的。 我分析并发现,在scipy余弦需要花费大量的时间来从python列表到numpy数组。

在这里输入图像说明

你可以使用简单的函数在Python中做到这一点:

 def get_cosine(text1, text2): vec1 = text1 vec2 = text2 intersection = set(vec1.keys()) & set(vec2.keys()) numerator = sum([vec1[x] * vec2[x] for x in intersection]) sum1 = sum([vec1[x]**2 for x in vec1.keys()]) sum2 = sum([vec2[x]**2 for x in vec2.keys()]) denominator = math.sqrt(sum1) * math.sqrt(sum2) if not denominator: return 0.0 else: return round(float(numerator) / denominator, 3) dataSet1 = [3, 45, 7, 2] dataSet2 = [2, 54, 13, 15] get_cosine(dataSet1, dataSet2) 

您可以使用这个简单的函数来计算余弦相似度:

 def cosine_similarity(a, b): return sum([i*j for i,j in zip(a, b)])/(math.sqrt(sum([i*i for i in a]))* math.sqrt(sum([i*i for i in b]))) 

使用numpy比较一个数字列表到多个列表(matrix):

 def cosine_similarity(vector,matrix): return ( np.sum(vector*matrix,axis=1) / ( np.sqrt(np.sum(matrix**2,axis=1)) * np.sqrt(np.sum(vector**2)) ) )[::-1] 
Interesting Posts