一、為什麼要再現性?
「三年後請打開三年前的 R 腳本,重新產出同樣的圖。」這是學界與業界對研究最基本的要求。再現性失敗的常見場景:
- 套件大版本更新後,函式參數預設值改了,結果不同。
- 原始資料路徑寫死,換電腦就找不到。
- 沒記錄「跑分析時用的 R 版本、套件版本」。
- 結果是手動 Excel 加工──沒人知道哪些步驟。
本章四大武器:Project + renv + Quarto/RMarkdown + Git。
"Three years from now, open this script and reproduce the same figure." Common failure modes:
- Package major update silently changes a default argument; result drifts.
- Hard-coded paths break on another machine.
- R version + package versions weren't recorded.
- Results were hand-tweaked in Excel — no audit trail.
Four weapons: Project + renv + Quarto/RMarkdown + Git.
二、RStudio Projects + here
已在 Setup 章詳述。複習要點:
Covered in Setup. Recap:
my_project/ ├── my_project.Rproj # 專案錨點,路徑自動設定 ├── README.md # 專案目的、樣本來源、執行步驟 ├── renv.lock # 套件版本鎖檔 (見下節) ├── data/ │ ├── raw/ # 唯讀 │ └── processed/ ├── R/ # 腳本依執行順序編號 01_, 02_, ... ├── results/ │ ├── tables/ │ └── figures/ └── reports/ # Quarto / RMarkdown 報告
# 在腳本中永遠用 here() 寫路徑,跨電腦零修改 / Always use here() — portable
# library(here)
# here() # 偵測到專案根
# read.csv(here('data', 'raw', 'x.csv'))
# saveRDS(dds, here('results', 'dds.rds'))
# ggsave(here('results','figures','volcano.pdf'), p, width = 6, height = 5)
cat('Project + here = portable, no setwd() needed.\n')
三、renv:套件版本時光機
renv 把專案使用的所有套件版本「鎖」到 renv.lock,並在專案內維護獨立的套件 library。換電腦或多年後重跑,只要 renv::restore() 就能裝回完全一樣的版本。
renv "locks" all package versions in renv.lock and maintains a project-local library. On a new machine or years later, renv::restore() brings back the exact versions.
# install.packages('renv')
# === 第一次設定 / First-time setup ===
# 在專案內 / In project root
# renv::init()
# 建立 renv/ 資料夾、renv.lock、修改 .Rprofile
# 專案內的 install.packages() 都裝到專案 library,不污染全局
# === 日常使用 / Daily ===
# install.packages('newPkg') # 裝到專案 library
# library(newPkg); ... # 用
# renv::snapshot() # 把當前版本寫進 renv.lock 並 commit
# === 把專案搬到新電腦 / Move project ===
# git clone ; cd
# 開 RStudio → 自動偵測 renv,提示是否還原
# renv::restore() # 一鍵裝回所有原始版本
# === 其他常用 ===
# renv::status() # 查看 lockfile 與實際 library 是否一致
# renv::update() # 更新套件並可選擇 snapshot
# renv::dependencies() # 掃描程式碼用了哪些套件
# === renv.lock 範例片段 ===
# {
# 'R': { 'Version': '4.4.1' },
# 'Packages': {
# 'DESeq2': {
# 'Package': 'DESeq2',
# 'Version': '1.46.0',
# 'Source': 'Bioconductor',
# 'Hash': '...'
# }
# }
# }
cat('renv demo — see renv vignette for full guide.\n')
四、RMarkdown 與 Quarto──「程式碼即報告」
| — | RMarkdown (.Rmd) | Quarto (.qmd) |
|---|---|---|
| 推出年份 | 2014 | 2022 |
| 支援語言 | R + 少量 Python/SQL via knitr | R + Python + Julia + Observable |
| 輸出 | HTML / PDF / Word / slides | 同上 + 書 + 網站 |
| 維護 | 仍維護,但 Quarto 是繼任 | 官方主推 |
新專案建議直接用 Quarto。Quarto 完全相容 .Rmd 語法,但更現代化、跨語言一致。
For new projects, go with Quarto. It's fully compatible with .Rmd syntax but more modern and language-agnostic.
---
title: "DEG analysis report"
author: "Charlene"
date: today
format:
html:
toc: true
toc-depth: 3
code-fold: true # 摺疊程式碼預設
theme: cosmo
fig-width: 7
fig-height: 5
execute:
warning: false
echo: true # 顯示程式碼
cache: true # chunk 結果快取
---
# Loading data
```{r setup}
library(DESeq2); library(here)
dds <- readRDS(here("results", "dds.rds"))
```
# Volcano plot
```{r volcano, fig.cap="Volcano plot of DEGs"}
EnhancedVolcano::EnhancedVolcano(results(dds),
lab = rownames(dds), x = 'log2FoldChange', y = 'padj')
```
# Session info
```{r session}
sessionInfo()
```
# Render in R / 在 R 中渲染
# rmarkdown::render('report.Rmd') # RMarkdown
# quarto::quarto_render('report.qmd') # Quarto (or hit Render in RStudio)
# 命令列 / Command line
# quarto render report.qmd
# quarto render report.qmd --to pdf
# 多輸出 / Multiple outputs
# quarto render report.qmd --to html --to pdf
# 投影片 / Slides
# format: revealjs
# format: pptx
cat('Render demo — install rmarkdown / quarto package or quarto CLI.\n')
sessionInfo()──記錄 R 版本、OS、所有套件版本。論文 supplementary materials 必備!
Key: always end every report with sessionInfo() — records R version, OS, and every package version. A must for paper supplementary materials.
五、Git──分析的時光機
RStudio 對 Git 有原生整合(Tools → Project Options → Git/SVN)。每次完成一階段就 commit,能精確記錄「我什麼時候改了什麼、為什麼」。
建議的 .gitignore:
RStudio integrates Git natively (Tools → Project Options → Git/SVN). Commit after every milestone — exact log of "what changed, when, why".
Recommended .gitignore:
# R 與 RStudio .Rhistory .RData .Rproj.user/ .Ruserdata # renv:要不要 commit library 看團隊──通常 commit lockfile 即可 renv/library/ renv/local/ renv/staging/ # (renv.lock 必須 commit!) # 大型資料 (用 Git LFS 或 cloud) data/raw/*.bam data/raw/*.fastq.gz *.h5 *.rds results/figures/*.pdf # 系統垃圾 .DS_Store Thumbs.db # IDE *.swp .vscode/
# 命令列基本 / Basic CLI
# git init
# git add R/01_load_data.R
# git commit -m '01: load raw counts and sample metadata'
# git log --oneline
# 與遠端同步 / Remote
# git remote add origin git@github.com:user/repo.git
# git push -u origin main
# git pull
# RStudio 內建:Git 分頁可勾選檔案、按 Commit、寫 message、Push
# 整合的好處:diff 直接視覺化、衝突解決有 GUI
# 建議 commit 頻率:
# - 每完成一個分析步驟 commit 一次
# - 每天結束 push 到遠端,避免硬碟壞掉一切歸零
cat('Git demo — install Git first; RStudio auto-detects.\n')
六、其他可加分的習慣
📝 README.md
每個專案都該有:1) 目的;2) 資料來源 / 引用;3) 必要套件;4) 執行步驟 (R/01_... → R/02_... 順序);5) 聯絡人。
Every project needs: 1) purpose; 2) data source / citation; 3) required packages; 4) execution order (R/01_... → R/02_...); 5) contact.
🎲 set.seed()
所有用到隨機(PCA tie、bootstrap、k-means 初始化)的程式碼,最前面寫 set.seed(42)。否則重跑結果會微小漂移。
Any code that uses randomness (PCA ties, bootstrap, k-means init) needs set.seed(42) at the top. Otherwise re-runs drift slightly.
🔄 Makefile / targets
targets 套件能自動偵測哪些步驟要重跑(類似 Snakemake / Make)。大型 pipeline 強烈建議。
The targets package auto-detects which steps need rerunning (like Snakemake / Make). Strongly recommended for big pipelines.
🐳 Docker
把 R 版本、OS、所有套件、外部工具(samtools, bowtie2)打包成 image。最終極可重現方案。rocker/tidyverse、bioconductor/bioconductor_docker 是好起點。
Bundle R + OS + packages + external tools (samtools, bowtie2) into an image. The ultimate reproducibility tool. Start with rocker/tidyverse or bioconductor/bioconductor_docker.
🧪 testthat 單元測試
對自訂函式寫測試,確保未來改動不會悄悄壞掉舊行為。usethis::use_testthat() 一鍵設定。
Write tests for custom functions so future edits don't silently break old behavior. usethis::use_testthat() sets it up.
📊 logging
大型 pipeline 用 logger 或 futile.logger 把進度、警告、錯誤寫到檔案。除錯救命。
For long pipelines, use logger or futile.logger to write progress/warnings/errors to file. Debug-saver.
七、論文投稿前的再現性 checklist
✅ 投稿前自我檢查
📝 自我檢測
1. 三年後重跑舊分析卻得不到相同結果,最可能原因?
1. Same analysis 3 years later produces different result — most likely cause?
2. RMarkdown / Quarto 的核心好處是?
2. The core benefit of RMarkdown / Quarto?
3. 想要團隊成員 git clone 後能重現你的分析環境,最關鍵的兩個檔案?
3. Two most critical files for a teammate to git-clone & reproduce your env?