2. Snakefile 基础:规则定义、输入输出、通配符、参数

好,咱们正式开始动手写 Snakefile。说白了一个 Snakefile 就是一个 规则文件。你告诉 Snakemake:“我要从 A 文件跑到 B 文件,中间用什么脚本跑,跑完后产出什么东西。” 它本质就是一个依赖关系图,也就是 DAG。我最初上手时,就觉得这玩意儿跟 Makefile 很像,但 Snakemake 在 Python 的基础上加了一层威力巨大的 通配符 系统。这其实是整个 DAG 审计的起点。

2.1 规则的骨架

一个规则长啥样?核心就四个关键字:rule、input、output、shell 或 run。比如下面这个最简单的例子:

rule say_hello:
    output: "hello.txt"
    shell:
        "echo 'Hello, Snakemake!' > hello.txt"

我刚开始写的时候,习惯把 input 和 output 写成绝对路径。后来被同事喷了:“你写绝对路径,换个机器怎么跑?” 所以记住了——用 相对路径,并且尽量用 config 来管理。

规则还可以有个名字,比如上面那个叫 say_hello。Snakemake 默认会按顺序推断执行,但真正决定顺序的是 输出依赖。你有一个规则产出 hello.txt,另一个规则输入 hello.txt,那它俩就形成一条边(edge)。DAG 就是这么构建出来的。

2.2 输入与输出

input 和 output 支持多种形式。最常见的是字符串或列表。看个例子:

rule process_data:
    input:
        "data/raw.csv",
        "config/params.yaml"
    output:
        "results/processed.csv"
    shell:
        "python scripts/process.py {input} {output}"

这里 {input} 会自动展开成两个文件的路径。我个人习惯在 shell 里显式写出每个输入,比如 {input[0]},这样管道脚本的参数顺序就不会错。踩过坑嘛——有一次我把两个输入顺序搞反,结果下游脚本把 CSV 文件当 YAML 读,直接崩溃。

画重点:input 里的文件可以不存在于磁盘上,Snakemake 会自动寻找能产出它的规则。如果找不到,抛错。

2.3 通配符

通配符是 Snakemake 的精髓,没有它,你就得手写成百上千条规则。比如你要处理一百个样本,用通配符一条规则就搞定:

rule align:
    input:
        "data/{sample}.fastq"
    output:
        "results/{sample}.bam"
    shell:
        "bwa mem ref.fa {input} > {output}"

这里 {sample} 就是通配符。你运行 snakemake results/sampleA.bam,它自动把 {sample} 替换成 sampleA。我在项目中遇到过最头疼的事是通配符冲突。比如两个规则用了同一个通配符名但指向不同文件,Snakemake 会报错:”AmbiguousRuleException“。怎么解决?把通配符名字写得更具体。比如 {sample_id} 和 {batch_id}。

我建议: 用一个明确的命名约定。比如输入文件叫 {sample}.fastq,输出就叫 {sample}.bam,保持变量名在全局唯一。

2.4 参数

参数 (params) 是用来传非文件信息的。比如线程数、阈值、标志位。本质上 params 是一个字典,你在 shell 里通过 {params.key} 引用。

rule variant_call:
    input:
        bam="results/{sample}.bam",
        ref="genome.fa"
    output:
        vcf="results/{sample}.vcf"
    params:
        qual=30,
        threads=4
    shell:
        "bcftools mpileup -f {input.ref} -q {params.qual} "
        "--threads {params.threads} {input.bam} -o {output.vcf}"

你可能问:为什么不用 input 里放个参数文件?因为参数文件不是真正的数据依赖,改了内容但文件名不变,Snakemake 不会重新执行。而 params 如果变了,Snakemake 会判定规则“状态不同”,自动重跑。

避坑: 我曾经把线程数写进 input 的配置文件里,结果调整了线程数,Snakemake 死活不重新跑。最后发现它只看文件名,不看文件内容。所以我建议:一切会影响运行结果但又不是文件的参数,扔进 params。

2.5 规则之间的依赖关系

咱们用一张图串起这节的核心逻辑。下面的 SVG 展示了从单个规则到通配符展开、再到多规则拼接的完整流程:

Snakefile 基础知识体系:从规则到 DAG 规则定义 rule name: ... input / output 文件路径 + 别名 通配符 {sample} 自动展开多文件 params 非文件参数传递 shell / run 执行命令或 Python 代码 多个规则通过 input-output 匹配自动拼接成 DAG rule A → out_a rule B → out_b rule C → out_c 通配符让单条规则能处理成千上万文件,这正是 DAG 高效审计的基础

这个流程很简单:定义规则、填充输入输出、用通配符批量展开、通过 params 注入非文件参数,然后把命令扔进 shell 或 run 里。当你写好两条以上规则,它们之间的输入输出会自动连接成一张图。审计 DAG,本质上就是检查这张图有没有断边、有没有循环、有没有多余节点。

总结:
  • 规则定义:一个 rule 块,包含名字和 body。
  • 输入输出:决定了文件依赖关系,是 DAG 的边。
  • 通配符:用 {wildcard} 达到“一规则多文件”的效果。
  • 参数:params 用来传线程数、阈值等非文件参数,变了会自动重跑。

嗯,第2章就到这儿。Snakefile 的玩法远不止这些,后面咱们会接触到 checkpoint、log、benchmark 等实用功能。你只要把规则、输入输出、通配符和参数这四个家伙搞明白,后面的路就好走多了。