J'ai besoin de calculer similarité de cosinus entre deux listes, disons par exemple la liste 1 qui est dataSetI
et la liste 2 qui est dataSetII
. Je ne peux utiliser aucun élément tel que numpy ou un module de statistiques. Je dois utiliser des modules communs (mathématiques, etc.) (et le moins de modules possible pour réduire le temps passé).
Disons que dataSetI
est [3, 45, 7, 2]
et dataSetII
est [2, 54, 13, 15]
. La longueur des listes est toujours égale.
Bien sûr, la similarité de cosinus est comprise entre 0 et 1 , et pour ce faire, elle sera arrondie à la troisième ou à la quatrième décimale avec format(round(cosine, 3))
.
Merci beaucoup d'avance pour votre aide.
Vous devriez essayer SciPy . Il contient un ensemble de routines scientifiques utiles, par exemple, "des routines pour calculer intégralement des intégrales, résoudre des équations différentielles, optimiser, et des matrices creuses". Il utilise NumPy ultra-optimisé pour le traitement des chiffres. Voir ici pour l'installation.
Notez que spatial.distance.cosine calcule la distance et non la similarité. Donc, vous devez soustraire la valeur de 1 pour obtenir la similarité .
from scipy import spatial
dataSetI = [3, 45, 7, 2]
dataSetII = [2, 54, 13, 15]
result = 1 - spatial.distance.cosine(dataSetI, dataSetII)
une autre version basée sur numpy
uniquement
from numpy import dot
from numpy.linalg import norm
cos_sim = dot(a, b)/(norm(a)*norm(b))
Vous pouvez utiliser cosine_similarity
formulaire de fonction sklearn.metrics.pairwise
docs
In [23]: from sklearn.metrics.pairwise import cosine_similarity
In [24]: cosine_similarity([[1, 0, -1]], [[-1,-1, 0]])
Out[24]: array([[-0.5]])
Je ne suppose pas que la performance compte beaucoup ici, mais je ne peux pas résister. La fonction Zip () recopie complètement les deux vecteurs (plus d'une matrice transposée, en fait) juste pour obtenir les données dans l'ordre "Pythonic". Il serait intéressant de chronométrer la mise en œuvre pratique:
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
Cela passe par le bruit semblable à C qui consiste à extraire des éléments un à un, mais ne copie pas en bloc, effectue toutes les tâches importantes en une seule boucle for et utilise une seule racine carrée.
ETA: L'appel d'impression mis à jour est une fonction. (L'original était Python 2.7, pas 3.3. Le courant s'exécute sous Python 2.7 avec une instruction from __future__ import print_function
.) Le résultat est identique, dans tous les cas.
CPYthon 2.7.3 sur 3.0GHz Core 2 Duo:
>>> 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
Donc, la manière unpythonique est environ 3,6 fois plus rapide dans ce cas.
J'ai fait un repère basé sur plusieurs réponses à la question et l'extrait suivant semble être le meilleur choix:
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)
Le résultat me surprend que la mise en œuvre basée sur scipy
ne soit pas la plus rapide. J'ai analysé et constaté que le cosinus dans scipy prend beaucoup de temps pour convertir un vecteur de la liste python vers un tableau numpy.
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)
Vous pouvez l'arrondir après le calcul:
cosine = format(round(cosine_measure(v1, v2), 3))
Si vous le voulez vraiment, vous pouvez utiliser ce one-liner:
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)))
sans utiliser aucune importation
math.sqrt (x)
peut être remplacé par
x ** .5
sans utiliser numpy.dot (), vous devez créer votre propre fonction de point en utilisant la compréhension de liste:
def dot(A,B):
return (sum(a*b for a,b in Zip(A,B)))
et ensuite, il suffit simplement d'appliquer la formule de similarité cosinus:
def cosine_similarity(a,b):
return dot(a,b) / ( (dot(a,a) **.5) * (dot(b,b) ** .5) )
En utilisant numpy, comparez une liste de nombres à plusieurs listes (matrice):
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]
Vous pouvez le faire dans Python en utilisant une fonction simple:
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)
Vous pouvez utiliser cette fonction simple pour calculer la similarité de cosinus:
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])))