我正在尝试使用sklearn对某些数据集进行K-means聚类。问题是其中一个维度是小时:0-23之间的数字,因此距离算法认为0离23非常远,因为从绝对意义上说它是。实际上,就我而言,小时0非常接近小时23.有没有办法让距离算法做某种形式的环绕,因此它计算出更“真实”的时差。 我正在做一些简单的事情,类似于以下内容:
from sklearn.cluster import KMeans
clusters = KMeans(n_clusters = 2)
data = vstack(data)
fit = clusters.fit(data)
classes = fit.predict(data)
data
元素看起来像[22, 418, 192]
,其中第一个元素是小时。
有什么想法吗?
答案 0 :(得分:3)
即使@elyase答案被接受,我认为不正确的方法。
是的,要使用这样的距离,你必须改进你的距离测量,所以 - 使用不同的库。但更重要的是 - k-means中使用的 mean 概念不适合循环维度。让我们考虑以下示例:
#current cluster X,, based on centroid position Xc=24
x1=1
x2=24
#current cluster Y, based on centroid position Yc=10
y1=12
y2=13
计算简单算术平均值会将中心点放在Xc=12.5
,Yc=12.5
中,从循环meausre的角度来看,它应该是Xc=0.5
,Yc=12.5
。如您所见,基于循环距离测量的对齐与简单的平均操作不“兼容”,并导致结果。
{x1,y1}, {x2,y2}
{x1,x2,y1,y2}
{x1,x2},{y1,y2}
解决这个问题需要检查一个(是否更好地测量“简单平均值”或将其中一个点表示为x'=x-24
)。不幸的是,考虑到n
分,它会产生2^n
种可能性。
这似乎是核化k-means 的一个用例,你实际上是在抽象特征空间中聚集(在你的情况下 - 一个围绕时间维度滚动的“管”)内核(“相似性度量”,是某些向量空间的内在产物)。
内核k-means 的详细信息为here
答案 1 :(得分:1)
K-means不是基于距离的算法。
K-means最小化了群内平方和,这是一种方差(它大致是所有群集的加权平均方差,其中每个对象和维度的权重相同)。
为了使Lloyds算法收敛,您需要两个步骤优化相同的功能:
现在,“均值”函数是最小二乘估计。即选择步骤2中的均值对于WCSS目标是最佳的。通过步骤1中的最小二乘偏差(=欧几里德平方距离,单调到欧几里德距离)来分配对象也得到有保证的收敛。 平均值正是你的环绕式想法会崩溃的地方。
如果按照@elyase 的建议插入一个随机的其他距离函数,k-means可能不再收敛。
有各种解决方案:
sin(hour / 12 * pi), cos(hour / 12 * pi)
,这可能适用于SSQ。答案 2 :(得分:0)
对我来说,最简单的方法是通过计算维度的"circular mean"来适应K-means算法的环绕维度。当然,您还需要相应地更改质心距离计算。
#compute the mean of hour 0 and 23
import numpy as np
hours = np.array(range(24))
#hours to angles
angles = hours/24 * (2*np.pi)
sin = np.sin(angles)
cos = np.cos(angles)
a = np.arctan2(sin[23]+sin[0], cos[23]+cos[0])
if a < 0: a += 2*np.pi
#angle back to hour
hour = a * 24 / (2*np.pi)
#23.5