01 从数据到项目结构
01 从数据到项目结构
这一章的目标是让读者在真正动手做 bulk RNA-seq 之前,先把"拿到什么输入、打算得到什么输出"想清楚。很多项目一开始跑得很顺,到了复盘阶段才发现样本表有问题、目录结构乱、中间文件丢了。bulk RNA-seq 的数据量比单细胞小得多,但分析链条更长,更值得一开始就把项目结构定下来。
一个典型项目的输入和输出
以最常见的两组比较为例:对照 vs 处理,每组 3 个生物学重复,每个样本一次双端测序。你手上会有的东西和最后要交付的东西大致是:
| 阶段 | 输入 | 输出 |
|---|---|---|
| 测序中心交付 | 12 个 FASTQ 文件(6 样本 × 2 端)+ 基本 QC 报告 | — |
| 分析起点 | 上面这些 FASTQ | 本地目录结构 + 样本表 |
| 比对 / 准比对 | FASTQ + 参考基因组 + GTF | 每个样本的 BAM 或 Salmon quant 目录 |
| 定量 | BAM / Salmon | 一份 gene × sample 的 counts 矩阵 |
| 差异分析 | counts + 样本表 | 差异基因表(gene, log2FC, padj) |
| 功能解释 | 差异基因表 | GO / KEGG / GSEA 结果 |
| 交付 | 所有上面的结果 | 图 + 表 + Methods 段 |
"样本表"是整条链路的中心。它至少要有:样本 ID、条件、批次、测序时间、FASTQ 路径。后面每一步都要回到这张表。
项目目录
长期维护的目录结构没有标准答案,但至少应该满足"6 个月后回来还知道每个文件是什么"。一个可用的结构:
rnaseq_project/
├── README.md # 项目背景、样本来源、关键决策
├── metadata/
│ └── samples.tsv # 样本表,唯一可信来源
├── data/
│ ├── raw/ # 原始 FASTQ(不动)
│ ├── qc/ # FastQC + MultiQC 报告
│ ├── align/ # BAM 或 Salmon quant
│ └── counts/ # 合并后的 counts 矩阵
├── analysis/
│ ├── 01-qc.R
│ ├── 02-de.R # DESeq2 / edgeR
│ ├── 03-enrichment.R
│ └── 04-figures.R
├── results/
│ ├── tables/
│ └── figures/
└── envs/
└── env.yaml # conda 环境,锁定版本
几个经验:
data/raw/只读,永远不直接修改,新文件写到其他目录- 分析脚本按步骤命名(
01-,02-...),从上到下能复现整个流程 results/只放最终交付物,中间产物放data/envs/env.yaml锁版本,过两个月再跑也能装回原环境
样本表怎么写
样本表是一个纯文本 TSV,越简单越好。一个最小可用版本:
sample_id condition batch r1 r2
CTRL_1 untreated 1 data/raw/CTRL_1_R1.fastq.gz data/raw/CTRL_1_R2.fastq.gz
CTRL_2 untreated 1 data/raw/CTRL_2_R1.fastq.gz data/raw/CTRL_2_R2.fastq.gz
CTRL_3 untreated 2 data/raw/CTRL_3_R1.fastq.gz data/raw/CTRL_3_R2.fastq.gz
TRT_1 treated 1 data/raw/TRT_1_R1.fastq.gz data/raw/TRT_1_R2.fastq.gz
TRT_2 treated 1 data/raw/TRT_2_R1.fastq.gz data/raw/TRT_2_R2.fastq.gz
TRT_3 treated 2 data/raw/TRT_3_R1.fastq.gz data/raw/TRT_3_R2.fastq.gz
batch 这一列不一定用得到,但有分析经验之后你会知道:一开始就记录下来是最便宜的,事后补很贵。测序批次、试剂批次、操作人、文库类型,只要是可能引入系统偏差的变量都值得留一列。DESeq2 的 design 会直接用到这些列:
design = ~ batch + condition
参考基因组和注释
人和小鼠的参考基因组有几个主流来源:
| 来源 | 特点 | 推荐用途 |
|---|---|---|
| GENCODE | 人 / 小鼠注释的金标准 | 绝大多数项目,和 Ensembl ID 对得上 |
| Ensembl | 覆盖物种最多 | 非模式物种 |
| UCSC / NCBI | 字段丰富 | 做浏览器可视化、变异分析 |
基因组和注释必须来自同一个来源、同一个版本,不然基因 ID 和坐标就对不上。推荐直接下载 GENCODE 的 primary assembly + 主注释 GTF,搭配 Salmon 或 STAR 都能跑:
# 人 v44 作为例子
wget -c https://ftp.ebi.ac.uk/pub/databases/gencode/Gencode_human/release_44/GRCh38.primary_assembly.genome.fa.gz
wget -c https://ftp.ebi.ac.uk/pub/databases/gencode/Gencode_human/release_44/gencode.v44.primary_assembly.annotation.gtf.gz
这两个文件解压后占 6~7 GB,落到 data/reference/ 里,长期项目里这份东西是共享的,不跟单个项目耦合。
比对 vs 准比对
两条主流路线:
| 路线 | 典型工具 | 速度 | 磁盘 | 适用场景 |
|---|---|---|---|---|
| 全比对到基因组 | STAR / HISAT2 → featureCounts | 慢 | 大(BAM) | 需要 BAM 做可视化 / SNV / 新转录本 |
| 准比对到转录本 | Salmon / kallisto → tximport | 快 | 小 | 只做差异分析和富集 |
新项目如果只做差异表达和富集,用 Salmon 几乎是最佳选择 —— 笔记本上几分钟就能定量一个样本。下面这段是 Salmon 的典型调用:
# 建立索引(只做一次)
salmon index \
-t gencode.v44.transcripts.fa.gz \
-i salmon_index \
--gencode
# 定量每个样本
for s in CTRL_1 CTRL_2 CTRL_3 TRT_1 TRT_2 TRT_3; do
salmon quant \
-i salmon_index \
-l A \
-1 data/raw/${s}_R1.fastq.gz \
-2 data/raw/${s}_R2.fastq.gz \
-p 8 --validateMappings \
-o data/align/${s}
done
定量完成后每个样本会得到一个目录,里面的 quant.sf 就是转录本级别的定量结果。后续在 R 里用 tximport 聚合到基因级就进入差异分析:
library(tximport)
library(readr)
samples <- read.table("metadata/samples.tsv", header = TRUE, sep = "\t")
files <- file.path("data/align", samples$sample_id, "quant.sf")
names(files) <- samples$sample_id
# tx2gene 映射(从 GTF 生成一次就好)
tx2gene <- readr::read_tsv("metadata/tx2gene.tsv")
txi <- tximport(files, type = "salmon", tx2gene = tx2gene)
# txi$counts 就是 gene x sample 的 counts 矩阵
快速跳过:直接用 airway / pasilla
上面这套是面向真实项目的;如果只是想学习差异分析,完全可以跳过 FASTQ → BAM → counts 这一段,直接用 Bioconductor 已经打包好的 counts 数据:
airway— 人肺上皮 8 样本(4 对照 + 4 糖皮质激素处理),DESeq2 官方教程数据pasilla— 果蝇 7 样本,DESeq2 最小例子
后面 02、03、04 的真实示例都用 airway,装上包就能跑,不用碰比对流程。
常见坑
坑 1:样本表用 Excel 编辑导致格式错乱
Excel 会自动把 MARCH1(基因名)改成日期、把数字前补 0 删掉、改空格成 unicode。永远用纯文本编辑器或 R / Python 维护样本表,要看的时候再 read.delim 进来。
坑 2:参考基因组版本和注释不匹配
下载 GRCh38 fasta 配 GENCODE v44 GTF 没问题;但配 v25 GTF 会出现"基因名对得上、坐标对不上"的诡异错误。fa 和 gtf 必须来自同一来源同一版本,不要混用。
坑 3:把 raw counts 当成已归一化的 TPM
featureCounts 出来的是 raw counts(整数),可以直接喂 DESeq2 / edgeR;但 Salmon / kallisto 的默认输出是 TPM 或估计 counts,给 DESeq2 时要用 tximport(..., type = "salmon") 转成 counts,不是直接读 quant.sf。
坑 4:Salmon 索引用错链路
--gencode 标志只对 GENCODE 来源的转录组用,Ensembl 来源的转录组不要加 --gencode。否则 transcript ID 在去版本号时会出错。
坑 5:把样本目录命名成数字开头
Linux 下排序看上去 1.fastq 2.fastq 没问题,但 R 读进来当数据框列名时会自动加 X 前缀(变成 X1、X2)。样本 ID 一律用字母开头(S1、CTRL_1),下游脚本省心。
下一步
接着深入:
- 02 DESeq2 差异表达分析 — counts 准备好之后的第一步,airway 公开数据可以直接跑
横向延伸:
- 03 GO / KEGG / GSEA 富集 — 如果你已经有差异基因表,可以直接跳到富集分析
- 06 批次效应 — 多批次样本要先想清楚 batch 怎么处理,再设计 design 公式