유전자 발현에 좌표가 생기면, 물어야 할 질문이 달라집니다
S35에서 공간 전사체 데이터의 구조를 살펴봤습니다. 이제 이 데이터 위에서 공간적으로 의미 있는 질문을 던져야 합니다. scRNA-seq에서는 "이 유전자가 이 클러스터에서 높게 발현되는가?"가 중심 질문이었다면, 공간 데이터에서는 다음과 같은 질문이 추가됩니다.
- 이 세포 유형은 어떤 세포 유형 옆에 자주 나타나는가? (이웃 농축 분석)
- 이 유전자의 발현이 공간적으로 군집해 있는가, 아니면 무작위로 흩어져 있는가? (공간 자기상관)
- 리간드를 발현하는 세포와 수용체를 발현하는 세포가 실제로 가까이 있는가? (공간 L-R 공존 검정)
이 편에서는 이 세 가지 질문에 답하는 통계적 방법을 소개하고, Squidpy로 직접 분석을 실행합니다.
공간 그래프 구축: 이웃을 정의하기
모든 공간 분석의 첫 단계는 이웃 관계를 정의하는 것입니다. 세포(또는 스팟)가 2D 좌표를 가지므로, 이를 그래프로 변환해야 합니다. 주요 방법은 세 가지입니다.
kNN 그래프: 각 세포의 가장 가까운 k개의 세포를 이웃으로 정의합니다. scRNA-seq에서 유전자 공간의 kNN을 만든 것(S29)과 동일한 논리이지만, 여기서는 물리적 XY 좌표를 사용합니다.
Delaunay 삼각분할: 점 집합을 삼각형으로 분할하되, 어떤 삼각형의 외접원 안에도 다른 점이 없도록 합니다. 결과 삼각형의 변으로 연결된 세포 쌍이 이웃입니다. 밀도가 불균일한 조직에서도 자연스러운 이웃 관계를 생성합니다.
고정 반경: 세포로부터 반경 r μm 이내의 모든 세포를 이웃으로 정의합니다. 생물학적으로 "접촉 가능 거리"를 직접 설정할 수 있어 직관적이지만, 밀집 영역에서 이웃 수가 폭발할 수 있습니다.
import squidpy as sq
# Visium 데이터 로드adata = sq.datasets.visium_hne_adata()sc.pp.normalize_total(adata)sc.pp.log1p(adata)
# 공간 이웃 그래프 구축 (Delaunay)sq.gr.spatial_neighbors(adata, coord_type="generic", delaunay=True)이웃 농축 분석: 누가 누구 옆에 있는가
세포 유형 A 옆에 세포 유형 B가 기대보다 자주 나타나면, 이 두 유형은 공간적으로 연관되어 있다고 말합니다. Squidpy의 nhood_enrichment는 이를 순열 검정으로 검증합니다.
- 각 세포의 이웃 중 세포 유형별 빈도를 집계합니다.
- 세포 유형 라벨을 무작위로 섞어(순열) 귀무 분포를 만듭니다.
- 실제 빈도가 귀무 분포에서 유의하게 높으면(농축) 또는 낮으면(회피), z-score로 보고합니다.
# 이웃 농축 분석sq.gr.nhood_enrichment(adata, cluster_key="cluster")sq.pl.nhood_enrichment(adata, cluster_key="cluster")결과 히트맵에서 양의 z-score(빨간색)는 공간적 농축(두 세포 유형이 가까이 모여 있음), 음의 z-score(파란색)는 회피(서로 떨어져 있음)를 나타냅니다. 뇌 조직이라면 올리고덴드로사이트가 백질 영역에서 서로 농축되고, 뉴런과는 공간적으로 분리되는 패턴이 전형적입니다.
공간 자기상관: Moran's I
Moran's I는 "이 유전자의 발현이 공간적으로 군집해 있는가?"에 대한 고전적 검정입니다. 공간 통계학에서 지리적 데이터에 사용되던 것을 그대로 공간 전사체에 적용합니다.
수식은 다음과 같습니다.
여기서 는 세포 와 의 공간 가중치(이웃이면 1, 아니면 0), 는 유전자 발현값입니다.
- : 강한 양의 자기상관 — 발현이 높은 세포끼리, 낮은 세포끼리 모여 있음
- : 공간적 패턴 없음 — 무작위 분포
- : 음의 자기상관 — 높고 낮은 세포가 체크보드처럼 교차
# 전 유전자 Moran's I 계산sq.gr.spatial_autocorr(adata, mode="moran")
# 상위 공간 변이 유전자 확인moran_df = adata.uns["moranI"].sort_values("I", ascending=False)print(moran_df.head(10))
# 상위 유전자 시각화sc.pl.spatial(adata, color=moran_df.index[:4].tolist(), spot_size=1.5)Moran's I가 높은 유전자들은 **공간적으로 변이하는 유전자(spatially variable genes, SVGs)**입니다. scRNA-seq의 HVG(S28)가 세포 간 변이를 기준으로 유전자를 선택했다면, SVG는 공간적 변이를 기준으로 선택합니다. 두 목록은 부분적으로만 겹칩니다 — 공간적으로 흥미롭지만 세포 간 분산은 작은 유전자(예: 층 특이적 마커)가 있기 때문입니다.
공간 L-R 공존 검정: 신호가 물리적으로 가능한가
S34에서 CellChat/CellPhoneDB로 리간드-수용체 통신을 추론했지만, 공간 정보 없이는 "이 두 세포가 실제로 가까이 있는가?"를 확인할 수 없었습니다. Squidpy의 ligrec는 이 질문에 답합니다.
# 공간 기반 L-R 상호작용 검정sq.gr.ligrec( adata, n_perms=1000, cluster_key="cluster", copy=False,)sq.pl.ligrec(adata, cluster_key="cluster", source_groups="Astrocyte", target_groups=["Neuron", "Oligodendrocyte"])이것은 CellPhoneDB의 순열 검정과 유사하지만, 공간 이웃 그래프 위에서 검정합니다. 리간드를 발현하는 세포와 수용체를 발현하는 세포가 공간적 이웃일 때만 상호작용 후보로 등록하므로, scRNA-seq 기반 분석보다 위양성이 줄어듭니다.
Giotto: R 생태계의 대안
Squidpy가 Python/Scanpy 생태계라면, Giotto는 R 생태계에서 동일한 분석을 제공합니다. 특히 Giotto는 공간 도메인 검출, 공간 네트워크 분석, 다중 모달 통합에서 독자적인 기능을 가지고 있습니다.
library(Giotto)
# Giotto 객체 생성
g <- createGiottoObject(
expression = expr_matrix,
spatial_locs = spatial_coords
)
# 공간 네트워크 + Delaunay
g <- createSpatialNetwork(g, method = "Delaunay")
# 공간 변이 유전자 (binSpect)
g <- binSpect(g, bin_method = "kmeans")
spatialDE_results <- g@spatial_enrichment$binSpectGiotto의 binSpect는 Moran's I 외에 k-means 기반 이진 분할로 공간 변이 유전자를 검출하며, 대규모 데이터에서 Moran's I보다 빠릅니다.
CS 매핑
- 공간 해시 그리드: 이웃 검색을 가속하는 공간 인덱싱은 게임 엔진의 공간 해시 그리드와 동일한 원리입니다. R-tree, k-d tree도 같은 맥락입니다.
- Moran's I / 공간 자기상관: 시계열의 자기상관(ACF)을 2D 공간으로 확장한 것입니다. 지리 정보 시스템(GIS)의 핵심 통계이기도 합니다.
- Delaunay 삼각분할: 계산 기하학(Computational Geometry)의 기초 알고리즘으로, DryBench의 기하 알고리즘과 직결됩니다. Voronoi 다이어그램의 듀얼(dual)입니다.
자주 만나는 결함
- 이웃 정의에 따라 결과가 달라짐 — kNN vs Delaunay vs 고정 반경 중 어떤 것을 선택하느냐에 따라 이웃 농축과 Moran's I 값이 바뀝니다. 민감도 분석(여러 방법으로 반복)이 필요합니다.
- 스팟 기반 데이터에 단일세포 해석 적용 — Visium 스팟에서의 Moran's I는 다세포 혼합물의 공간 패턴이지, 개별 세포의 공간 패턴이 아닙니다.
- 다중 검정 보정 누락 — 2만 개 유전자에 대해 Moran's I를 계산하면 수백 개가 우연히 유의합니다. FDR 보정(q-value)이 필수입니다.
- 조직 가장자리 효과 — 조직 경계의 스팟/세포는 이웃이 한쪽으로만 있으므로 통계가 왜곡됩니다. 가장자리 스팟을 제외하거나 보정하는 것이 좋습니다.
더 깊게 파고 싶다면
본문은 BPD가 직접 재구성한 서술입니다. 심화는 아래 정통 자료를 활용합시다.
- Squidpy 공식 문서·튜토리얼: Palla et al. (2022), Squidpy: a scalable framework for spatial omics analysis, Nature Methods 19:171. 공간 그래프·이웃 분석·이미지 분석의 통합 프레임워크입니다.
- Giotto 공식 문서: Dries et al. (2021), Giotto provides a toolbox for integrative analysis and visualization of spatial expression data, Genome Biology 22:78.
- SpatialDE(공간 변이 유전자 검출 원조): Svensson et al. (2018), SpatialDE: identification of spatially variable genes, Nature Methods 15:343. 가우시안 프로세스 기반 접근입니다.
- 참고 무료 웹북: Single-cell best practices (sc-best-practices.org) — Spatial Analysis 챕터.
이웃 관계와 공간 패턴을 읽었으니, 한 가지 근본 문제가 남았습니다 — Visium 스팟의 다세포 혼합물을 어떻게 풀어낼 것인가. 다음 편 S37에서 Tangram과 cell2location으로 스팟을 단일세포로 디컨볼루션하는 방법을 다룹니다.