提问者:小点点

基于jaccard相似度的分类数据聚类


我正在尝试为分类数据构建一个聚类算法。

我读过不同的算法,比如k模式、摇滚、LIMBO,但是我想建立一个我的算法,并将其精度和成本与其他算法进行比较。

我有(m)个训练集和(n=22)个功能

我的方法很简单:

  • 步骤1:我计算每个训练数据之间的jaccard相似度,形成一个(m*m)相似度矩阵
  • 第2步:然后我执行一些操作,找到最佳质心,并使用简单的k-均值方法找到簇

在执行k-means算法时,将使用我在步骤1中创建的相似性矩阵

total_columns=22
for i in range(0,data_set):
    for j in range(0,data_set):
        if j>=i:
            # Calculating jaccard similarity between two data rows i and j 
            for column in data_set.columns:    
                if data_orig[column][j]==data_new[column][i]:
                    common_count=common_count+1
            probability=common_count/float(total_columns)    
            fnl_matrix[i][j] =probability  
            fnl_matrix[j][i] =probability

我的fnl\u矩阵的部分快照(6行)如下所示:

我面临的问题是,当我创建(m*m)矩阵时,对于更大的数据集,我的性能会受到影响。即使对于8000行的较小数据集,创建相似性矩阵也需要花费难以忍受的时间。是否有任何方法可以调整我的代码或对矩阵做一些经济高效的事情。


共2个答案

匿名用户

解释过的Python代码很慢。真慢。

这就是为什么好的python工具包包含大量Cython代码,甚至C和Fortran代码(例如Numpy中的矩阵操作),并且只使用Python来驱动整个过程。

如果您尽可能多地使用numpy,您可能会大大加快代码的速度。或者如果你用Cython来代替。

与其对抗质心,不如考虑使用基于距离的聚类算法:

  • 层次聚合聚类(HAC),它期望距离矩阵
  • DBSCAN,它可以与任意距离工作。它甚至不需要距离矩阵,只需要一些阈值的相似项列表。
  • K-medoid/PAM当然也值得一试;但通常不会很快。

匿名用户

首先,计算Jaccard的方法似乎效率低下(如果不是错误的话)。您正在使用for循环,这可能是Python中最慢的方法。我建议您使用Python的set来存储行。集合提供了快速的交集,因为它们是哈希表,所有的计算都是用C/C而不是Python本身执行的。想象一下,r1r2是两行。

r1 = set(some_row1)
r2 = set(some_row2)
intersection_len = len(r1.intersect(r2))
union_len = len(r1) + len(r2) - intersection_len
jaccard = intersection_len / union_len

集合构造成本很高,因此最初应将所有行存储为集合。那你应该摆脱

for i in range(0,data_set):
    for j in range(0,data_set):

部分也是。请改为使用迭代工具。假设data_set是一个行列表。

for row1, row2 in itertools.combinations(data_set, r=2):
    ...

这个东西运行得更快,并且不再需要if j

from scipy.spatial import distance
from itertools import combinations
import numpy as np


def jaccard(set1, set2):
    intersection_len = set1.intersection(set2)
    union_len = len(set1) + len(set2) - intersection_len
    return intersection_len / union_len

original_data_set = [row1, row2, row3,..., row_m]
data_set = [set(row) for row in original_data_set]

jaccard_generator = (jaccard(row1, row2) for row1, row2 in combinations(data_set, r=2))
flattened_matrix = np.fromiter(jaccard_generator, dtype=np.float64)

# since flattened_matrix is the flattened upper triangle of the matrix
# we need to expand it.
normal_matrix = distance.squareform(flattened_matrix)
# replacing zeros with ones at the diagonal. 
normal_matrix += np.identity(len(data_set))

就这样。你有你的矩阵。从这点上,您可以考虑将这一块代码移植到Cython(没有太多的工作要做),您只需要以稍微不同的方式定义<代码> JACARDAR> /COD>函数,即为局部变量添加类型声明。比如:

cpdef double jaccard(set set1, set set2):
    cdef long intersection_len, union_len # or consider int 
    intersection_len = set1.intersection(set2)
    union_len = len(set1) + len(set2) - intersection_len
    return intersection_len / union_len

但我不确定这是否会正确编译(我的Cython经验非常有限)

另外,您可以使用numpy数组而不是setS,因为它们提供了类似的交集方法,并且也在C/C中运行,但是两个数组的交集大约需要O(n^2)时间,而两个哈希表(set对象)的交集需要O(n)时间,前提是冲突率接近于零。