web-dev-qa-db-fra.com

Cosinité Similarité entre 2 listes de numéros

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.

91
Rob Alsod

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)
133
charmoniumQ

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))
57
dontloo

Vous pouvez utiliser cosine_similarity formulaire de fonction sklearn.metrics.pairwisedocs

In [23]: from sklearn.metrics.pairwise import cosine_similarity

In [24]: cosine_similarity([[1, 0, -1]], [[-1,-1, 0]])
Out[24]: array([[-0.5]])
53
Akavall

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.

30
Mike Housky

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.

enter image description here

13
McKelvin
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)))
10
pkacprzak

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) )
7
Mohammed

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]
2
sten

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)
2
rajeshwerkushwaha

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])))
1
Isira