计算 Python 中的 Jaccard 相似度
Computing Jaccard Similarity in Python
我有 20,000 个文档要为其计算真正的 Jaccard 相似度,以便我稍后可以检查 MinWise 哈希对其进行近似的准确度。
每个文档都表示为 numpy 矩阵中的一列,其中每一行都是一个出现在文档中 (entry=1) 或不出现 (entry=0) 的单词。大约有 600 个字(行)。
因此,例如第 1 列将是 [1 0 0 0 0 0 1 0 0 0 1 0],这意味着其中出现了单词 1、7、11 而没有其他单词。
除了我的逐元素比较方法之外,还有更有效的计算相似度的方法吗?我不知道如何使用集合来提高速度,因为集合刚刚变成 (0,1),但就目前而言,代码慢得不可思议。
import numpy as np
#load file into python
rawdata = np.loadtxt("myfile.csv",delimiter="\t")
#Convert the documents from rows to columns
rawdata = np.transpose(rawdata)
#compute true jacard similarity
ndocs = rawdata.shape[1]
nwords = rawdata.shape[0]
tru_sim = np.zeros((ndocs,ndocs))
#computes jaccard similarity of 2 documents
def jaccard(c1, c2):
n11 = sum((c1==1)&(c2==1))
n00 = sum((c1==0)&(c2==0))
jac = n11 / (nfeats-n00)
return (jac)
for i in range(0,ndocs):
tru_sim[i,i]=1
for j in range(i+1,ndocs):
tru_sim[i,j] = jaccard(rawdata[:,i],rawdata[:,j])
这是一个向量化的方法 -
# Get the row, col indices that are to be set in output array
r,c = np.tril_indices(ndocs,-1)
# Use those indicees to slice out respective columns
p1 = rawdata[:,c]
p2 = rawdata[:,r]
# Perform n11 and n00 vectorized computations across all indexed columns
n11v = ((p1==1) & (p2==1)).sum(0)
n00v = ((p1==0) & (p2==0)).sum(0)
# Finally, setup output array and set final division computations
out = np.eye(ndocs)
out[c,r] = n11v / (nfeats-n00v)
用 np.einsum
-
计算 n11v
和 n00v
的替代方法
n11v = np.einsum('ij,ij->j',(p1==1),(p2==1).astype(int))
n00v = np.einsum('ij,ij->j',(p1==0),(p2==0).astype(int))
如果 rawdata
仅由 0s
和 1s
组成,获得它们的更简单方法是 -
n11v = np.einsum('ij,ij->j',p1,p2)
n00v = np.einsum('ij,ij->j',1-p1,1-p2)
基准测试
函数定义-
def original_app(rawdata, ndocs, nfeats):
tru_sim = np.zeros((ndocs,ndocs))
for i in range(0,ndocs):
tru_sim[i,i]=1
for j in range(i+1,ndocs):
tru_sim[i,j] = jaccard(rawdata[:,i],rawdata[:,j])
return tru_sim
def vectorized_app(rawdata, ndocs, nfeats):
r,c = np.tril_indices(ndocs,-1)
p1 = rawdata[:,c]
p2 = rawdata[:,r]
n11v = ((p1==1) & (p2==1)).sum(0)
n00v = ((p1==0) & (p2==0)).sum(0)
out = np.eye(ndocs)
out[c,r] = n11v / (nfeats-n00v)
return out
验证和计时 -
In [6]: # Setup inputs
...: rawdata = (np.random.rand(20,10000)>0.2).astype(int)
...: rawdata = np.transpose(rawdata)
...: ndocs = rawdata.shape[1]
...: nwords = rawdata.shape[0]
...: nfeats = 5
...:
In [7]: # Verify results
...: out1 = original_app(rawdata, ndocs, nfeats)
...: out2 = vectorized_app(rawdata, ndocs, nfeats)
...: print np.allclose(out1,out2)
...:
True
In [8]: %timeit original_app(rawdata, ndocs, nfeats)
1 loops, best of 3: 8.72 s per loop
In [9]: %timeit vectorized_app(rawdata, ndocs, nfeats)
10 loops, best of 3: 27.6 ms per loop
一些神奇的300x+
加速那里!
那么,为什么这么快? 好吧,这涉及到很多因素,最重要的一个是 NumPy 数组是为提高性能而构建的,并针对向量化计算进行了优化。通过提议的方法,我们很好地利用了它,因此看到了这样的加速。
这里 related Q&A
详细讨论了这些性能标准。
要计算 Jaccard,请使用:
def jaccard(x,y):
x = np.asarray(x, np.bool) # Not necessary, if you keep your data
y = np.asarray(y, np.bool) # in a boolean array already!
return np.double(np.bitwise_and(x, y).sum()) / np.double(np.bitwise_or(x, y).sum())
print jaccard([1,1,0,0,0],[0,1,0,0,1])
>>> 0.33333333333333331
我有 20,000 个文档要为其计算真正的 Jaccard 相似度,以便我稍后可以检查 MinWise 哈希对其进行近似的准确度。
每个文档都表示为 numpy 矩阵中的一列,其中每一行都是一个出现在文档中 (entry=1) 或不出现 (entry=0) 的单词。大约有 600 个字(行)。
因此,例如第 1 列将是 [1 0 0 0 0 0 1 0 0 0 1 0],这意味着其中出现了单词 1、7、11 而没有其他单词。
除了我的逐元素比较方法之外,还有更有效的计算相似度的方法吗?我不知道如何使用集合来提高速度,因为集合刚刚变成 (0,1),但就目前而言,代码慢得不可思议。
import numpy as np
#load file into python
rawdata = np.loadtxt("myfile.csv",delimiter="\t")
#Convert the documents from rows to columns
rawdata = np.transpose(rawdata)
#compute true jacard similarity
ndocs = rawdata.shape[1]
nwords = rawdata.shape[0]
tru_sim = np.zeros((ndocs,ndocs))
#computes jaccard similarity of 2 documents
def jaccard(c1, c2):
n11 = sum((c1==1)&(c2==1))
n00 = sum((c1==0)&(c2==0))
jac = n11 / (nfeats-n00)
return (jac)
for i in range(0,ndocs):
tru_sim[i,i]=1
for j in range(i+1,ndocs):
tru_sim[i,j] = jaccard(rawdata[:,i],rawdata[:,j])
这是一个向量化的方法 -
# Get the row, col indices that are to be set in output array
r,c = np.tril_indices(ndocs,-1)
# Use those indicees to slice out respective columns
p1 = rawdata[:,c]
p2 = rawdata[:,r]
# Perform n11 and n00 vectorized computations across all indexed columns
n11v = ((p1==1) & (p2==1)).sum(0)
n00v = ((p1==0) & (p2==0)).sum(0)
# Finally, setup output array and set final division computations
out = np.eye(ndocs)
out[c,r] = n11v / (nfeats-n00v)
用 np.einsum
-
n11v
和 n00v
的替代方法
n11v = np.einsum('ij,ij->j',(p1==1),(p2==1).astype(int))
n00v = np.einsum('ij,ij->j',(p1==0),(p2==0).astype(int))
如果 rawdata
仅由 0s
和 1s
组成,获得它们的更简单方法是 -
n11v = np.einsum('ij,ij->j',p1,p2)
n00v = np.einsum('ij,ij->j',1-p1,1-p2)
基准测试
函数定义-
def original_app(rawdata, ndocs, nfeats):
tru_sim = np.zeros((ndocs,ndocs))
for i in range(0,ndocs):
tru_sim[i,i]=1
for j in range(i+1,ndocs):
tru_sim[i,j] = jaccard(rawdata[:,i],rawdata[:,j])
return tru_sim
def vectorized_app(rawdata, ndocs, nfeats):
r,c = np.tril_indices(ndocs,-1)
p1 = rawdata[:,c]
p2 = rawdata[:,r]
n11v = ((p1==1) & (p2==1)).sum(0)
n00v = ((p1==0) & (p2==0)).sum(0)
out = np.eye(ndocs)
out[c,r] = n11v / (nfeats-n00v)
return out
验证和计时 -
In [6]: # Setup inputs
...: rawdata = (np.random.rand(20,10000)>0.2).astype(int)
...: rawdata = np.transpose(rawdata)
...: ndocs = rawdata.shape[1]
...: nwords = rawdata.shape[0]
...: nfeats = 5
...:
In [7]: # Verify results
...: out1 = original_app(rawdata, ndocs, nfeats)
...: out2 = vectorized_app(rawdata, ndocs, nfeats)
...: print np.allclose(out1,out2)
...:
True
In [8]: %timeit original_app(rawdata, ndocs, nfeats)
1 loops, best of 3: 8.72 s per loop
In [9]: %timeit vectorized_app(rawdata, ndocs, nfeats)
10 loops, best of 3: 27.6 ms per loop
一些神奇的300x+
加速那里!
那么,为什么这么快? 好吧,这涉及到很多因素,最重要的一个是 NumPy 数组是为提高性能而构建的,并针对向量化计算进行了优化。通过提议的方法,我们很好地利用了它,因此看到了这样的加速。
这里 related Q&A
详细讨论了这些性能标准。
要计算 Jaccard,请使用:
def jaccard(x,y):
x = np.asarray(x, np.bool) # Not necessary, if you keep your data
y = np.asarray(y, np.bool) # in a boolean array already!
return np.double(np.bitwise_and(x, y).sum()) / np.double(np.bitwise_or(x, y).sum())
print jaccard([1,1,0,0,0],[0,1,0,0,1])
>>> 0.33333333333333331