Snakemake 配置与变量:配置文件、全局变量、params、resources

说实话,Snakemake 最让我欣赏的一点就是它的配置系统。你不需要把参数硬编码在规则里,而是可以通过外部文件、全局变量、params 和 resources 灵活传递。今天我们就把这几个概念彻底理清楚。

配置文件:把参数从代码里拆出来

我习惯把配置单独写成一个 config.yaml,然后在 Snakefile 开头用一行指令加载:

configfile: "config.yaml"

以后要改参数,编辑 YAML 文件就行,不用动 Snakefile。我最怕的就是跑管道时反复修改脚本,稍不留神就改出 bug。

支持 YAML、JSON、TOML 三种格式。我个人偏爱 YAML,可读性好,也支持注释。举个例子:

# config.yaml
samples: ["sample1", "sample2", "sample3"]
reference: "refdata/hg38"
min_cells: 3
min_features: 200
threads: 8

路径建议用相对路径或全局变量,别写死绝对路径。我在项目中吃过这个亏——把绝对路径写到配置文件里,换到集群上跑时全得重改。

全局变量 config:管道里的「共享状态」

Snakemake 把配置文件的内容自动变成一个全局字典 config。你在任何规则里都能通过 config["key"] 或 config.get("key", default) 访问。

当然,config 里不仅能放从外部文件读来的值,也可以在 Snakefile 里直接赋值:

config["project"] = "scRNA_2024"
config["output_dir"] = "results/"

不过我不推荐在 Snakefile 里大段赋值——那和硬编码没区别。配置文件才是正道。

另外,运行 snakemake 时可以通过 --config key=value 覆盖已有值:

snakemake --cores 8 --config min_cells=5

这样调试很方便。你想想,不用改任何文件就能试参数。

params:给规则传参数的灵活工具

每个 rule 里可以用 params 指令定义额外的参数,这些参数可以来自 config、wildcards 甚至处理过的表达式。

rule create_seurat_object:
    input:
        counts = "data/{sample}.mtx",
        barcodes = "data/{sample}.barcodes.tsv"
    output:
        seurat_obj = "results/{sample}_seurat.rds"
    params:
        min_cells = config["min_cells"],
        min_features = config["min_features"],
        project = "my_project"
    shell:
        """
        Rscript scripts/create_seurat.R \
            --input {input.counts} \
            --barcodes {input.barcodes} \
            --output {output.seurat_obj} \
            --min_cells {params.min_cells} \
            --min_features {params.min_features}
        """

看到没?参数可以混搭:从 config 取值,也可以写固定值。甚至可以基于 wildcards 动态计算:

params:
    outfile = lambda wildcards: f"intermediate/{wildcards.sample}_qc.pdf"

注意:params 的值会在规则运行时被求值。如果你用了 lambda,确保函数执行没有副作用。我曾经写过一段从网络请求参数的 lambda,结果每次重启管道都要重新下载,浪费时间。

resources:告诉调度器你用了多少资源

resources 指令用来声明规则需要消耗的资源:内存、CPU 时间、GPU 等。它不是强制的,但强烈建议写上——尤其是在集群上跑管道时。

rule run_qc:
    input:
        seurat_obj = "results/{sample}_seurat.rds"
    output:
        qc_report = "results/{sample}_qc.html"
    resources:
        mem_mb = 8000,
        threads = 4,
        runtime = 30
    shell:
        "Rscript scripts/run_qc.R {input.seurat_obj} {output.qc_report}"

调度器会根据你声明的 resources 分配节点。比如在 SLURM 集群上,Snakemake 能自动转成 --mem --cpus-per-task 等参数。你不需要手动写 sbatch 脚本,它全帮你搞定。

常见的资源键名有:

键 单位 说明
mem_mb MB 所需内存
mem_gb GB Snakemake 7.8+ 支持,自动换算
threads 整数 并行线程数
runtime 分钟 预计运行时间
disk_mb MB 磁盘空间(部分集群支持)
gpu 整数 GPU 数量

params vs resources 如何选择?

  • params 只传递值,不影响调度;resources 影响资源分配。
  • 如果参数只用于脚本内部(比如阈值、文件名后缀),用 params。
  • 如果参数决定了计算资源需求(内存、线程、时间),用 resources。

一张图看懂配置流动

下面这张图我把整个流程串了起来:配置文件、Snakefile、config 字典、params 和 resources 之间的关系一清二楚。你看完后应该能把握整个体系。

Snakemake 配置与变量关系 外部配置文件 Snakefile config 字典 params resources 说明 箭头表示数据流向 params 和 resources 实例化后

实战:一个完整的 Seurat 管道配置

下面我给你展示一个真实项目的片段。配置文件长这样:

# config.yaml for Seurat pipeline
samples:
    - sample1
    - sample2
    - sample3
reference_dir: "/data/refs/hg38"
qc:
    min_cells: 3
    min_features: 200
    max_mito: 0.05
clustering:
    resolution: 0.8
    dims: 1:30
resources:
    default_mem: 8000
    default_threads: 4
    qc_threads: 2
    clustering_mem: 16000

然后在 Snakefile 里用 configfile: "config.yaml" 加载,接着可以写出这样的规则:

rule filter_cells:
    input:
        seurat_obj = "results/{sample}_raw.rds"
    output:
        seurat_obj = "results/{sample}_filtered.rds"
    params:
        min_cells = config["qc"]["min_cells"],
        min_features = config["qc"]["min_features"],
        max_mito   = config["qc"]["max_mito"]
    resources:
        mem_mb = config["resources"]["qc_threads"] * 2000,
        threads = config["resources"]["qc_threads"]
    shell:
        """
        Rscript scripts/filter_cells.R \
            --input {input.seurat_obj} \
            --output {output.seurat_obj} \
            --min_cells {params.min_cells} \
            --min_features {params.min_features} \
            --max_mito {params.max_mito}
        """

我的经验:资源尽量和配置里的数字统一管理,别在 rule 里写死 mem_mb = 8000。一旦集群资源变化,你只需要改 config.yaml 一处就可以了。

避坑指南

  • 别把 configfile 写错路径。我曾经把配置文件名写成了 config,没有扩展名,Snakemake 根本认不出来。(还是小写 config.yaml 最稳。)
  • 小心 YAML 的布尔值陷阱。yes/no/true/false 会被解析成布尔类型。如果你需要字符串 "yes",请加引号。
  • resources 只是声明,不保证分配。调度器会尽量满足,但如果你没有配置集群,Snakemake 默认忽略 resources。我在本地测试时经常忘了设 --resources,结果导致并行太多把内存撑爆。
  • params 中的 lambda 延迟求值。如果依赖 wildcards,确保在 lambda 内部能用 wildcards.sample 访问,不要在 lambda 外面就计算了。

好了,配置这一块的内容基本就这些了。你能清晰地区分配置文件、config 字典、params 和 resources,就起码掌握了 Snakemake 管道里参数流动的骨架。后面写规则才不容易犯迷糊。