2. 聚类算法基础概念归纳总结
让我先问一个问题:为什么空间转录组数据需要聚类?
说白了,组织切片上的每个 spot 都测到了几百到几千个基因的表达量。你想知道哪些 spot 属于同一个细胞类型或功能区域。聚类就是帮我们做这件事的——把相似的 spot 归到一堆,不相似的隔开。
我刚开始接触 Visium 数据时,天真地以为随便拿个 K-means 就能搞定。结果一跑出来,组织形态完全不对——同一个解剖区域的 spots 被分到了不同簇,空间连续性全丢了。嗯,后来才意识到,在空间转录组里,聚类不仅要看基因表达,还得考虑空间位置。所以这一章,我们先打基础,把聚类算法的核心概念理清楚。
2.1 聚类到底是什么?
聚类是一种无监督学习。我们不给算法提供任何标签,只给它数据点(每个 spot 的特征向量)。算法自己决定哪些点应该分到一起。
- 目标:让同一簇内的点尽可能相似,不同簇的点尽可能不同。
- 输入:一个 N×M 的矩阵,N 个 spot,M 个特征(基因表达量 + 可选空间坐标)。
- 输出:每个 spot 的簇标签(0,1,2,...)。
啊对了——在 Visium 场景下,我们通常会把 空间坐标 (x,y) 作为额外特征拼进表达矩阵,也可以单独构建 空间邻近图 作为约束。这个后面会细讲。
2.2 核心要素:距离度量
所有聚类算法都依赖“相似度”或“距离”。选哪个距离,直接影响结果。我个人习惯先看数据分布:
| 距离类型 | 公式 | 适用场景 |
|---|---|---|
| 欧氏距离 | √∑(xᵢ-yᵢ)² | 连续型数值,最常用 |
| 曼哈顿距离 | ∑|xᵢ-yᵢ| | 高维稀疏,或特征量纲差异大 |
| 余弦相似度 | (x·y)/(||x||·||y||) | 表达谱方向(如归一化后的基因表达) |
| 相关性距离 | 1 - Pearson r | 想要忽略绝对表达水平,只看表达模式 |
| 图距离(最短路径) | 节点间最短边权之和 | 基于空间邻接图时 |
我在一个项目中遇到基因表达量跨度很大(从 0 到几万),直接用欧氏距离让高表达基因主导了聚类。后来做了 log1p 归一化,再用余弦相似度,才把组织区域分开。所以——数据预处理跟距离选择是一起考虑的。
2.3 主流聚类算法家族
别看算法名字一堆,其实可以分成几个家族。我画了张图,你一看就明白:
2.4 各算法核心特点速览
这张表我平时做选型时经常翻,给你参考:
| 算法 | 是否需要预设 K | 对噪声容忍度 | 能否发现任意形状 | 空间先验支持 | 典型复杂度 |
|---|---|---|---|---|---|
| K-means | 是 | 低 | 否(球形假设) | 通过特征拼接 | O(N·K·d) |
| 层次聚类 | 否(切割后需确定) | 中等 | 依赖链接准则 | 通过距离矩阵 | O(N²·logN) |
| DBSCAN | 否 | 高(可标噪声) | 是 | 通过 eps 和空间距离 | O(N·logN) 平均 |
| 谱聚类 | 是 | 中等 | 是(图割视角) | 自然支持(基于邻接矩阵) | O(N³) 构建谱 |
| Louvain/Leiden | 否 | 中等(会聚小簇) | 是(图结构) | 必须提供邻接图 | O(N·logN) |
我曾经踩过的坑:在 Visium 数据上直接用 K-means 聚类基因表达量,结果总是把组织边缘的 spot 分配到和中心一样。后来才发现,K-means 对离群点和空间尺度极其敏感。如果你非要用 K-means,至少把空间坐标做 Z-score 标准化再加进去。
2.5 如何评价聚类质量?
无监督学习最头疼的就是:没有 ground truth 标签,怎么知道聚类好不好?这里有几个常用指标:
- 轮廓系数 (Silhouette Score):综合簇内紧密度和簇间分离度。范围 [-1,1],越高越好。但注意它对非凸簇表现不佳。
- Davies-Bouldin Index (DBI):簇内平均距离 / 簇间中心距离。越小越好。容易计算,但只考虑中心点。
- Calinski-Harabasz 指数:簇间方差 / 簇内方差。越大越好。适合球形簇。
- 空间连续性:Visium 特有。看同一簇的 spots 在组织切片上是否形成连贯区域。可以用 邻域一致性分数(比如 spot 的邻居中属于同一簇的比例)。
我个人经验:空间转录组的聚类千万别只靠纯数学指标。你跑出一个高轮廓系数的结果,结果一看图,组织边界完全不对——那就白搭。一定要配合组织学图像或 H&E 染色来验证。
2.6 一个关键洞察:距离选择 × 空间权重
你想想看,Visium 数据天生有双重结构:基因表达相似性 + 空间邻近性。怎么融合?
通常做法有两种:
- 特征拼接:把归一化的表达向量和归一化的空间坐标拼成一个长向量,然后跑常规聚类。简单,但空间权重很难调。
- 图约束:先根据空间坐标构建 Delaunay 三角网或 kNN 邻接图,然后在图上跑谱聚类或 Louvain。这样能天然保证空间连续性。
我的建议:优先尝试第二种。用 Scanpy 或 Squidpy 建图,然后跑 Leiden 聚类。因为它不仅考虑了表达相似性,还通过“图上的模块度”天然整合了空间邻接关系。这也是当前 Visium 社区的主流做法。
2.7 避坑指南(来自现场)
最后,我把这些年踩过的雷总结成几条:
- 不要迷信默认参数:scikit-learn 的 K-means 默认用欧氏距离,但在空间转录组里,维度灾难会让距离失效。先做 PCA 降维再聚类。
- 注意簇数不稳定:很多算法需要预设 K,但生物学上你预先不知道有多少种细胞类型。建议跑多个 K,结合生物学先验或肘部法则选。
- 空间噪声跟技术性 dropout 混合:Visium 每个 spot 可能是多种细胞的混合,小簇太多容易变成“假团”。最好用 HDBSCAN 这种能自动丢弃噪声的算法先过滤。
- 可重复性问题:K-means 和谱聚类受随机初始化影响大,务必设 random_state 并多次重启选最优。
好了,基础概念就讲到这里。下一块内容我们会直接上手跑一个真实 Visium 数据集,从数据加载到聚类结果可视化,一步一步看怎么选参数。