세포마다 깊이가 다르다는 문제
S27에서 깨끗한 세포만 남겼습니다. 이제 이 세포들을 서로 비교하려는데, 첫 번째 벽에 부딪힙니다. 세포마다 총 UMI 수가 다릅니다. 어떤 세포는 5,000 카운트, 어떤 세포는 500 카운트. 이 차이의 대부분은 생물학이 아니라 기술적 잡음 — 세포가 방울 안에서 얼마나 잘 잡혔는지, 얼마나 효율적으로 역전사됐는지 — 입니다.
깊이가 5,000인 세포에서 어떤 유전자가 50번 나오고, 깊이 500인 세포에서 5번 나왔다면, 상대적으로는 같은 비율입니다. 이 기술적 깊이 차이를 걷어내야 세포들을 공정하게 비교할 수 있습니다. 그다음, 3만 개나 되는 유전자 중 정작 세포를 구분하는 데 도움 되는 유전자만 골라내야 합니다. 이 두 전처리 — 정규화와 HVG 선택 — 가 이 편의 주제입니다.
표준 정규화 — log1p
가장 널리 쓰이는 방식은 세 단계입니다.
- 깊이 보정: 각 세포의 카운트를 그 세포 총합으로 나눠 목표 크기(예: 1e4 = CP10K)로 스케일. "세포당 10,000 카운트로 통일".
- 로그 변환: 적용.
+1(pseudocount)은 0을 로그 취할 때의 발산을 막습니다. - (선택) 유전자별 스케일링(z-score).
로그를 씌우는 이유가 중요합니다. 발현 카운트는 소수의 초고발현 유전자가 분포를 지배합니다. 로그는 이 긴 꼬리를 눌러 큰 값과 작은 값의 스케일 차이를 줄이고(분산 안정화), 곱셈적 차이를 덧셈적 차이로 바꿔 이후 선형 분석(PCA)이 잘 작동하게 합니다.
이 방식은 빠르고 견고해 기본값입니다. 다만 완벽하진 않습니다. 저발현 유전자에서 log1p는 깊이 효과를 완전히 못 지웁니다.
더 정교한 길 — Pearson residual (sctransform)
sctransform은 다른 발상입니다. 각 유전자를 음이항 회귀로 모델링하고(설명변수 = 세포 깊이), 관측값이 모델 기댓값에서 얼마나 벗어났는지를 Pearson residual로 표현합니다.
는 깊이를 반영한 기대 카운트, 분모는 음이항 표준편차(S19의 와 같은 골격!). 이렇게 하면 깊이 효과가 모델로 명시적으로 제거되고, 고발현·저발현 유전자의 분산이 고르게 안정화됩니다. 계산은 더 무겁지만, 미묘한 세포 유형을 구분해야 할 때 강합니다. 최신 Scanpy는 sc.experimental.pp.normalize_pearson_residuals로 제공합니다.
HVG — 3만에서 2천으로
정규화 후에도 유전자가 3만 개입니다. 대부분은 모든 세포에서 비슷하게 발현되는 살림유전자(housekeeping)로, 세포를 구분하지 못합니다. 세포 유형을 가르는 것은 소수의 변이가 큰 유전자입니다.
핵심 함정: "그냥 분산 큰 유전자"를 고르면 안 됩니다. 발현이 높은 유전자는 자연히 분산도 큽니다(평균-분산 관계). 그래서 평균 대비 기대보다 분산이 큰 유전자를 골라야 합니다. Scanpy의 highly_variable_genes는 유전자를 평균 발현 구간(bin)으로 나눈 뒤, 각 구간 안에서 표준화된 분산(dispersion)이 높은 유전자를 뽑습니다. 보통 상위 2,000개를 남깁니다.
이건 CS의 특징 선택 그 자체입니다. 수만 특징에서 판별력 있는 소수만 남겨 차원의 저주를 완화하고, 이후 PCA·클러스터링의 신호 대 잡음비를 끌어올립니다.
Scanpy 실습 (PBMC 3k)
S27의 QC를 통과한 adata에서 이어갑니다.
import scanpy as sc# adata = S27 QC 완료 상태
# 원시 카운트 백업 (되돌릴 수 있게)adata.layers["counts"] = adata.X.copy()
# 표준 정규화sc.pp.normalize_total(adata, target_sum=1e4) # 깊이 보정sc.pp.log1p(adata) # 로그 변환adata.raw = adata # 로그 정규화값 보존
# HVG 선택 (상위 2000개)sc.pp.highly_variable_genes(adata, n_top_genes=2000, flavor="seurat")sc.pl.highly_variable_genes(adata) # 선택 결과 시각화
# HVG만 남기기adata = adata[:, adata.var.highly_variable].copy()print(adata) # n_vars가 2000으로 줄었는지 확인sc.pl.highly_variable_genes가 그리는 산점도에서 검게 표시된 점이 선택된 HVG입니다. 평균-분산 추세선 위로 솟은 유전자들이 뽑히는 것을 눈으로 확인합시다. 여기 뜨는 유전자들(예: PBMC라면 마커 계열)이 이후 세포 유형 구분의 주역이 됩니다.
adata.raw와 layers["counts"]를 남기는 습관이 중요합니다. 마커 유전자 시각화(S30)나 차등 발현은 정규화 이전/이후 값을 오가며 쓰기 때문입니다.
CS 매핑
- 특징 선택(feature selection): HVG 선택은 수만 특징에서 판별력 있는 소수를 남기는 것으로, 머신러닝 전처리의 핵심 단계 그대로입니다.
- 분산 안정화(variance stabilization): log1p·Pearson residual은 평균에 의존하는 분산을 고르게 만드는 변환입니다. 통계의 분산 안정화 변환(Anscombe 등)과 같은 목적입니다.
- 정규화 = 스케일 불변성: 세포 깊이 보정은 관측을 공통 스케일로 맞추는 것으로, 데이터 표준화와 직결됩니다.
- 차원의 저주 완화: 특징을 줄여 이후 거리 계산(PCA·kNN)의 신뢰도를 높입니다.
자주 만나는 결함
- HVG 선택을 정규화 전에 수행 — 깊이 효과가 분산을 오염시켜 엉뚱한 유전자가 뽑힙니다. 정규화 → HVG 순서를 지킵시다.
- 원시 카운트를 백업 안 함 — 이후 차등 발현·마커 검정에 raw가 필요합니다.
layers["counts"]습관이 필수입니다. - z-score 스케일링 후 그 값으로 차등 발현 — 스케일링된 값은 시각화·PCA용입니다. DGE는 로그 정규화값(
adata.raw)으로 합니다. - HVG 개수를 무작정 크게 — 2,000개 근처가 관례입니다. 너무 많으면 잡음이, 너무 적으면 희귀 세포 유형 신호가 사라집니다.
더 깊게 파고 싶다면
본문은 BPD가 직접 재구성한 서술입니다. 심화는 아래 정통 자료를 활용합시다.
- 참고 무료 웹북: Single-cell best practices (sc-best-practices.org) — 정규화·특징 선택 챕터가 방법 비교의 결정판입니다.
- 원 논문(sctransform): Hafemeister & Satija (2019), Normalization and variance stabilization of single-cell RNA-seq data using regularized negative binomial regression, Genome Biology 20:296.
- 원 논문(scran pooling): Lun, Bach, Marioni (2016), Pooling across cells to normalize single-cell RNA sequencing data with many zero counts, Genome Biology 17:75.
- EMBL-EBI Training — Single-Cell Transcriptomics: Normalisation & Feature Selection (자막 완비). 실무 파라미터 감각을 줍니다.
이제 2,000개 유전자 × 깨끗한 세포 행렬이 준비됐습니다. 하지만 2,000차원은 여전히 사람이 볼 수 없습니다. 다음 편 S29에서 이 고차원을 PCA·t-SNE·UMAP으로 눌러, 세포들이 만드는 구조를 2차원 지도로 펼쳐봅니다.