第一次接触医疗数据集时,我完全被2126条胎心监护记录搞懵了。这些数据包含22个维度的指标,从基础心率到宫缩强度,每个数字背后都可能藏着胎儿健康的关键信息。但人脑根本无法同时处理这么多维度的数据关系,这时候聚类分析就像一台精密的"数据显微镜"。
聚类分析的本质是让数据自己说话。通过算法自动发现隐藏的分组模式,比如把胎心数据分成"正常"、"疑似"、"病态"三类。我在三甲医院合作项目中发现,有经验的产科医生凭直觉做出的分类判断,往往与ward.D2算法的聚类结果高度一致。这印证了算法的可靠性。
实际工作中常遇到三类需求:
提示:医疗数据聚类前务必进行标准化处理,避免量纲差异导致心率指标(单位:bpm)主导宫缩频率(单位:%)的情况
拿到CTG数据集时,原始Excel文件有23个变量。我习惯先用summary()快速扫描:
r复制raw_data <- read.csv("ctg_raw.csv")
summary(raw_data[,1:5])
输出显示NSP列有3%缺失值,这时新手常犯两个错误:
我的解决方案是:
r复制clean_data <- na.omit(raw_data[,-23]) # 删除NSP列
data <- clean_data[sample(nrow(clean_data), 1500), ] # 随机抽样避免过载
不同聚类算法对标准化要求不同。K-means必须标准化,而层次聚类可以选用相关系数。我常用的标准化组合拳:
r复制library(cluster)
scaled_data <- scale(data[,1:20]) # 连续变量标准化
mixed_data <- cbind(scaled_data, data[,21:22]) # 保留分类变量
曾有个项目因忽略Tendency变量的有序分类特性(-1,0,1),导致聚类结果完全失真。后来我改用Gower距离处理混合数据类型:
r复制library(proxy)
gower_dist <- daisy(mixed_data, metric = "gower")
层次聚类就像观察生物进化树,能清晰展现数据聚合过程。测试六种连接方法:
r复制methods <- c("single", "complete", "average", "centroid", "ward.D", "ward.D2")
par(mfrow=c(2,3))
for(m in methods){
hc <- hclust(dist(scaled_data), method=m)
plot(hc, main=m, hang=-1)
}
实测发现:
传统K-means在医疗数据中表现不稳定,我改进后的流程:
r复制library(factoextra)
fviz_nbclust(scaled_data, kmeans, method = "wss") +
geom_vline(xintercept = 3, linetype=2)
r复制pca_res <- prcomp(scaled_data)
km_res <- kmeans(pca_res$x[,1:3], centers=3, nstart=25)
r复制library(ggfortify)
autoplot(km_res, data=pca_res$x, frame=TRUE)
r复制library(cluster)
sil <- silhouette(km_res$cluster, dist(scaled_data))
mean(sil[,3]) # 值越接近1越好
r复制library(fpc)
clusterboot(scaled_data, B=20, clustermethod=kmeansCBI, k=3)
r复制aggregate(data[,1:5], by=list(Cluster=km_res$cluster), median)
动态交互图比静态图更利于发现模式:
r复制library(plotly)
p <- plot_ly(x=pca_res$x[,1], y=pca_res$x[,2],
color=factor(km_res$cluster), type="scatter", mode="markers")
htmlwidgets::saveWidget(p, "cluster.html")
制作热图展示变量重要性:
r复制library(pheatmap)
pheatmap(cor(scaled_data)[order(km_res$cluster),],
cluster_rows=FALSE)
r复制library(bigmemory)
big_data <- as.big.matrix(scaled_data)
big_dist <- bigmemory::dist(big_data)
对于10万+样本:
r复制library(fastcluster)
hc_fast <- hclust.vector(scaled_data, method="ward")
GPU加速方案:
r复制library(ClusterR)
gpu_km <- KMeans_rcpp(scaled_data, clusters=3, initializer="kmeans++")
记得在医疗场景中,解释性比算法复杂度更重要。有次我用DBSCAN发现了异常病例,但医生更认可层次聚类的树状图解释。这提醒我们:没有最好的算法,只有最合适的解释。