如何计算质心和数据矩阵之间的距离(用于 kmeans 算法)

Posted

技术标签:

【中文标题】如何计算质心和数据矩阵之间的距离(用于 kmeans 算法)【英文标题】:How to compute distances between centroids and data matrix (for kmeans algorithm) 【发布时间】:2015-01-20 20:08:56 【问题描述】:

我是聚类和 R 的学生。为了更好地掌握两者,我想计算每次迭代的质心和我的 xy 矩阵之间的距离,直到它“收敛”。如何使用 R 解决第 2 步和第 3 步?

library(fields)
x <- c(3,6,8,1,2,2,6,6,7,7,8,8)
y <- c(5,2,3,5,4,6,1,8,3,6,1,7)

df <- data.frame(x,y) initial matrix
a  <- c(3,6,8)
b  <- c(5,2,3)

df1 <- data.frame(a,b) # initial centroids

这是我想做的:

    I0 &lt;- t(rdist(df, df1)) 零迭代后 根据最小距离对对象进行聚类 根据聚类平均值确定质心 I1 重复

我尝试了kmeans 功能。但由于某些原因,它会产生那些必须在最后出现的质心。那就是我定义的开始:

start   <- matrix(c(3,5,6,2,8,3), 3, byrow = TRUE)
cluster <- kmeans(df,centers = start, iter.max = 1) # one iteration

kmeans 不允许我跟踪质心的运动。因此,我想通过使用 R 应用第 2 步和第 3 步来“手动”完成。

【问题讨论】:

我建议您将问题缩小到特定部分。 我希望这是具体的?! 好的,现在向我们展示您尝试过的内容以及出错的地方。 我希望这很清楚。不确定我是否必须指定它。 【参考方案1】:

您的主要问题似乎是如何计算数据矩阵与一组点(“中心”)之间的距离。

为此,您可以编写一个函数,将数据矩阵和点集作为输入,并将数据矩阵中每一行(点)的距离返回到所有“中心”。

这是这样一个函数:

myEuclid <- function(points1, points2) 
    distanceMatrix <- matrix(NA, nrow=dim(points1)[1], ncol=dim(points2)[1])
    for(i in 1:nrow(points2)) 
        distanceMatrix[,i] <- sqrt(rowSums(t(t(points1)-points2[i,])^2))
    
    distanceMatrix

points1 是以点为行、维度为列的数据矩阵。 points2 是中心矩阵(再次以行表示)。第一行代码只定义了答案矩阵(它的行数与数据矩阵中的行数一样多,列数与中心数一样多)。所以结果矩阵中的点i,j就是ith点到jth中心的距离。

然后 for 循环遍历所有中心。对于每个中心,它计算从每个点到当前中心的欧几里得距离并返回结果。这里的这条线:sqrt(rowSums(t(t(points1)-points2[i,])^2)) 是欧几里得距离。如果您对此有任何问题,请仔细检查并查找公式。 (那里的转置主要是为了确保减法是按行进行的)。

现在你也可以实现k-means算法了:

myKmeans <- function(x, centers, distFun, nItter=10) 
    clusterHistory <- vector(nItter, mode="list")
    centerHistory <- vector(nItter, mode="list")

    for(i in 1:nItter) 
        distsToCenters <- distFun(x, centers)
        clusters <- apply(distsToCenters, 1, which.min)
        centers <- apply(x, 2, tapply, clusters, mean)
        # Saving history
        clusterHistory[[i]] <- clusters
        centerHistory[[i]] <- centers
    

    list(clusters=clusterHistory, centers=centerHistory)

如您所见,它也是一个非常简单的函数 - 它需要数据矩阵、中心、距离函数(上面定义的函数)和想要的迭代次数。

通过为每个点分配最近的中心来定义集群。并且中心被更新为分配给该中心的点的平均值。这是一个基本的 k-means 算法)。

让我们试一试。定义一些随机点(在 2d 中,所以列数 = 2)

mat <- matrix(rnorm(100), ncol=2)

从该矩阵中指定 5 个随机点作为初始中心:

centers <- mat[sample(nrow(mat), 5),]

现在运行算法:

theResult <- myKmeans(mat, centers, myEuclid, 10)

这是第 10 次迭代中的中心:

theResult$centers[[10]]
        [,1]        [,2]
1 -0.1343239  1.27925285
2 -0.8004432 -0.77838017
3  0.1956119 -0.19193849
4  0.3886721 -1.80298698
5  1.3640693 -0.04091114

与实现的kmeans函数比较:

theResult2 <- kmeans(mat, centers, 10, algorithm="Forgy")

theResult2$centers
        [,1]        [,2]
1 -0.1343239  1.27925285
2 -0.8004432 -0.77838017
3  0.1956119 -0.19193849
4  0.3886721 -1.80298698
5  1.3640693 -0.04091114

工作正常。然而,我们的函数会跟踪迭代。我们可以像这样绘制前 4 次迭代的进度:

par(mfrow=c(2,2))
for(i in 1:4) 
    plot(mat, col=theResult$clusters[[i]], main=paste("itteration:", i), xlab="x", ylab="y")
    points(theResult$centers[[i]], cex=3, pch=19, col=1:nrow(theResult$centers[[i]]))

不错。

但是,这种简单的设计可以实现更多功能。例如,如果我们想使用另一种距离(不是欧几里得),我们可以使用任何将数据和中心作为输入的函数。这是一个相关距离:

myCor <- function(points1, points2) 
    return(1 - ((cor(t(points1), t(points2))+1)/2))

然后我们可以根据这些来做 Kmeans:

theResult <- myKmeans(mat, centers, myCor, 10)

4 次迭代的结果图片如下所示:

即使我们指定了 5 个集群 - 最后还剩下 2 个。这是因为对于 2 维,相关性可能具有值 - +1 或 -1。然后在寻找集群时,每个点都被分配到一个中心,即使它与多个中心的距离相同 - 选择第一个。

无论如何,这已经超出了范围。最重要的是,有许多可能的距离度量,一个简单的函数允许您使用任何您想要的距离并跟踪迭代的结果。

【讨论】:

你刚刚成功了。你向我解释了整个概念非常好,我希望我能给你更多的信任。【参考方案2】:

修改了上面的距离矩阵函数(为点数添加了另一个循环),因为上面的函数只显示第一个点与所有簇的距离,而不是所有点,这就是问题所要寻找的:

myEuclid <- function(points1, points2) 
    distanceMatrix <- matrix(NA, nrow=dim(points1)[1], ncol=dim(points2)[1])
    for(i in 1:nrow(points2)) 
        for (j in c(1:dim(t(points1))[2])) 
            
        distanceMatrix[j,i] <- sqrt(rowSums(t(t(points1)[,j]-t(points2[i,]))^2))
            
    
    distanceMatrix

如果这工作正常,请告诉我!

【讨论】:

以上是关于如何计算质心和数据矩阵之间的距离(用于 kmeans 算法)的主要内容,如果未能解决你的问题,请参考以下文章

在单个特征数据框中查找质心和点之间的距离 - KMeans

sklearn KMeans聚类算法(总结)

郑捷《机器学习算法原理与编程实践》学习笔记(第四章 推荐系统原理)kmeans

如何在 Scikit KMeans 中使用预先计算的距离矩阵?

如何在 R 中为 kmeans 指定距离度量?

如何使用 tSNE 和 kmeans 质心找到质心对应的原始数据点?