第3章 通配符(Wildcard):从原理到多样本实战
讲真,我第一次接触 Snakemake 时,就被通配符的灵活度震惊了。想象一下,你手上有 50 个肿瘤样本的测序数据,每条数据都要走相同的质控流程。如果每个样本都要手写一条规则……
别急,通配符就是来解决这个问题的。
3.1 通配符到底是什么?
简单说,通配符就是一个占位符。
它就像你去饭店点餐时说的「来一份套餐」——「套餐」就是通配符,具体是红烧牛肉还是鱼香肉丝,由后厨(也就是 Snakemake 引擎)根据实际情况决定。
在 Snakemake 里,通配符用 {wildcard_name} 这种格式来表示。比如:
rule fastqc:
input:
"data/{sample}.fastq"
output:
"results/fastqc/{sample}_fastqc.html"
shell:
"fastqc {input} -o results/fastqc/"
这里的 {sample} 就是通配符。
当 Snakemake 看到这条规则时,它会说:「嗯,只要输入文件路径里有 {sample} 这个占位,输出文件路径里也有对应的 {sample},我就能帮你自动填上实际值。」
sample、lane、chr。千万别用 x、y、z 这种单字母——维护起来简直想哭。
3.2 通配符的语法规则
通配符的写法说穿了就两条:
- 花括号包裹:
{名字} - 名字必须由字母、数字、下划线组成:不能有空格和特殊字符
展示个对比表格吧:
| 写法 | 是否合法 | 说明 |
|---|---|---|
{sample} | ✅ | 标准写法 |
{sample_name} | ✅ | 下划线支持 |
{sample123} | ✅ | 数字也支持 |
{Sample} | ✅ | 大小写敏感,但建议统一小写 |
{sample name} | ❌ | 空格,不行 |
{sample-name} | ❌ | 短横线,也不行 |
还有个容易踩坑的点——通配符可以多个同时出现:
rule multi_wildcards:
input:
"data/{project}/{sample}.fastq"
output:
"results/{project}/{sample}_qc.html"
这里有两个通配符:{project} 和 {sample}。Snakemake 会同时匹配它们。
{sample},output 里却写成了 {samples}(多了个 s)。结果 Snakemake 一直报错说找不到匹配。这种拼写错误特别隐蔽,一定要仔细检查!
3.3 通配符的匹配原理
说白了,Snakemake 的通配符匹配就像变量替换游戏。
它的工作流程是这样的:
- 先看你的输出文件需要什么——比如
results/fastqc/SRR123_fastqc.html - 然后找到能生成这个输出的规则——找到
rule fastqc - 比对输出模板
results/fastqc/{sample}_fastqc.html - 从实际输出文件名里提取通配符的值——
SRR123赋值给{sample} - 再用这个值去生成输入文件名——
data/SRR123.fastq
你想想看,这不就是解代数方程吗?Snakemake 是个聪明的解方程器。
3.4 多样本处理的实战套路
说完了理论,来点实战操作。
假设你有三个样本,文件名分别是:
data/
sample_A.fastq
sample_B.fastq
sample_C.fastq
要给每个样本跑 FastQC 和 Trimmomatic,传统做法你得写三个类似规则,累死。
用通配符,一行搞定:
rule fastqc:
input:
"data/{sample}.fastq"
output:
"results/fastqc/{sample}_fastqc.html"
shell:
"fastqc {input} -o results/fastqc/"
那怎么触发三个样本呢?
最直接的方法是指定最终目标文件:
rule all:
input:
"results/fastqc/sample_A_fastqc.html",
"results/fastqc/sample_B_fastqc.html",
"results/fastqc/sample_C_fastqc.html"
Snakemake 会看到 rule all 需要三个文件,然后自动用 rule fastqc 分别生成它们。
但手动写样本名还是麻烦。我习惯用配置文件 + 列表推导:
SAMPLES = ["sample_A", "sample_B", "sample_C"]
rule all:
input:
expand("results/fastqc/{sample}_fastqc.html", sample=SAMPLES)
expand() 函数会自动把列表里的每个元素替换进通配符,生成完整的文件列表。
expand() + 样本列表代替手动枚举。样本列表可以从 CSV 文件、YAML 配置文件或 Python 代码里读,非常灵活。
3.5 通配符的约束与冲突
通配符虽然灵活,但也有底线。
一个规则里不能出现相同名字但值不同的通配符。
比如这样会报错:
rule bad_wildcard:
input:
"data/{sample}_R1.fastq",
"data/{sample}_R2.fastq"
——等等,这个其实是合法的!因为两个 {sample} 名字相同,Snakemake 会要求它们的值必须一致。
真正有问题的是这种:
rule bad_wildcard:
input:
"data/{sample}_R1.fastq",
"data/{other}_R2.fastq"
output:
"results/{sample}_merged.fastq"
这里 {sample} 和 {other} 是两个不同的通配符,Snakemake 不知道如何同时满足它们。
怎么解决?要么统一名字,要么用通配符约束:
ruleconstrained:
input:
"data/{sample}_R1.fastq",
"data/{sample}_R2.fastq"
output:
"results/{sample}_merged.fastq"
wildcard_constraints:
sample = "sample_[A-C]"
这里我用正则表达式限制了 {sample} 的取值只能是 sample_A、sample_B 或 sample_C。
3.6 我的通配符避坑指南
做了几十个 Snakemake 工作流后,我总结了几个高频踩坑点:
- 避免通配符在输出里缺失:output 里的通配符必须和 input 里的一一对应,多一个少一个都不行
- 小心通配符命名冲突:不同规则的全局通配符不能同名但含义不同,Snakemake 会迷惑
- 用 wildcard_constraints 防匹配过度:我遇到过
{sample}匹配了不该匹配的路径层级,加个约束就好了 - debug 时用 --print-compilation:这个参数能打印出 Snakemake 如何解析通配符,特别有用
3.7 通配符核心要素一张图
下面这张图总结了通配符的完整工作流程:
你看这张图,流程一目了然:样本列表 → 通配符定义 → 规则解析 → 模式匹配 → 提取通配符值 → 生成具体的输入文件。环环相扣,自动流转。
其实说白了,通配符就是 Snakemake 工作流的「核心引擎」。掌握了它,你就能从写重复代码的苦海里跳出来。
最后分享一个我的习惯:每次新建工作流,我都先定义好样本列表和通配符命名规范。基础打好了,后面写规则就跟搭积木一样顺畅。