如何为 kmeans 聚类重新关联原始标签并用 fviz_cluster 绘图?
可以把 type 作为文字标签添加到 fviz_cluster() 返回的图中,无须放回参与聚类的数值数据。先只绘制点,再将原始类别与绘图坐标对应起来,用 geom_text_repel() 添加标签。
为什么要单独添加标签
kmeans() 根据数值变量计算距离,type 应保留为样本的类别信息,供聚类后比较。将因子转换成整数后参与聚类,会让类别编码影响距离计算。
图中用颜色和文字区分聚类结果与原始类别,便于查看每个聚类包含哪些原始类别:
- 点的颜色表示
kmeans()得到的聚类。 - 点旁的文字显示原始类别
occurrence或random。
完整代码
聚类使用原示例中的三个变量:water、barren 和 evergreen。
library(factoextra)
library(ggplot2)
library(ggrepel)
type <- c(rep("occurrence", 3), rep("random", 3))
water <- c(18.8, rep(17.2, 4), 16.6)
barren <- c(3.34, rep(3.20, 4), 3.21)
evergreen <- c(75.5, rep(72.2, 4), 77.8)
mixed <- c(0.461, rep(0.457, 4), 0.459)
test_frame <- data.frame(
type, water, barren, evergreen, mixed
)
# 只选取参与聚类的数值变量
cluster_test <- test_frame[
, c("water", "barren", "evergreen"),
drop = FALSE
]
set.seed(123)
kc_test <- kmeans(cluster_test, centers = 2, nstart = 25)
# 先画点,关闭默认文字标签
p <- fviz_cluster(
kc_test,
data = cluster_test,
geom = "point",
stand = FALSE,
ellipse = FALSE,
show.clust.cent = FALSE
)
# 根据样本行名,将原始类别关联到绘图数据
label_data <- p$data
label_data$type <- as.character(
test_frame$type[
match(as.character(label_data$name), rownames(test_frame))
]
)
# 在相同坐标上添加类别文字
p + geom_text_repel(
data = label_data,
aes(x = x, y = y, label = type),
inherit.aes = FALSE,
color = "black",
max.overlaps = Inf
)
p$data 包含绘图坐标,其中的 name 对应样本行名。用 match() 找到每个样本的 type 后,通过 label = type 指定标签文字,原有的聚类配色仍会保留。
输入的数值变量超过两个时,fviz_cluster() 会通过主成分分析生成二维坐标。标签层直接使用图中的 x、y,才能与点的位置一致。
为什么调整这些绘图参数
geom = "point" 关闭默认的样本编号标签,避免两套文字同时出现。geom_text_repel() 用于减少新增标签之间的遮挡。
示例中的 kmeans() 使用原始百分比聚类,因此设置 stand = FALSE,让绘图也使用未标准化的数据。如果需要先标准化再聚类,应先执行 scale(),然后将同一份标准化数据同时传给 kmeans() 和 fviz_cluster()。
这组数据的样本很少,且有多行数值完全相同,因此关闭了椭圆和聚类中心的显示。max.overlaps = Inf 允许保留所有文字标签,但不能保证每种布局都清晰。
关联标签时需要注意什么
不要直接执行:
rownames(cluster_test) <- test_frame$type
数据框的行名必须唯一,而 occurrence 和 random 都有重复。它们适合作为标签列,样本行名应使用唯一标识。
上述代码要求 cluster_test 与 test_frame 的样本行名保持对应。如果聚类前删除了缺失值、筛选或重排了样本,应同步处理类别信息,或通过唯一的样本 ID 关联。
原代码中的 test_frame[, 2:4] 不包含 mixed。如果也要让它参与聚类,需要修改变量选择并重新运行聚类:
cluster_test <- test_frame[
, c("water", "barren", "evergreen", "mixed"),
drop = FALSE
]
示例中第 2 至第 5 行的数值相同,即使原始类别不同,在图中的位置也会重合。文字避让只会移动标签,不会将这些样本点分开。
如何直接比较原始类别与聚类结果
除了看图,还可以查看交叉表:
table(
type = test_frame$type,
cluster = kc_test$cluster
)
这里要求 test_frame 的行顺序与聚类输入一致。交叉表显示各聚类中两类样本的数量,聚类编号 1、2 本身没有预先定义的类别含义。
备注:内容仅供参考。
