计算k-means距离时的环绕

时间:2013-09-08 22:49:33

标签: python machine-learning cluster-analysis classification

我正在尝试使用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],其中第一个元素是小时。

有什么想法吗?

3 个答案:

答案 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.5Yc=12.5中,从循环meausre的角度来看,它应该是Xc=0.5Yc=12.5 。如您所见,基于循环距离测量的对齐与简单的平均操作不“兼容”,并导致结果。

  • 简单的k-means将导致群集{x1,y1}, {x2,y2}
  • 简单k - 表示退化超集群{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不是基于距离的算法。

K-means最小化了群内平方和,这是一种方差(它大致是所有群集的加权平均方差,其中每个对象和维度的权重相同)。

为了使Lloyds算法收敛,您需要两个步骤优化相同的功能:

  • 重新分配步骤
  • 质心更新步骤

现在,“均值”函数是最小二乘估计。即选择步骤2中的均值对于WCSS目标是最佳的。通过步骤1中的最小二乘偏差(=欧几里德平方距离,单调到欧几里德距离)来分配对象得到有保证的收敛。 平均值正是你的环绕式想法会崩溃的地方

如果按照@elyase 的建议插入一个随机的其他距离函数,k-means可能不再收敛

正确的解决方案

有各种解决方案:

  • 使用K-medoids(PAM)。通过选择 medoid而不是平均值,您可以确保在任意距离内收敛。但是,计算medoid是相当昂贵的。
  • 将数据转换为内核空间,您可以最大限度地减少平方和。例如,您可以将小时转换为sin(hour / 12 * pi), cos(hour / 12 * pi),这可能适用于SSQ。
  • 使用其他基于距离的群集算法。 K-means已经过时了,自那时起就有很多关于聚类的研究。您可能希望从层次聚类开始(实际上与k-means一样古老),然后尝试DBSCAN及其变体。

答案 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