使用k-means聚类时如何确定k?

我一直在研究k-means聚类 ,有一点不清楚的是你如何selectk的值。 这只是一个反复试验的问题,还是还有更多呢?

您可以最大化贝叶斯信息准则(BIC):

 BIC(C | X) = L(X | C) - (p / 2) * log n 

其中L(X | C)是根据模型C的数据集X对数似然性, p是模型C中参数的数量, n是数据集中的点数。 请参阅Dan Pelleg和Andrew Moore在ICML 2000中的“X-means:扩展具有有效估计群集数量的K -means ” 。

另一种方法是从一个大的k值开始,继续删除质心(减lessk),直到不再缩短描述长度。 参见Horst Bischof,Ales Leonardis和Alexander Selb在“ Pattern Analysis and Applications”模式分析和应用) 2,p。 59-72,1999。

最后,您可以从一个群集开始,然后保持分裂群集,直到分配给每个群集的点具有高斯分布。 格雷格·哈默利(Greg Hamerly)和查尔斯·埃尔坎(Charles Elkan)在“学习k -means ” (NIPS 2003)中展示了一些证据,certificate这比BIC好得多,BIC不足以惩罚模型的复杂性。

基本上,您想要在两个variables之间find平衡点:聚类数( k )和聚类的平均方差。 你想尽量减less前者,同时也要尽量减less后者。 当然,随着聚类数量的增加,平均方差减小(直到k = n和方差= 0的微不足道的情况)。

和往常一样,在数据分析中,没有一个真正的方法在所有情况下都比其他方法更好。 最后,你必须用自己最好的判断。 为此,有助于根据平均方差(假定您已经为几个k值运行algorithm)绘制聚类数量。 然后,您可以使用曲线拐点处的聚类数量。

是的,你可以使用Elbow方法find最佳的聚类数量,但是我发现使用脚本从肘形图中find聚类的值是很麻烦的。 你可以观察肘关节图并自己find肘关节点,但是从脚本中find它是很多的工作。

所以另一个select是使用Silhouette方法来find它。 Silhouette的结果完全符合R中Elbow方法的结果。

这是我做的。

 #Dataset for Clustering n = 150 g = 6 set.seed(g) d <- data.frame(x = unlist(lapply(1:g, function(i) rnorm(n/g, runif(1)*i^2))), y = unlist(lapply(1:g, function(i) rnorm(n/g, runif(1)*i^2)))) mydata<-d #Plot 3X2 plots attach(mtcars) par(mfrow=c(3,2)) #Plot the original dataset plot(mydata$x,mydata$y,main="Original Dataset") #Scree plot to deterine the number of clusters wss <- (nrow(mydata)-1)*sum(apply(mydata,2,var)) for (i in 2:15) { wss[i] <- sum(kmeans(mydata,centers=i)$withinss) } plot(1:15, wss, type="b", xlab="Number of Clusters",ylab="Within groups sum of squares") # Ward Hierarchical Clustering d <- dist(mydata, method = "euclidean") # distance matrix fit <- hclust(d, method="ward") plot(fit) # display dendogram groups <- cutree(fit, k=5) # cut tree into 5 clusters # draw dendogram with red borders around the 5 clusters rect.hclust(fit, k=5, border="red") #Silhouette analysis for determining the number of clusters library(fpc) asw <- numeric(20) for (k in 2:20) asw[[k]] <- pam(mydata, k) $ silinfo $ avg.width k.best <- which.max(asw) cat("silhouette-optimal number of clusters:", k.best, "\n") plot(pam(d, k.best)) # K-Means Cluster Analysis fit <- kmeans(mydata,k.best) mydata # get cluster means aggregate(mydata,by=list(fit$cluster),FUN=mean) # append cluster assignment mydata <- data.frame(mydata, clusterid=fit$cluster) plot(mydata$x,mydata$y, col = fit$cluster, main="K-means Clustering results") 

希望它有帮助!

请看这篇论文,Charles Elkan的Greg Hamerly的“用k-means学习k”。 它使用高斯testing来确定正确的聚类数量。 另外,作者声称这种方法比接受的答案中提到的BIC好。

首先build立你的数据的最小生成树 。 去除K-1最昂贵的边将树分成K个簇,
所以你可以build立MST一次,查看各种K的集群间距/度量标准,并且采取曲线的拐点。

这仅适用于Single-linkage_clustering ,但为此,它非常快速和简单。 另外,MST可以制作出很好的视觉效果。
请参阅stats.stackexchange可视化软件下的MST图以进行群集 。

有一些叫做经验法则。 它说聚类的数量可以通过k =(n / 2)^ 0,5来计算,其中n是样本中元素的总数。 你可以在下面的文章中检查这个信息的真实性:

docs/paper/volume1/issue6/V1I6-0015.pdf

还有另一种称为G均值的方法,其中的分布遵循高斯分布或正态分布。 它包括增加k,直到你所有的k组遵循高斯分布。 它需要大量的统计数据,但是可以完成。 这里是来源:

NIPS-2003-learning-the-k-in-k-means-Paper.pdf

我希望这有帮助!

如果你使用MATLAB,自2013b以来的任何版本,你可以使用函数evalclusters来找出最佳的k应该为给定的数据集。

这个function可以让您从3种聚类algorithm中select – kmeanslinkagegmdistribution

它还可以让您从4个集群评估标准 – CalinskiHarabaszDaviesBouldingapsilhouette

我的想法是使用轮廓系数来find最佳聚类数(K)。 详细说明在这里 。

假设你有一个名为DATA的数据matrix,你可以执行分区周围medoids估计的群集数量(通过轮廓分析),如下所示:

 library(fpc) maxk <- 20 # arbitrary here, you can set this to whatever you like estimatedK <- pamk(dist(DATA), krange=1:maxk)$nc 

我很惊讶没有人提到这个优秀的文章: http : //www.ee.columbia.edu/~dpwe/papers/PhamDN05-kmeans.pdf

在阅读以下其他几条build议之后,我终于看到这篇文章: https : //datasciencelab.wordpress.com/2014/01/21/selection-of-k-in-k-means-clustering-reloaded/

之后,我在Scala中实现了它,这个实现为我的用例提供了非常好的结果。 这是代码:

 import breeze.linalg.DenseVector import Kmeans.{Features, _} import nak.cluster.{Kmeans => NakKmeans} import scala.collection.immutable.IndexedSeq import scala.collection.mutable.ListBuffer /* https://datasciencelab.wordpress.com/2014/01/21/selection-of-k-in-k-means-clustering-reloaded/ */ class Kmeans(features: Features) { def fkAlphaDispersionCentroids(k: Int, dispersionOfKMinus1: Double = 0d, alphaOfKMinus1: Double = 1d): (Double, Double, Double, Features) = { if (1 == k || 0d == dispersionOfKMinus1) (1d, 1d, 1d, Vector.empty) else { val featureDimensions = features.headOption.map(_.size).getOrElse(1) val (dispersion, centroids: Features) = new NakKmeans[DenseVector[Double]](features).run(k) val alpha = if (2 == k) 1d - 3d / (4d * featureDimensions) else alphaOfKMinus1 + (1d - alphaOfKMinus1) / 6d val fk = dispersion / (alpha * dispersionOfKMinus1) (fk, alpha, dispersion, centroids) } } def fks(maxK: Int = maxK): List[(Double, Double, Double, Features)] = { val fadcs = ListBuffer[(Double, Double, Double, Features)](fkAlphaDispersionCentroids(1)) var k = 2 while (k <= maxK) { val (fk, alpha, dispersion, features) = fadcs(k - 2) fadcs += fkAlphaDispersionCentroids(k, dispersion, alpha) k += 1 } fadcs.toList } def detK: (Double, Features) = { val vals = fks().minBy(_._1) (vals._3, vals._4) } } object Kmeans { val maxK = 10 type Features = IndexedSeq[DenseVector[Double]] } 

一个可能的答案是使用类似遗传algorithm的元启发式algorithm来寻找k。 这很简单。 你可以使用随机K(在一定的范围内),并通过一些测量(如Silhouette)来评估遗传algorithm的拟合函数。

https://en.wikipedia.org/wiki/Silhouette_(clustering);

 km=[] for i in range(num_data.shape[1]): kmeans = KMeans(n_clusters=ncluster[i])#we take number of cluster bandwidth theory ndata=num_data[[i]].dropna() ndata['labels']=kmeans.fit_predict(ndata.values) cluster=ndata co=cluster.groupby(['labels'])[cluster.columns[0]].count()#count for frequency me=cluster.groupby(['labels'])[cluster.columns[0]].median()#median ma=cluster.groupby(['labels'])[cluster.columns[0]].max()#Maximum mi=cluster.groupby(['labels'])[cluster.columns[0]].min()#Minimum stat=pd.concat([mi,ma,me,co],axis=1)#Add all column stat['variable']=stat.columns[1]#Column name change stat.columns=['Minimum','Maximum','Median','count','variable'] l=[] for j in range(ncluster[i]): n=[mi.loc[j],ma.loc[j]] l.append(n) stat['Class']=l stat=stat.sort(['Minimum']) stat=stat[['variable','Class','Minimum','Maximum','Median','count']] if missing_num.iloc[i]>0: stat.loc[ncluster[i]]=0 if stat.iloc[ncluster[i],5]==0: stat.iloc[ncluster[i],5]=missing_num.iloc[i] stat.iloc[ncluster[i],0]=stat.iloc[0,0] stat['Percentage']=(stat[[5]])*100/count_row#Freq PERCENTAGE stat['Cumulative Percentage']=stat['Percentage'].cumsum() km.append(stat) cluster=pd.concat(km,axis=0)## see documentation for more info cluster=cluster.round({'Minimum': 2, 'Maximum': 2,'Median':2,'Percentage':2,'Cumulative Percentage':2})