如何在R语言中仅通过数据与聚类标签手动计算组间平方和(BCSS)与总平方和
Got it!我完全理解你的需求——不用依赖kmeans()的输出,只凭原始数据和聚类标签就能算出这些关键统计量,这样不管用什么聚类算法(比如层次聚类、DBSCAN之后手动分配的标签)都能复用这套方法。下面我一步步给你讲清楚怎么实现,还用你给的iris数据做验证,确保和kmeans()的结果完全一致。
先明确核心统计量的定义
在动手写代码前,先理清这三个统计量的数学逻辑,这样你也能灵活调整:
- 总平方和(Total SS):所有样本点到全局均值的平方距离之和,它衡量的是数据整体的离散程度,和聚类结果无关。
- 组内平方和(Within SS):每个簇内的样本点到该簇均值的平方距离之和,衡量簇内的紧密程度。
- 组间平方和(Between SS):每个簇的均值到全局均值的平方距离,乘以该簇的样本数后求和;或者直接用总平方和减去组内平方和(因为总平方和 = 组内平方和 + 组间平方和)。
步骤1:准备测试数据和聚类标签
先用你给的例子生成数据和聚类标签,方便后续验证:
# 加载iris数据的前4个特征 data <- iris[1:4] # 用kmeans生成聚类标签(仅用于验证,实际你可以替换成任意聚类结果) fit <- kmeans(data, 3) clusters <- fit$cluster
步骤2:计算总平方和(Total SS)
有两种等价的计算方式,选你顺手的就行:
方式1:直接计算每个样本到全局均值的平方距离之和
# 计算全局均值(所有样本的特征均值) global_mean <- colMeans(data) # 遍历每个样本,计算到全局均值的平方距离,再求和 totss <- sum(apply(data, 1, function(x) sum((x - global_mean)^2))) totss #> [1] 681.3706
方式2:用方差推导(更高效)
总平方和等于每个特征的(样本数-1)*方差之和,因为方差的定义是平方和/(n-1),反过来就能得到平方和:
totss_alt <- sum((nrow(data)-1)*apply(data, 2, var)) totss_alt #> [1] 681.3706
两种方式结果完全一致,和fit$totss的输出匹配。
步骤3:计算组内平方和(Within SS)
你提到已经从其他帖子得到了这个方法,这里再写一遍完整代码,方便对照:
withinss <- 0 # 遍历每个聚类标签 for (cluster in unique(clusters)) { # 提取当前簇的所有样本 cluster_data <- data[clusters == cluster, ] # 计算当前簇的均值 cluster_mean <- colMeans(cluster_data) # 累加当前簇内所有样本到簇均值的平方距离 withinss <- withinss + sum(apply(cluster_data, 1, function(x) sum((x - cluster_mean)^2))) } withinss #> [1] 78.85144
这个结果和sum(fit$withinss)完全一致(因为fit$withinss是每个簇单独的组内平方和,求和就是总组内平方和)。
步骤4:计算组间平方和(Between SS)
同样有两种等价方式:
方式1:总平方和减组内平方和(最简单)
betweenss <- totss - withinss betweenss #> [1] 602.5192
方式2:直接计算簇均值到全局均值的加权平方和
betweenss_alt <- 0 global_mean <- colMeans(data) for (cluster in unique(clusters)) { # 当前簇的样本数量 cluster_size <- sum(clusters == cluster) # 当前簇的均值 cluster_mean <- colMeans(data[clusters == cluster, ]) # 累加:簇大小 * 簇均值到全局均值的平方距离 betweenss_alt <- betweenss_alt + cluster_size * sum((cluster_mean - global_mean)^2) } betweenss_alt #> [1] 602.5192
两种方式的结果都和fit$betweenss完全匹配。
封装成可复用的函数
如果经常需要计算,可以把这些逻辑封装成一个函数,输入数据和聚类标签就能直接得到三个统计量:
cluster_ss <- function(data, clusters) { # 计算总平方和 global_mean <- colMeans(data) totss <- sum(apply(data, 1, function(x) sum((x - global_mean)^2))) # 计算组内平方和 withinss <- 0 for (cluster in unique(clusters)) { cluster_data <- data[clusters == cluster, ] cluster_mean <- colMeans(cluster_data) withinss <- withinss + sum(apply(cluster_data, 1, function(x) sum((x - cluster_mean)^2))) } # 计算组间平方和 betweenss <- totss - withinss # 返回结果列表 return(list( total_ss = totss, within_ss = withinss, between_ss = betweenss )) } # 测试函数 result <- cluster_ss(data, clusters) result$total_ss # 对应fit$totss #> [1] 681.3706 result$between_ss # 对应fit$betweenss #> [1] 602.5192 result$within_ss # 对应sum(fit$withinss) #> [1] 78.85144
这样不管你用什么聚类方法得到的标签,只要传入数据和标签,就能快速算出这三个关键统计量啦!
内容的提问来源于stack exchange,提问作者Dominique Makowski




