[파이썬 기초] 차원 축소 (PCA, UMAP)

2026. 6. 21. 16:00·BI&Programming-Tools/Python

차원 축소는 특성(변수)이 많은 고차원 데이터를 더 적은 차원으로 줄이는 기법입니다.

 

시각화, 노이즈 제거, 연산량 감소에 사용되며,

생물정보학에서는 유전자 발현 데이터나 단일 세포 데이터처럼 특성 수가 매우 많은 데이터를 다룰 때 자주 사용됩니다.

pip install scikit-learn umap-learn

 



왜 차원 축소가 필요한지

RNA-seq 발현량 데이터는 보통 수천~수만 개의 유전자(특성)를 가집니다.

이런 고차원 데이터를 그대로 시각화하거나 분석하기는 어렵습니다.

  • 사람은 2~3차원까지만 직관적으로 시각화할 수 있음
  • 특성 수가 많을수록 차원의 저주(curse of dimensionality)로 거리 기반 분석이 왜곡됨
  • 많은 특성이 서로 상관되어 있어 정보가 중복됨

차원 축소는 데이터의 핵심 구조와 패턴을 최대한 보존하면서 차원 수를 줄여 이런 문제를 완화합니다.



PCA (Principal Component Analysis)

PCA는 데이터의 분산을 가장 잘 설명하는 방향(주성분)을 찾아 데이터를 투영하는 선형 차원 축소 기법입니다.

가장 기본적이고 널리 쓰이는 차원 축소 방법입니다.


기본 개념

  • 첫 번째 주성분(PC1)은 데이터의 분산이 가장 큰 방향
  • 두 번째 주성분(PC2)은 PC1과 직교하면서 남은 분산을 가장 잘 설명하는 방향
  • 이런 식으로 서로 직교하는 주성분을 순서대로 찾아냄


기본 사용법

import numpy as np
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler

# 예시 데이터: 20개 샘플, 100개 유전자 발현량
np.random.seed(0)
X = np.random.rand(20, 100)

# PCA 전 표준화
X_scaled = StandardScaler().fit_transform(X)

# PCA 적용
pca = PCA(n_components=5)
X_pca = pca.fit_transform(X_scaled)

print(X_pca.shape)    # (20, 5)

 

PCA 적용 전에는 특성 스케일링(표준화)을 거치는 것이 일반적입니다.

스케일이 큰 특성이 분산을 과대하게 좌우해서 주성분이 왜곡될 수 있기 때문입니다.


설명된 분산 (Explained Variance)

각 주성분이 전체 분산 중 얼마를 설명하는지 확인합니다.

explained_var = pca.explained_variance_ratio_
print(explained_var)
# 예: [0.08 0.07 0.06 0.06 0.05]

cumulative_var = np.cumsum(explained_var)
print(cumulative_var)    # 누적 설명 분산


Scree plot

주성분 개수에 따라 설명되는 분산이 어떻게 줄어드는지 확인하는 그래프입니다.

몇 개의 주성분까지 사용할지 정할 때 참고합니다.

import matplotlib.pyplot as plt

pca_full = PCA().fit(X_scaled)
explained_var_full = pca_full.explained_variance_ratio_

fig, ax = plt.subplots(figsize=(7, 4))
ax.plot(range(1, len(explained_var_full) + 1), explained_var_full, "bo-")
ax.set_xlabel("Principal Component")
ax.set_ylabel("Explained Variance Ratio")
ax.set_title("Scree Plot")
plt.tight_layout()
plt.show()


PCA 결과 시각화

fig, ax = plt.subplots(figsize=(6, 5))
ax.scatter(X_pca[:, 0], X_pca[:, 1])
ax.set_xlabel(f"PC1 ({explained_var[0]:.1%})")
ax.set_ylabel(f"PC2 ({explained_var[1]:.1%})")
ax.set_title("PCA")
plt.tight_layout()
plt.show()

라벨(그룹)이 있는 데이터라면 색상으로 구분해서 그릴 수 있습니다.

import pandas as pd
import seaborn as sns

labels = ["group1"] * 10 + ["group2"] * 10    # 예시 라벨

df_pca = pd.DataFrame({
    "PC1": X_pca[:, 0],
    "PC2": X_pca[:, 1],
    "group": labels
})

fig, ax = plt.subplots(figsize=(6, 5))
sns.scatterplot(data=df_pca, x="PC1", y="PC2", hue="group", ax=ax)
plt.tight_layout()
plt.show()


주성분에 기여하는 특성 확인

각 주성분에 가장 크게 기여하는 특성(유전자)을 확인할 수 있습니다.

loadings = pca.components_    # (주성분 수, 원본 특성 수)

pc1_loadings = pd.Series(loadings[0])
top_features = pc1_loadings.abs().sort_values(ascending=False).head(5)
print(top_features)


새 데이터 투영

학습된 PCA로 새로운 샘플을 같은 공간에 투영할 수 있습니다.

X_new_scaled = scaler.transform(X_new)    # 기존 scaler로 transform
X_new_pca = pca.transform(X_new_scaled)   # fit 없이 transform만


UMAP (Uniform Manifold Approximation and Projection)

UMAP은 비선형 차원 축소 기법입니다.

고차원 데이터의 구조를 저차원에서도 비교적 잘 보존하며, 단일 세포 RNA-seq 분석에서 세포를 시각화하는 데 널리 사용됩니다.

pip install umap-learn


기본 사용법

import umap

reducer = umap.UMAP(n_neighbors=15, min_dist=0.1, random_state=42)
X_umap = reducer.fit_transform(X_scaled)

print(X_umap.shape)    # (20, 2)


주요 하이퍼파라미터

파라미터 의미 영향
n_neighbors 국소 구조 파악에 사용할 이웃 수 작으면 국소 구조 강조, 크면 전역 구조 강조
min_dist 저차원에서 점들 간 최소 거리 작으면 조밀한 클러스터, 크면 흩어진 분포
n_components 출력 차원 수 시각화 목적이면 보통 2
fig, ax = plt.subplots(figsize=(6, 5))
ax.scatter(X_umap[:, 0], X_umap[:, 1])
ax.set_xlabel("UMAP1")
ax.set_ylabel("UMAP2")
ax.set_title("UMAP")
plt.tight_layout()
plt.show()

 

n_neighbors 값을 바꿔보면서 결과가 어떻게 달라지는지 비교해볼 수 있습니다.

fig, axes = plt.subplots(1, 3, figsize=(13, 4))

for ax, n in zip(axes, [5, 15, 50]):
    reducer = umap.UMAP(n_neighbors=n, random_state=42)
    emb = reducer.fit_transform(X_scaled)
    ax.scatter(emb[:, 0], emb[:, 1])
    ax.set_title(f"n_neighbors={n}")

plt.tight_layout()
plt.show()


PCA 후 UMAP 적용

원본 고차원 데이터에 바로 UMAP을 적용하기보다, PCA로 먼저 차원을 줄인 뒤 UMAP을 적용하는 방식이 실무에서 자주 쓰입니다. 노이즈를 줄이고 계산 속도도 빨라집니다.

# 1단계: PCA로 50차원까지 축소
pca = PCA(n_components=50, random_state=42)
X_pca50 = pca.fit_transform(X_scaled)

# 2단계: UMAP으로 2차원까지 축소
reducer = umap.UMAP(n_neighbors=15, min_dist=0.1, random_state=42)
X_umap = reducer.fit_transform(X_pca50)


Scanpy 같은 단일 세포 분석 도구도 내부적으로 이 흐름(PCA → UMAP)을 기본 단계로 사용합니다.



PCA vs UMAP vs t-SNE

항목 PCA t-SNE UMAP
방식 선형 비선형 비선형
전역 구조 보존 우수 약함 비교적 우수
국소 구조 보존 약함 우수 우수
계산 속도 빠름 느림 비교적 빠름
새 데이터 투영 가능 (transform) 어려움 가능 (transform)
주요 용도 전처리, 분산 분석 시각화 전용 시각화, 전처리 모두

 

t-SNE와 UMAP은 군집 내부의 상대적 위치는 의미가 있지만, 군집 간 거리나 군집 크기를 정량적으로 해석하는 것은 권장되지 않습니다. 두 기법 모두 시각화를 위해 거리 정보를 어느 정도 왜곡하기 때문입니다.


t-SNE 기본 사용법

from sklearn.manifold import TSNE

tsne = TSNE(n_components=2, perplexity=30, random_state=42)
X_tsne = tsne.fit_transform(X_scaled)

 

perplexity는 UMAP의 n_neighbors와 비슷한 역할을 하며, 보통 5~50 사이의 값을 사용합니다.

t-SNE는 transform() 메서드가 없어서, 새로운 데이터에 기존 임베딩을 그대로 적용하기는 어렵습니다.



생물정보학에서의 활용


RNA-seq 샘플 QC

PCA는 RNA-seq 분석에서 샘플 간 그룹화가 잘 되어 있는지, 이상치 샘플이 있는지 확인하는 용도로 자주 사용됩니다.

정규화된 발현량 행렬(샘플 × 유전자)에 PCA를 적용해서 시각화하고, 조건별로 샘플이 잘 분리되는지 확인합니다.


단일 세포 RNA-seq 클러스터링

scRNA-seq 데이터는 보통 수천~수만 개의 세포와 수천 개의 유전자로 구성됩니다.

PCA로 차원을 줄인 뒤 UMAP으로 시각화하고, 클러스터링으로 세포 유형을 구분하는 것이 일반적인 흐름입니다.

# Scanpy의 일반적인 흐름 (개념 참고용)
import scanpy as sc

# adata: AnnData 객체 (세포 × 유전자)
sc.pp.normalize_total(adata)
sc.pp.log1p(adata)
sc.tl.pca(adata, n_comps=50)
sc.pp.neighbors(adata, n_neighbors=15)
sc.tl.umap(adata)
sc.tl.leiden(adata)    # 클러스터링

sc.pl.umap(adata, color="leiden")

정리표

항목 내용
PCA 전 권장 작업 특성 스케일링 (StandardScaler)
PCA 핵심 지표 explained_variance_ratio_
PCA → UMAP 흐름 PCA로 먼저 축소한 뒤 UMAP 적용이 일반적
UMAP 핵심 파라미터 n_neighbors, min_dist
거리 해석 주의 UMAP/t-SNE의 군집 간 거리는 정량적 해석 지양

'BI&Programming-Tools > Python' 카테고리의 다른 글

TDD 개념 — Test Driven Development  (0) 2026.06.24
Python 커맨드 라인 인자 받기 — sys.argv & argparse  (0) 2026.06.23
[파이썬 기초] 데이터 전처리 심화 (결측값, 이상값, 정규화, 인코딩)  (0) 2026.06.20
[파이썬 기초] scikit-learn 기초 (분류, 회귀, 클러스터링)  (0) 2026.06.20
[파이썬 기초] argparse로 커맨드라인 도구 만들기  (0) 2026.06.20
'BI&Programming-Tools/Python' 카테고리의 다른 글
  • TDD 개념 — Test Driven Development
  • Python 커맨드 라인 인자 받기 — sys.argv & argparse
  • [파이썬 기초] 데이터 전처리 심화 (결측값, 이상값, 정규화, 인코딩)
  • [파이썬 기초] scikit-learn 기초 (분류, 회귀, 클러스터링)
데이터로 읽는 생명
데이터로 읽는 생명
is-note 님의 블로그 입니다.
  • 데이터로 읽는 생명
    In Silico Note
    데이터로 읽는 생명
  • 전체
    오늘
    어제
    • 분류 전체보기 (190)
      • Bio-Knowledge (16)
        • 분자생물학 & 유전학 기초 (0)
        • 전사체학 & 유전자 발현 (0)
        • 구조생물학 & 단백질 (0)
        • 싱글셀 & 다중오믹스 (0)
        • 임상유전학 & 질환 데이터 (0)
      • Programming (4)
        • API (4)
      • BI&Programming-Tools (111)
        • File Formats (14)
        • Linux & Bash Script (38)
        • Python (35)
        • R (11)
        • 통계 (8)
        • Pipeline Manager (0)
        • Etc (5)
      • Bio Data Analysis (28)
        • 서열분석개론 (6)
        • WGS(Whole Genome Seq) (11)
        • WES(Whole Exome Seq) (2)
        • RNA-Seq (4)
        • Metagenome (2)
        • Non-human Resequencing (1)
        • 임상유전체 분석 (1)
        • Multi-Omics (1)
      • Bio-Trends & Tech (1)
      • 코딩테스트 연습 (30)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    FASTQ
    생물정보학
    R기초
    R
    파일포맷
    분자생물학
    데이터분석
    파이썬
    리눅스
    bioinformatics
    wgs
    코딩테스트
    파이썬연습
    ngs
    유전체분석
    GATK
    파이썬기초
    통계
    fasta
    리눅스기초
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.6
데이터로 읽는 생명
[파이썬 기초] 차원 축소 (PCA, UMAP)
상단으로

티스토리툴바