我在尝试使用sklearn对某个数据集进行K-means聚类。问题在于其中一个维度是小时数:从0到23的数字,因此距离算法认为0和23之间的距离非常远,因为从绝对值上看确实如此。但实际上,对于我的目的来说,0点和23点非常接近。有没有办法让距离算法进行某种形式的环绕计算,从而计算出更‘真实’的时间差?我在做一些简单的事情,类似于以下内容:
from sklearn.cluster import KMeansclusters = KMeans(n_clusters = 2)data = vstack(data)fit = clusters.fit(data)classes = fit.predict(data)
data
元素看起来像[22, 418, 192]
,其中第一个元素是小时数。
有什么想法吗?
回答:
为什么k-means不适用于任意距离
K-means不是基于距离的算法。
K-means最小化簇内平方和,这是一种方差(它大致是所有簇的加权平均方差,其中每个对象和维度都被赋予相同的权重)。
为了让Lloyd算法收敛,你需要让两个步骤优化同一个函数:
- 重新分配步骤
- 质心更新步骤
现在,“均值”函数是一个最小二乘估计器。也就是说,在步骤2中选择均值对于WCSS目标是最优的。在步骤1中通过最小二乘偏差(=平方欧几里得距离,与欧几里得距离单调)分配对象,同样保证了收敛。均值正是你的环绕想法会失效的地方。
如果你像@elyase建议的那样插入一个随机的其他距离函数,k-means可能不再收敛。
适当的解决方案
对此有各种解决方案:
- 使用K-medoids(PAM)。通过选择中位数而不是均值,你可以保证使用任意距离时收敛。然而,计算中位数相当昂贵。
- 将数据转换到一个核空间,在那里你可以接受最小化平方和。例如,你可以将小时数转换为
sin(hour / 12 * pi), cos(hour / 12 * pi)
,这对于SSQ可能是可以接受的。 - 使用其他基于距离的聚类算法。K-means已经很老了,自那以后在聚类方面已经进行了大量研究。你可以从层次聚类开始(实际上它和k-means一样老),然后尝试DBSCAN及其变体。