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 분석을 함께 사용하면 가장 신뢰할 수 있음! 🚀