You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在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

火山引擎 最新活动