R과 Python, 오믹스 분석에는 언제 무엇을 쓸까요? 도구 고르기와 재현 가능한 코드 남기기
목차
📌 이 글에서 알 수 있는 것
- R/Bioconductor와 Python이 각각 강한 분석 단계
- BiocManager로 릴리스 맞춰 설치하기, 두 언어로 count 표 읽기와 그림 한 장
- reticulate·rpy2와 파일 교환(TSV·parquet·h5ad)으로 두 언어 오가기
- renv·sessionInfo·시드·노트북 재실행으로 재현 가능한 코드 남기기
🧭 언어 싸움 대신 도구 고르기
오믹스 분석에서 “R이냐 Python이냐"는 보통 필요한 도구가 어느 언어로 만들어졌느냐로 정해집니다. 3편(정렬·변이 호출)과 12편(차등발현)에서 만드는 VCF·count 같은 표를 통계 검정과 그림으로 바꾸는 단계에서 두 언어가 쓰입니다.
어느 쪽이 “많이 쓰이는지"를 보여 주는 2021년 이후 생물정보 분야 설문은 찾지 못했습니다. 생의학 논문에 딸린 Jupyter 노트북 27,271개를 분석한 2024년 연구에서는 Python이 82.8%, R이 3.3%였지만1, Jupyter만 센 숫자라 R Markdown·Quarto로 공유된 R 코드는 빠져 있습니다. 언어 점유율로 읽으면 안 됩니다.
📦 R과 Bioconductor: 릴리스를 맞춰 설치
Bioconductor는 오믹스용 R 패키지 저장소입니다. R은 1년에 한 번, Bioconductor는 1년에 두 번 릴리스하고, 패키지는 같은 릴리스끼리 쓸 때 가장 잘 맞습니다2. 2026-11-06 기준 release는 4월 29일에 나온 3.23(소프트웨어 패키지 2,418개, R 4.6)이고, 3.24는 11월 25일 예정입니다34. 차등발현(DE) 분석의 DESeq2·edgeR·limma(12편)와, 유전자×시료 행렬에 유전자·시료 정보를 함께 묶는 표준 객체 SummarizedExperiment가 여기에 있습니다5.
install.packages("BiocManager")
BiocManager::install("DESeq2")
BiocManager::version()
BiocManager::valid()
install.packages("BiocManager"): CRAN(R 기본 저장소)에서 설치 관리자를 받습니다.BiocManager::install("DESeq2"): 지금 R 버전에 맞는 Bioconductor 릴리스에서 DESeq2와 의존 패키지를 설치합니다.version(): 사용 중인 Bioconductor 릴리스(예: ‘3.23’)를 보여 줍니다.valid(): 오래됐거나 다른 릴리스에서 온 패키지를 찾아 줍니다2.- 자주 하는 실수: Bioconductor 패키지를
install.packages()로 설치하려다 “not available” 오류를 보는 것.
🐍 Python: pandas에서 AnnData까지
Python에서는 표는 pandas, 수치 계산은 numpy·scipy, 그림은 matplotlib·seaborn, 머신러닝은 scikit-learn이 기본입니다. Python의 단일세포 분석은 AnnData(세포×유전자 행렬과 주석을 묶는 객체, h5ad 파일)와 scanpy가 중심이고, R에서는 Seurat과 Bioconductor의 SingleCellExperiment가 같은 역할을 합니다678. 단일세포 분석 자체는 별도 글에서 다룰 예정입니다.
같은 count 표(첫 열이 유전자 ID인 TSV)를 두 언어로 읽어 봅니다.
counts <- read.delim("counts.tsv", row.names = 1, check.names = FALSE)
dim(counts); head(rownames(counts))
read.delim: 탭 구분, 첫 줄을 열 이름으로 읽는 것이 기본입니다9.row.names = 1: 첫 열(유전자 ID)을 행 이름으로 씁니다.check.names = FALSE: 기본값 TRUE는1-A같은 시료 이름을X1.A로 바꿉니다9.- 자주 하는 실수: 시료 이름이 바뀐 줄 모르고 메타데이터와 맞추다 어긋나는 것.
import pandas as pd
counts = pd.read_csv("counts.tsv", sep="\t", index_col=0)
print(counts.shape, counts.index[:5].tolist())
sep="\t": 기본 구분자는 쉼표라 TSV에는 꼭 지정합니다10.index_col=0: 첫 열을 행 라벨(index)로 씁니다.- 자주 하는 실수:
index_col없이 읽어 유전자 ID가 일반 열이 되고, 행 번호 0, 1, 2…가 대신 붙는 것. - 자주 하는 실수 2: R은 중복된 유전자 ID가 있으면 오류로 멈추지만 pandas는 그대로 읽습니다.
print(counts.index.is_unique)로 확인합니다(False면 중복).
그림 한 장(시료별 총 리드 수)도 그려 봅니다.
library(ggplot2)
lib <- data.frame(sample = colnames(counts), total = colSums(counts))
ggplot(lib, aes(sample, total)) + geom_col()
ggsave("library_size.png", width = 5, height = 3, dpi = 300)
aes(sample, total): x축에 시료, y축에 총 리드 수를 연결하고geom_col()로 막대를 그립니다.ggsave: 마지막 그림을 크기·해상도를 정해 저장합니다11.
import matplotlib.pyplot as plt
counts.sum(axis=0).plot.bar()
plt.tight_layout(); plt.savefig("library_size_py.png", dpi=300)
sum(axis=0): 열(시료)별 합계입니다.axis=1이면 유전자별 합계가 됩니다.- 자주 하는 실수:
axis를 헷갈려 시료가 아니라 유전자별 합계를 그리는 것. 막대 개수가 시료 수인지 확인합니다.
🔁 두 언어 오가기
- reticulate: R 안에서 Python 모듈을 불러 씁니다. rpy2: Python 안에서 R을 부르고 pandas 표를 R 데이터 프레임으로 바꿉니다1213.
- 파일 교환: 작은 표는 TSV, 큰 표는 parquet(열 단위 압축 형식), 단일세포는 h5ad. parquet은 pandas에서 pyarrow, R에서 arrow 패키지를 따로 설치해야 읽힙니다14. R에서는 anndataR가 h5ad를 직접 읽고 SingleCellExperiment로 바꿉니다8.
- 한 단계에는 한 언어: 정규화는 R, 그림은 Python처럼 단계별로 나누고 사이를 파일로 넘기면, 각 단계를 따로 재실행·검증하기 쉽습니다.
방향도 주의합니다. SummarizedExperiment는 행이 유전자·열이 시료이고5, AnnData는 행이 관측(세포·시료)·열이 변수(유전자)입니다6. 넘길 때 전치가 필요한지 확인하세요.
🖥️ IDE와 노트북
IDE(통합 개발 환경)는 코드 편집·실행·그림 보기를 한 창에 모은 프로그램입니다.
- RStudio·Positron: 둘 다 Posit이 만듭니다. Positron은 R·Python을 기본 지원하는 새 IDE이고, RStudio도 계속 유지·업데이트됩니다15.
- Jupyter·JupyterLab: 코드·결과·설명을 셀 단위로 담는 노트북입니다. 코드를 실제로 실행하는 프로그램을 커널이라 하며, R 커널(IRkernel)도 있습니다.
- Quarto·R Markdown: 코드와 글을 한 문서로 묶어 HTML·PDF로 렌더링합니다. Quarto는 R·Python·Julia를 지원하고, R Markdown도 계속 지원됩니다16.
노트북은 셀을 아무 순서로나 실행할 수 있어 위험합니다. GitHub의 노트북 1,159,568개를 분석한 2021년 연구에서, 실행 순서가 확인된 노트북의 36.36%에 순서가 뒤바뀐 셀이 있었습니다. 실행을 시도한 863,878개 중 오류 없이 끝난 것은 24.11%, 저장된 결과와 같게 나온 것은 4.03%였습니다. 대부분은 실행 오류에서 막혔으니, 낮은 재현율을 셀 순서 탓으로만 볼 수는 없습니다17.
jupyter nbconvert --to notebook --execute analysis.ipynb
quarto render analysis.qmd
--execute: 노트북을 위에서부터 새로 실행해 analysis.nbconvert.ipynb로 저장합니다. 오류가 나면 멈춥니다18.quarto render: 문서를 처음부터 실행해 결과물을 만듭니다.- 자주 하는 실수: 손으로 돌린 셀 결과를 그대로 공유하는 것. 공유 전 한 번은 처음부터 돌립니다.
🔒 재현 가능한 코드 남기기
renv::init()
renv::snapshot()
renv::restore()
sessionInfo()
init(): 프로젝트 전용 패키지 폴더(renv/library)와 버전 목록 파일 renv.lock을 만듭니다19.snapshot(): 프로젝트 코드가 불러 쓰는 패키지의 버전을 renv.lock에 기록합니다(기본값). 그 패키지들이 의존하는 패키지는 함께 기록되지만, 설치만 하고 코드에서 쓰지 않은 패키지는 빠집니다20.restore(): 다른 컴퓨터에서 renv.lock대로 다시 설치합니다.sessionInfo(): R·운영체제·불러온 패키지 버전을 출력합니다. 결과 파일 옆에 저장해 두세요21.- 자주 하는 실수: renv가 R 자체 버전과 시스템 라이브러리까지 맞춰 준다고 믿는 것. R 버전은 기록만 합니다19.
import numpy as np
rng = np.random.default_rng(42)
- 시드(난수 시작값)를 정하지 않으면 실행할 때마다 다른 난수가 나옵니다22. R은
set.seed(42), scikit-learn은random_state=42로 정합니다. - Python 환경은 conda
environment.yml로 기록합니다(6편).pip freeze > requirements.txt는 pip 패키지 목록만 남기고 Python 자체 버전이나 conda로 설치한 C 라이브러리는 남기지 않습니다23. - 유전자 목록을 엑셀로 열면 이름이 날짜로 바뀔 수 있습니다(5편). 코드에서 TSV로 읽고 씁니다.
- 코드는 git으로 관리하고, 원시 데이터 대신 renv.lock·.Rprofile·renv/activate.R·renv/settings.json, environment.yml, 스크립트를 커밋합니다19. Bioconductor를 쓰면
renv::init(bioconductor = "3.23")으로 릴리스도 함께 고정합니다.
🚧 자주 하는 실수 모음
| 실수 | 증상 | 확인·해결 |
|---|---|---|
| Bioconductor 릴리스 섞기 | 설치 오류, 이상한 동작 | BiocManager::valid()2 |
| 엉뚱한 라이브러리에 설치 | 설치했는데 불러오지 못함 | .libPaths() 확인, BiocVersion 중복 제거2 |
| 노트북 셀 순서 뒤섞임 | 다시 돌리면 결과가 다름 | 커널 재시작 후 전체 실행 |
| pandas 행 이름 잃음 | 유전자 ID가 0, 1, 2…로 | 읽을 때 index_col=0. 열 기준 merge는 index를 버리니 left_index=True 등을 지정24 |
| 0·1 기반 번호 혼동 | 한 칸씩 어긋남 | Python은 0부터, R은 1부터12. 좌표 파일은 8편 |
⚠️ 한계와 주의
- 숫자의 범위: 노트북 연구 두 편은 주로 Python 노트북을 자동 재실행한 결과입니다. 2021년 연구는 생의학에 한정되지 않은 GitHub의 일반 노트북이라 오믹스 분석에 그대로 옮길 수 없습니다171.
- 언어 자체의 우열을 보여 주는 근거는 없습니다. 같은 방법도 언어별 구현이 기본값과 결과를 다르게 낼 수 있으니, 논문에는 언어가 아니라 패키지 이름과 버전을 적습니다. DESeq2의 Python 구현(PyDESeq2)도 있는데, 개발진 스스로 R판과 비슷하지만 똑같지는 않은 결과를 낸다고 밝혔습니다. 섞어 쓰거나 비교할 때는 버전과 기본값을 함께 적으세요25.
🏛️ 지금 어디까지 왔나
- 버전(2026-11-06 확인): Bioconductor 3.23(R 4.6), DESeq2 1.52.0, edgeR 4.10.7, limma 3.68.5, BiocManager 1.30.27, renv 1.3.1, reticulate 1.47.0, ggplot2 4.0.3 / pandas 3.1.1, numpy 2.5.4, scikit-learn 1.9.1, anndata 0.13.4, scanpy 1.12.5, rpy2 3.6.9, JupyterLab 4.6.5, Quarto 1.11.9.
- 흐름: 두 언어를 잇는 도구가 늘고 있습니다. anndataR는 Bioconductor 3.22(2025년 10월)부터 포함됐고, Python 없이 R에서 h5ad를 읽고 쓰는 방법이 2026년 논문으로 나왔습니다826.
- 한국: 국내 기관의 R·Python 교육·공용 분석 환경 현황은 확인하지 못했습니다.
🧪 연구자 노트
- 언어는 쓰려는 도구가 어느 쪽에 있는지로 정하는 것이 현실적입니다. 벌크 RNA-seq 통계는 R(Bioconductor)이, 대규모 단일세포 처리나 머신러닝은 Python이 편한 경우가 많습니다.
- 두 언어를 오가며 파일을 넘길 때는 행·열 방향과 시료 이름이 바뀌지 않았는지 꼭 확인하세요.
🙋 그래서 나에게는? — 실습 전 체크리스트
- 쓰려는 핵심 패키지가 R·Python 중 어디에 있는지 먼저 확인했다
-
BiocManager::version()과valid()로 릴리스가 하나인지 확인했다 - 표를 읽은 뒤 행 이름(유전자 ID)과 시료 이름이 그대로인지 확인했다
- renv.lock 또는 environment.yml, sessionInfo를 코드와 함께 git에 남긴다
- 난수를 쓰는 단계에 시드를 정했고, 공유 전 노트북을 처음부터 다시 돌렸다
⚠️ 이 글은 의학적 조언이 아니며, 특정 제품을 권하지 않습니다. 실제 분석은 사용하는 패키지 설명서와 기관 SOP를 우선하세요.
❓ 자주 묻는 질문
Q. 처음 배운다면 하나만 고르면 되나요? A. 연구실이 이미 쓰는 언어와, 내 데이터에 필요한 핵심 패키지가 있는 언어를 먼저 배우세요. 다른 언어는 파일을 주고받을 정도만 익혀도 충분한 경우가 많습니다.
Q. conda로 R 패키지를 깔아도 되나요?
A. 됩니다. 다만 bioconda 패키지 버전은 Bioconductor 릴리스와 다를 수 있고, 한 프로젝트 안에서 conda와 BiocManager 설치를 섞으면 어느 라이브러리에 깔렸는지 헷갈립니다. 한 방식으로 정하고 .libPaths()를 확인하세요.
Q. 노트북과 스크립트 중 무엇이 좋나요? A. 탐색과 설명에는 노트북, 반복 실행되는 단계에는 스크립트나 9편의 워크플로가 편합니다. 노트북도 처음부터 다시 돌려 확인하면 됩니다.
📚 오믹스 기술 시리즈
- 1편 WGS 플랫폼 비교
- 2편 WGS 커버리지·깊이 설계
- 3편 WGS 데이터 처리와 QC
- 4편 WGS vs WES vs 패널
- 5편 리눅스 명령줄 기본기
- 6편 분석 환경 만들기
- 7편 FASTQ·BAM 실전
- 8편 VCF·BED 실전
- 9편 워크플로 관리
- 10편 R과 Python (이 글)
- 11편 RNA-seq 실험 설계
- 12편 RNA-seq 데이터 분석
- 13편 small RNA-seq 실험 설계
- 14편 small RNA-seq 데이터 분석
참고
Samuel S, Mietchen D. Computational reproducibility of Jupyter notebooks from biomedical publications. GigaScience 2024;13:giad113. doi:10.1093/gigascience/giad113 ↩︎ ↩︎
BiocManager vignette (BiocManager 1.30.27, 2026-11-06 확인) — cran.r-project.org ↩︎ ↩︎ ↩︎ ↩︎
Bioconductor. Bioconductor 3.23 release announcement (2026-04-29) — bioconductor.org ↩︎
Bioconductor. Bioconductor 3.24 release schedule (2026-11-06 확인) — bioconductor.org ↩︎
SummarizedExperiment vignette (1.42.0, 2026-11-06 확인) — bioconductor.org ↩︎ ↩︎
anndata. AnnData API (anndata 0.13.4, 2026-11-06 확인) — anndata.readthedocs.io ↩︎ ↩︎
Scanpy documentation (scanpy 1.12.5, 2026-11-06 확인) — scanpy.readthedocs.io ↩︎
Deconinck L 외. anndataR improves interoperability between R and Python in single-cell transcriptomics. Bioinformatics 2026;42:btag288. doi:10.1093/bioinformatics/btag288 ↩︎ ↩︎ ↩︎
R documentation. read.table {utils} (R 4.6, 2026-11-06 확인) — stat.ethz.ch ↩︎ ↩︎
pandas. pandas.read_csv (pandas 3.1.1, 2026-11-06 확인) — pandas.pydata.org ↩︎
ggplot2. ggsave (ggplot2 4.0.3, 2026-11-06 확인) — ggplot2.tidyverse.org ↩︎
reticulate. Arrays in R and Python (reticulate 1.47.0, 2026-11-06 확인) — rstudio.github.io/reticulate ↩︎ ↩︎
rpy2. Interoperability with pandas (rpy2 3.6.9, 2026-11-06 확인) — rpy2.github.io ↩︎
pandas. DataFrame.to_parquet (pandas 3.1.1, 2026-11-06 확인) — pandas.pydata.org ↩︎
Posit. Positron FAQ (Positron 2026.11.0, 2026-11-06 확인) — positron.posit.co ↩︎
Quarto. FAQ for R Markdown users (Quarto 1.11.9, 2026-11-06 확인) — quarto.org ↩︎
Pimentel JF 외. Understanding and improving the quality and reproducibility of Jupyter notebooks. Empir Softw Eng 2021;26:65. doi:10.1007/s10664-021-09961-9 ↩︎ ↩︎
nbconvert. Executing notebooks (2026-11-06 확인) — nbconvert.readthedocs.io ↩︎
renv. Introduction to renv (renv 1.3.1, 2026-11-06 확인) — rstudio.github.io/renv ↩︎ ↩︎ ↩︎
renv. snapshot (renv 1.3.1, type “implicit”, 2026-11-06 확인) — rstudio.github.io/renv ↩︎
R documentation. sessionInfo {utils} (R 4.6, 2026-11-06 확인) — stat.ethz.ch ↩︎
NumPy. Random sampling (numpy 2.5.4, 2026-11-06 확인) — numpy.org ↩︎
pip. pip freeze (2026-11-06 확인) — pip.pypa.io ↩︎
pandas. DataFrame.merge (pandas 3.1.1, 2026-11-06 확인) — pandas.pydata.org ↩︎
Muzellec B 외. PyDESeq2: a python package for bulk RNA-seq differential expression analysis. Bioinformatics 2023;39(9):btad547. doi:10.1093/bioinformatics/btad547 ↩︎
Bioconductor. anndataR (1.2.3, BioC 3.22부터 포함, 2026-11-06 확인) — bioconductor.org ↩︎