Bio-info/analysis
single cell 분석 - cluster 개수 정하기
z_e
2025. 3. 5. 20:48
참고 : scanpy - preprocessing & clustering
https://scanpy.readthedocs.io/en/stable/tutorials/basics/clustering.html#manual-cell-type-annotation
written by chatGPT 4o
🔍 클러스터 개수를 정하는 가장 근거 있는 방법
클러스터 개수(Optimal Number of Clusters)를 정하는 방법은 데이터의 구조와 분석 목적에 따라 다르지만, 일반적으로 다음과 같은 방법들이 신뢰할 수 있습니다.
📌 1. 데이터 기반의 정량적 방법
1️⃣ Elbow Method (엘보우 방법)
- 클러스터 개수 k를 변화시키면서 클러스터 내 응집도(SSE, Within-cluster sum of squares, WCSS)를 계산한 후, 그래프에서 곡선이 급격히 꺾이는 지점(Elbow point)을 최적의 클러스터 개수로 선택하는 방법.
- K-means, Leiden, Louvain 등 대부분의 클러스터링 알고리즘에서 사용 가능.
✅ 적용 방법 (Scanpy):
import scanpy as sc
import numpy as np
import matplotlib.pyplot as plt
from sklearn.metrics import silhouette_score
wcss = []
resolutions = np.linspace(0.01, 2.0, 20) # 여러 가지 해상도(resolution) 값 테스트
for res in resolutions:
sc.tl.leiden(adata_pdo_hvg, resolution=res, key_added=f'leiden_{res:.2f}')
clusters = adata_pdo_hvg.obs[f'leiden_{res:.2f}'].astype(int)
wcss.append(np.mean([np.var(adata_pdo_hvg[clusters == i].X) for i in range(clusters.nunique())]))
# 그래프 출력
plt.plot(resolutions, wcss, marker='o')
plt.xlabel("Resolution (Leiden parameter)")
plt.ylabel("Within-Cluster Variance (WCSS)")
plt.title("Elbow Method for Optimal Clustering")
plt.show()
📌 Elbow point에서 클러스터 개수가 최적인 경우가 많음.
2️⃣ Silhouette Score (실루엣 스코어)
- 클러스터링이 얼마나 잘 분리되었는지를 측정하는 지표.
- 값이 1에 가까울수록 클러스터가 명확하게 구분됨.
- Leiden, Louvain, K-means 등에서 사용 가능.
✅ 적용 방법 (Scanpy):
from sklearn.metrics import silhouette_score
scores = []
resolutions = np.linspace(0.01, 2.0, 20)
for res in resolutions:
sc.tl.leiden(adata_pdo_hvg, resolution=res, key_added=f'leiden_{res:.2f}')
clusters = adata_pdo_hvg.obs[f'leiden_{res:.2f}'].astype(int)
score = silhouette_score(adata_pdo_hvg.obsm['X_umap'], clusters)
scores.append(score)
plt.plot(resolutions, scores, marker='o')
plt.xlabel("Resolution")
plt.ylabel("Silhouette Score")
plt.title("Optimal Clustering based on Silhouette Score")
plt.show()
📌 Silhouette Score가 가장 높은 클러스터 개수를 선택하면 좋음.
3️⃣ Gap Statistics (갭 통계량)
- 클러스터링의 성능을 랜덤 데이터와 비교하여 최적 개수를 결정.
- Leiden, Louvain 등에서 사용 가능.
✅ 적용 방법: gap_statistic 패키지를 이용한 방법
from gap_statistic import OptimalK
optimal_k = OptimalK(parallel_backend='joblib')
n_clusters = optimal_k(adata_pdo_hvg.obsm['X_umap'])
print(f"Optimal number of clusters: {n_clusters}")
📌 랜덤 데이터와의 차이가 가장 큰 클러스터 개수를 선택하면 최적의 클러스터 수를 결정할 수 있음.
📌 2. 생물학적/도메인 지식 기반 방법
1️⃣ Marker Genes 확인
- 각 클러스터에서 특이적으로 발현되는 유전자를 확인하여 클러스터 개수를 결정.
- 만약 일부 클러스터가 생물학적으로 의미 없는 분리라면, 클러스터 개수를 줄이는 것이 좋음.
✅ 적용 방법 (Scanpy):
sc.tl.rank_genes_groups(adata_pdo_hvg, 'leiden', method='wilcoxon')
sc.pl.rank_genes_groups(adata_pdo_hvg, n_genes=20, sharey=False)
📌 각 클러스터가 생물학적으로 의미 있는지를 확인한 후, 클러스터 개수를 조정 가능.
2️⃣ Cluster Stability Test (클러스터 안정성 테스트)
- 동일한 데이터에 대해 여러 번 클러스터링을 수행한 후, 클러스터 구성의 안정성을 평가.
- 특정 클러스터가 계속 유지된다면 신뢰할 수 있는 클러스터 개수임.
✅ 적용 방법 (Bootstrap 기반)
import random
stability_scores = []
resolutions = np.linspace(0.01, 2.0, 10)
for res in resolutions:
scores = []
for _ in range(10): # 10번 반복
sampled_cells = random.sample(list(adata_pdo_hvg.obs.index), int(len(adata_pdo_hvg) * 0.8))
sc.tl.leiden(adata_pdo_hvg[sampled_cells], resolution=res, key_added=f'leiden_{res:.2f}')
clusters = adata_pdo_hvg[sampled_cells].obs[f'leiden_{res:.2f}']
scores.append(len(set(clusters)))
stability_scores.append(np.mean(scores))
plt.plot(resolutions, stability_scores, marker='o')
plt.xlabel("Resolution")
plt.ylabel("Cluster Stability Score")
plt.title("Cluster Stability Test")
plt.show()
📌 클러스터 개수가 일정하게 유지되는 해상도를 선택하는 것이 좋음.
📌 3. 실용적인 결론
방법 설명 추천 시나리오
Elbow Method | WCSS가 급격히 감소하는 지점을 선택 | 클러스터 개수를 빠르게 결정하고 싶을 때 |
Silhouette Score | 가장 높은 실루엣 점수를 가지는 클러스터 개수 선택 | 클러스터가 얼마나 잘 분리되었는지 확인하고 싶을 때 |
Gap Statistics | 랜덤 데이터와 비교하여 최적 개수 선택 | 데이터 구조가 불분명할 때 |
Marker Genes 분석 | 특정 클러스터가 생물학적으로 유의미한지 확인 | 유전자 발현을 기반으로 의미 있는 클러스터를 찾고 싶을 때 |
Cluster Stability Test | 반복된 샘플링에서 안정적인 클러스터 개수를 찾음 | 클러스터링이 얼마나 안정적인지 평가하고 싶을 때 |
💡 보통 Elbow Method + Silhouette Score + Marker Genes 분석을 함께 사용하면 가장 신뢰할 수 있음! 🚀