BioPlayground

🧬
목록으로

공간 통계 분석: Squidpy·Giotto로 세포의 이웃 관계와 공간 패턴을 읽기

조직 위의 유전자 발현에서 공간 패턴을 찾습니다. 이웃 농축 분석, 공간 자기상관(Moran's I), 그리고 리간드-수용체의 공간적 공존을 Squidpy로 직접 실습합시다.

심화
|
20
|
검증 완료 (2026-07-24)
spatial statisticsSquidpyGiottospatial autocorrelation
진행률0/73 (0%)

유전자 발현에 좌표가 생기면, 물어야 할 질문이 달라집니다

S35에서 공간 전사체 데이터의 구조를 살펴봤습니다. 이제 이 데이터 위에서 공간적으로 의미 있는 질문을 던져야 합니다. scRNA-seq에서는 "이 유전자가 이 클러스터에서 높게 발현되는가?"가 중심 질문이었다면, 공간 데이터에서는 다음과 같은 질문이 추가됩니다.

  • 이 세포 유형은 어떤 세포 유형 옆에 자주 나타나는가? (이웃 농축 분석)
  • 이 유전자의 발현이 공간적으로 군집해 있는가, 아니면 무작위로 흩어져 있는가? (공간 자기상관)
  • 리간드를 발현하는 세포와 수용체를 발현하는 세포가 실제로 가까이 있는가? (공간 L-R 공존 검정)

이 편에서는 이 세 가지 질문에 답하는 통계적 방법을 소개하고, Squidpy로 직접 분석을 실행합니다.

공간 그래프 구축: 이웃을 정의하기

모든 공간 분석의 첫 단계는 이웃 관계를 정의하는 것입니다. 세포(또는 스팟)가 2D 좌표를 가지므로, 이를 그래프로 변환해야 합니다. 주요 방법은 세 가지입니다.

kNN 그래프: 각 세포의 가장 가까운 k개의 세포를 이웃으로 정의합니다. scRNA-seq에서 유전자 공간의 kNN을 만든 것(S29)과 동일한 논리이지만, 여기서는 물리적 XY 좌표를 사용합니다.

Delaunay 삼각분할: 점 집합을 삼각형으로 분할하되, 어떤 삼각형의 외접원 안에도 다른 점이 없도록 합니다. 결과 삼각형의 변으로 연결된 세포 쌍이 이웃입니다. 밀도가 불균일한 조직에서도 자연스러운 이웃 관계를 생성합니다.

고정 반경: 세포로부터 반경 r μm 이내의 모든 세포를 이웃으로 정의합니다. 생물학적으로 "접촉 가능 거리"를 직접 설정할 수 있어 직관적이지만, 밀집 영역에서 이웃 수가 폭발할 수 있습니다.

python
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는 이를 순열 검정으로 검증합니다.

  1. 각 세포의 이웃 중 세포 유형별 빈도를 집계합니다.
  2. 세포 유형 라벨을 무작위로 섞어(순열) 귀무 분포를 만듭니다.
  3. 실제 빈도가 귀무 분포에서 유의하게 높으면(농축) 또는 낮으면(회피), z-score로 보고합니다.
python
# 이웃 농축 분석
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는 "이 유전자의 발현이 공간적으로 군집해 있는가?"에 대한 고전적 검정입니다. 공간 통계학에서 지리적 데이터에 사용되던 것을 그대로 공간 전사체에 적용합니다.

수식은 다음과 같습니다.

I=Nijwijijwij(xixˉ)(xjxˉ)i(xixˉ)2I = \frac{N}{\sum_{i}\sum_{j} w_{ij}} \cdot \frac{\sum_{i}\sum_{j} w_{ij}(x_i - \bar{x})(x_j - \bar{x})}{\sum_{i}(x_i - \bar{x})^2}

여기서 wijw_{ij}는 세포 iijj의 공간 가중치(이웃이면 1, 아니면 0), xix_i는 유전자 발현값입니다.

  • I1I \approx 1: 강한 양의 자기상관 — 발현이 높은 세포끼리, 낮은 세포끼리 모여 있음
  • I0I \approx 0: 공간적 패턴 없음 — 무작위 분포
  • I1I \approx -1: 음의 자기상관 — 높고 낮은 세포가 체크보드처럼 교차
python
# 전 유전자 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는 이 질문에 답합니다.

python
# 공간 기반 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는 공간 도메인 검출, 공간 네트워크 분석, 다중 모달 통합에서 독자적인 기능을 가지고 있습니다.

r
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$binSpect

Giotto의 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으로 스팟을 단일세포로 디컨볼루션하는 방법을 다룹니다.