第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 会同时匹配它们。

我踩过的坑:我曾经把两个通配符的名字写反了——input 里用 {sample},output 里却写成了 {samples}(多了个 s)。结果 Snakemake 一直报错说找不到匹配。这种拼写错误特别隐蔽,一定要仔细检查!

3.3 通配符的匹配原理

说白了,Snakemake 的通配符匹配就像变量替换游戏。

它的工作流程是这样的:

  1. 先看你的输出文件需要什么——比如 results/fastqc/SRR123_fastqc.html
  2. 然后找到能生成这个输出的规则——找到 rule fastqc
  3. 比对输出模板 results/fastqc/{sample}_fastqc.html
  4. 从实际输出文件名里提取通配符的值——SRR123 赋值给 {sample}
  5. 再用这个值去生成输入文件名——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 通配符匹配完整流程 样本列表 sample_A, B, C 通配符定义 {sample} 规则规则 fastqc / trimmomatic 模式匹配 output ↔ 文件名 通配符值 sample_A → sample_B → 输入文件 data/sample_A.fastq 核心思想:一次编写通配符规则,Snakemake 自动为每个样本执行相同流程

你看这张图,流程一目了然:样本列表 → 通配符定义 → 规则解析 → 模式匹配 → 提取通配符值 → 生成具体的输入文件。环环相扣,自动流转。

其实说白了,通配符就是 Snakemake 工作流的「核心引擎」。掌握了它,你就能从写重复代码的苦海里跳出来。

最后分享一个我的习惯:每次新建工作流,我都先定义好样本列表和通配符命名规范。基础打好了,后面写规则就跟搭积木一样顺畅。