2013-08-24 21 views
45

tôi cần phải tính toán cosin tương giữa hai danh sách, chúng ta hãy nói ví dụ như danh sách 1 mà là dataSetI và danh sách 2 là dataSetII. Tôi không thể sử dụng bất kỳ thứ gì như numpy hoặc mô-đun thống kê. Tôi phải sử dụng các mô-đun chung (toán học, v.v.) (và các mô-đun ít nhất có thể, tại đó, để giảm thời gian sử dụng).Cosine tương đồng giữa 2 Number Giá ảo

Giả sử dataSetI[3, 45, 7, 2]dataSetII[2, 54, 13, 15]. Độ dài của danh sách là luôn là bằng nhau.

Tất nhiên, độ tương tự cosin là giữa 0 và 1 và vì lợi ích của nó, số này sẽ được làm tròn thành số thập phân thứ ba hoặc thứ tư với format(round(cosine, 3)).

Cảm ơn bạn rất nhiều vì đã giúp đỡ.

+6

Tôi thích cách SO nghiền nát linh hồn ra khỏi câu hỏi bài tập về nhà này để làm cho nó một đẹp một tài liệu tham khảo chung. OP nói "** Tôi không thể sử dụng * numpy ***, tôi phải đi theo cách toán học cho người đi bộ", và câu trả lời hàng đầu "bạn nên thử scipy, nó sử dụng numpy". Cơ học SO cấp huy hiệu vàng cho câu hỏi phổ biến. –

+0

Nikana Reklawyks, đó là một điểm tuyệt vời. Tôi đã có vấn đề đó nhiều hơn và thường xuyên hơn với StackOverflow. Và tôi đã có một số câu hỏi được đánh dấu là "trùng lặp" của một số câu hỏi trước đó, bởi vì những người kiểm duyệt đã không dành thời gian để hiểu những gì làm cho câu hỏi của tôi trở nên độc đáo. – LRK9

Trả lời

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

Bạn có thể khỏa lấp nó sau khi tính toán:

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

Nếu bạn muốn nó thực sự ngắn, bạn có thể sử dụng một lớp lót:

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))) 
+0

Tôi đã thử đoạn mã này, và nó dường như không hoạt động. Tôi đã thử nó với v1 là '[2,3,2,5]', và v2 là '[3,2,2,0]'. Nó trả về với '1.0', như thể chúng giống hệt nhau. Bất cứ ý tưởng gì là sai? –

+0

Hãy thử lại, đã có lỗi đánh máy trong mã. Bây giờ nó đã được sửa. – pkacprzak

+0

Bản sửa lỗi đã hoạt động tại đây. Công việc tốt! Xem bên dưới để biết cách tiếp cận xấu hơn nhưng nhanh hơn. –

77

Bạn nên cố gắng SciPy. Nó có một loạt các thói quen khoa học hữu ích ví dụ, "thói quen cho tích phân máy tính số, giải phương trình vi phân, tối ưu hóa, và ma trận thưa thớt." Nó sử dụng các NumPy tối ưu hóa siêu tốc cho crunching số của nó. Xem here để cài đặt.

Lưu ý rằng spatial.distance.cosine tính toán khoảng cách khoảng cách và không giống nhau. Vì vậy, bạn phải trừ giá trị từ 1 để có được sự tương đồng tương tự.

from scipy import spatial 

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

Tôi không cho rằng hiệu suất quan trọng nhiều ở đây, nhưng tôi không thể cưỡng lại. Hàm zip() hoàn toàn recopies cả hai vectơ (nhiều hơn một ma trận transpose, thực sự) chỉ để lấy dữ liệu theo thứ tự "Pythonic". Nó sẽ là thú vị khi thời gian thực hiện các loại hạt và bu lông:

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 

Đó đi qua các tiếng ồn C giống như chiết xuất các yếu tố một-at-a-thời gian, nhưng làm không sao chép mảng số lượng lớn và được tất cả mọi thứ quan trọng được thực hiện trong một vòng lặp đơn và sử dụng một căn bậc hai.

ETA: Cuộc gọi in được cập nhật thành một hàm. (Bản gốc là Python 2.7, không phải 3.3. Hiện tại chạy dưới Python 2.7 với câu lệnh from __future__ import print_function.) Đầu ra là giống nhau, theo một trong hai cách.

CPython 2.7.3 trên 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 

Vì vậy, cách unpythonic là khoảng 3,6 lần nhanh hơn trong trường hợp này.

+2

'cosine_measure' trong trường hợp này là gì? – MERose

+0

@MERose: 'cosine_measure' và' cosine_similarity' đơn giản là các cách triển khai khác nhau của cùng một phép tính. Tương đương với việc chia tỷ lệ cả mảng đầu vào thành "vectơ đơn vị" và lấy sản phẩm chấm. –

+0

Tôi đã đoán như vậy. Nhưng nó không hữu ích. Bạn trình bày so sánh thời gian của hai thuật toán nhưng chỉ trình bày một trong số chúng. – MERose

35

Bạn có thể sử dụng cosine_similarity chức năng hình thức 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]]) 
+6

Chỉ cần nhắc nhở rằng Việc chuyển một mảng thứ nguyên làm dữ liệu đầu vào không được dùng trong phiên bản sklearn 0,17 và sẽ tăng ValueError ở 0,19. –

+0

Cách chính xác để thực hiện điều này với sklearn là gì? – Elliott

+1

@Elliott one_dimension_array.reshape (-1,1) – bobo32

2

Bạn có thể làm điều này bằng Python sử dụng chức năng đơn giản:

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) 
+1

Đây là cách thực hiện văn bản cosin. Nó sẽ cung cấp cho đầu ra sai cho đầu vào số. – alvas

6

Tôi đã làm một benchmark dựa trên một số câu trả lời trong câu hỏi và sau đây đoạn mã được cho là lựa chọn tốt nhất:

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) 

Kết quả khiến tôi ngạc nhiên rằng việc triển khai dựa trên scipy không phải là nhanh nhất. Tôi profiled và thấy rằng cosin trong scipy mất rất nhiều thời gian để đúc một vector từ python danh sách để mảng numpy.

enter image description here

+0

Bạn chắc chắn rằng đây là cách nhanh nhất? –

+0

@JeruLuke Tôi đã dán liên kết kết quả điểm chuẩn của mình vào lúc bắt đầu câu trả lời: https://gist.github.com/mckelvin/5bfad28ceb3a484dfd2a#file-cos_sim-py-L9-L25 – McKelvin

+0

bỏ lỡ điều đó. Chỉ cần nhìn thấy nó. gr8 work –

1

Bạn có thể sử dụng chức năng đơn giản này để tính toán sự tương đồng cosin:

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]))) 
+0

tại sao tái tạo lại bánh xe? –

0

Sử dụng NumPy so sánh một danh sách các số đến nhiều danh sách (ma trận):

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] 
12

khác phiên bản dựa trên numpy chỉ

from numpy import dot 
from numpy.linalg import norm 

cos_sim = dot(a, b)/(norm(a)*norm(b)) 
+0

Rất rõ ràng như định nghĩa, nhưng có thể 'np.inner (a, b)/(chuẩn (a) * norm (b))' là tốt hơn để hiểu. 'dấu chấm' có thể nhận được kết quả tương tự như' bên trong' cho vectơ. – Belter

1

mà không sử dụng bất kỳ nhập khẩu

Math.sqrt (x)

có thể được thay thế bằng

x ** .5

mà không sử dụng NumPy .dot() bạn phải tạo hàm dấu chấm của riêng mình bằng cách sử dụng tính năng đọc danh sách:

def dot(A,B): 
    return (sum(a*b for a,b in zip(A,B))) 

và sau đó chỉ là một vấn đề đơn giản của nó áp dụng công thức tương đồng cosin:

def cosine_similarity(a,b): 
    return dot(a,b)/((dot(a,a) **.5) * (dot(b,b) ** .5)) 
Các vấn đề liên quan