通配符与模式匹配:Wildcards 的使用、多文件扩展示例

你写了一个规则,能处理一个样本的质控。但面对 200 个样本,难道要复制 200 次规则?

我刚开始接触 Snakemake 时就是这么干的——累得半死,还容易漏改参数。后来才发现,通配符(wildcards)就是为这种批量场景设计的。

Wildcards 是什么?

说白了,通配符就是一个占位符。你在规则输出里写 {sample},Snakemake 就会自动根据实际要生成的文件名,把 {sample} 替换成真实的值。比如我有一个规则要生成 QC/ERR123456_trimmed.fastq,Snakemake 就知道 wildcards.sample = "ERR123456"。

我习惯把通配符看作是规则的“参数化入口”——它让一条规则能匹配成百上千个文件。

通配符的匹配逻辑

看一个最简例子:

rule trim:
    input:
        "raw/{sample}.fastq"
    output:
        "QC/{sample}_trimmed.fastq"
    shell:
        "fastp -i {input} -o {output}"

当我执行 snakemake QC/ERR123456_trimmed.fastq 时,Snakemake 会做三件事:

  • 把输出文件名里的 {sample} 绑定为 ERR123456
  • 把输入里的 {sample} 替换为 raw/ERR123456.fastq
  • 执行 shell 命令

注意,通配符名称必须一致。输出里写了 {sample},输入和 shell 里也得用 {sample},不能一会写 {id} 一会写 {sample}。

我的小习惯:通配符名用样本相关的词,比如 sample、run、patient,别用单个字母 a、b。代码跑一跑还能排查,但半年后回来看自己写的 Snakefile,{a} 和 {b} 基本就是天书。

多文件扩展:从规则到批量目标

光有规则还不够。要让 Snakemake 一次性处理所有样本,我们需要用 expand() 函数生成所有目标文件。

SAMPLES = ["ERR123456", "ERR123457", "ERR123458"]
rule all:
    input:
        expand("QC/{sample}_trimmed.fastq", sample=SAMPLES)

运行 snakemake -j 4,Snakemake 会推断出需要生成三个 QC 文件,然后自动调用 trim 规则三次,每次使用不同的 sample 值。

我遇到过最典型的坑:expand() 默认做笛卡尔积。假如你有两个列表:

MODULES = ["R1", "R2"]
DATASETS = ["A", "B"]
expand("QC/{dataset}_{module}.fastq", dataset=DATASETS, module=MODULES)

它会生成 QC/A_R1.fastq、QC/A_R2.fastq、QC/B_R1.fastq、QC/B_R2.fastq——四个文件。如果你只想配对(A配R1,B配R2),需要用 zip 模式:expand("QC/{dataset}_{module}.fastq", zip, dataset=DATASETS, module=MODULES)。

嗯,记着,默认笛卡尔积,想配对就加 zip。

通配符冲突与限定

同一个 Snakefile 里两条规则输出同名模式怎么办?看这个:

rule trim_se:
    output: "QC/{sample}_se_trimmed.fastq"
rule trim_pe:
    output: "QC/{sample}_pe_trimmed.fastq"

没问题,因为通配符模式不同——_se_ 和 _pe_ 区分开了。

但如果两条规则的输出模式完全相同(比如都输出 QC/{sample}.fastq),Snakemake 会报 AmbiguousRuleException。解决办法:要么改输出命名,要么用 wildcard_constraints 给通配符加限制。

通配符限定示例:

wildcard_constraints:
    sample = "[A-Z0-9]+"

rule trim:
    output: "QC/{sample}.fastq"

强制 sample 只能匹配大写字母和数字。我曾在项目中遇到样本名包含特殊字符(比如“ERR-001”),一旦连字符被当成目录分隔符解析,后果很惨。加约束是最稳的办法。

多文件扩展实战:质控参数调优

回到我们的课程主题——分类质控参数调优。假设你有三种数据类型:WGS、RNA、ATAC,每种需要不同的质控参数。我们可以用通配符 {datatype} 来组织:

DATATYPES = ["WGS", "RNA", "ATAC"]

rule fastp_with_config:
    input:
        "raw/{sample}_{datatype}.fastq"
    output:
        "QC/{sample}_{datatype}_trimmed.fastq"
    params:
        config = lambda wildcards: f"configs/{wildcards.datatype}.json"
    shell:
        "fastp -i {input} -o {output} -j {params.config}"

每个数据类型单独配一个 JSON 文件,完美实现分类调优。

数据类型通配符值配置示例
WGSWGS低 stringency,保留多平台 mismatch
RNARNA高 stringency,3' 端修剪短序列
ATACATAC中 stringency,去除线粒体 reads
我曾经踩过的坑:变量 DATATYPES 里写的是 "WGS"、"RNA",但 JSON 配置文件名是 "wgs.json"、"rna.json"——大小写不匹配。Snakemake 不会自动帮你转换大小写!{datatype} 替换成什么就是什么,你得自己维护一致性。后来我统一用大写枚举,配置文件也改成大写。

下面这张图把通配符——多目标——限定——配置调优的逻辑串了一遍:

通配符与多文件扩展流程图 规则定义 output: "QC/{s}.fastq" 通配符绑定 wildcards = {s: "sampleX"} expand() 批量触发 expand("QC/{sample}.fastq", sample=SAMPLES) 实例1: sample=X 实例2: sample=Y 实例3: sample=Z X_trimmed.fastq Y_trimmed.fastq Z_trimmed.fastq wildcard_constraints sample = "[A-Z0-9]+" 说明:当样本名有特殊 字符时,用约束防误匹配

避坑指南:通配符使用的常见问题

  • 通配符未定义:输出里用了 {sample},但输入或 params 里写的是 {s}。Snakemake 报错:WildcardError: No wildcard named 's'。解决办法——统一定义,名称一致。
  • 过度匹配:输出模式 {sample}.fastq 可能会匹配到 ERR123.fastq,也可能匹配到 ERR_123.fastq。如果 schema 不严格,会导致错误实例。用 wildcard_constraints 限定字符集。
  • expand() 与 zip 混淆:我想批量配对两个列表,结果笛卡尔积生成了 9 个文件而不是 3 个。后来老实加 zip。
  • 大小写敏感:上面讲过了,配置文件、样本列表、通配符值必须大小写一致。
最佳实践建议:把所有样本列表、数据类型列表集中定义在 Snakefile 开头,用 Python 语法处理。通配符名用描述性名称,配合 wildcard_constraints 做输入校验。这样跑批量任务时,问题一眼就能看出来。