Snakemake动态输入输出规则链报错:通配符无法从输出确定
我太懂你这个困扰了——当规则a动态生成一批{id}.txt文件后,想让后续的规则b自动逐个处理这些文件,却被"wildcards in input files cannot be determined from output files:"这个报错卡住。其实这个需求完全是支持的,只是Snakemake需要更明确的线索来追踪这些动态生成的wildcard值,而不是直接用dynamic()在后续规则里引用。
为什么会报错?
Snakemake在启动时就需要构建完整的依赖关系图,它得提前知道所有wildcard的可能取值。但你用dynamic('my/path/{id}.txt')作为规则b的输入时,这些{id}的值是规则a运行后才生成的,初始化阶段Snakemake根本没法预判,自然就会报错说无法从输出确定wildcard。
两种解决方案
方案1:提前明确wildcard取值(适用于可预知id的场景)
如果你的{id}取值是可以提前确定的(比如来自配置文件、静态列表,或者能从某个输入文件里提前解析出来),那直接把这些值定义好,用expand()来串联规则就行:
# 提前定义所有可能的id,也可以从文件读取,比如pd.read_csv("ids_list.csv")["id"].tolist() IDS = ["sample1", "sample2", "sample3"] # 定义最终目标,让Snakemake知道要生成哪些文件 rule all: input: expand("processed/{id}.final_result", id=IDS) rule a: input: "my_static_file.txt" output: "my/path/{id}.txt" shell: """ # 这里写生成单个{id}.txt的逻辑,比如根据IDS循环生成 echo "Processing {wildcards.id}" > {output} """ rule b: input: "my/path/{id}.txt" output: "processed/{id}.final_result" shell: """ # 处理每个id对应的输入文件 cat {input} > {output} """
方案2:用Checkpoint处理完全动态的输出(适用于id不可预知的场景)
如果{id}是规则a运行时才动态生成的(比如从输入文件里提取未知数量的id),那Snakemake的checkpoint机制就是专门解决这个问题的——它会先运行checkpoint规则生成动态文件,然后重新构建依赖图,这样就能捕获到新生成的wildcard值了:
# 用checkpoint替代普通规则,标记这是动态生成内容的步骤 checkpoint generate_dynamic_files: input: "my_static_file.txt" output: directory("my/path") # 用目录作为输出,告诉Snakemake这里会有动态内容 shell: """ # 这里写动态生成文件的逻辑,比如随机生成几个id的文件 mkdir -p my/path echo "Content for id_x" > my/path/id_x.txt echo "Content for id_y" > my/path/id_y.txt echo "Content for id_z" > my/path/id_z.txt """ # 定义一个函数,从checkpoint的输出目录里获取所有生成的文件 def get_dynamic_inputs(wildcards): import glob # 返回所有生成的txt文件路径 return glob.glob("my/path/*.txt") # 用wildcard函数提取每个输入文件对应的id def extract_id_from_input(input_file): # 从路径my/path/id_x.txt里提取id_x return input_file.split("/")[-1].split(".")[0] rule process_dynamic_files: input: get_dynamic_inputs output: "processed/{id}.final_result" # 告诉Snakemake如何从输入文件映射到输出的wildcard wildcard_constraints: id = extract_id_from_input(input[0]) shell: """ cat {input} > {output} """ rule all: input: # 动态获取最终要生成的结果文件路径 lambda: expand("processed/{id}.final_result", id=[extract_id_from_input(f) for f in glob.glob("my/path/*.txt")])
额外提示
如果你的规则链更长(比如b之后还有c、d规则),只需要基于checkpoint生成的文件列表,继续用同样的方式传递wildcard就行——核心就是让Snakemake在动态文件生成后,能重新识别到这些新的依赖关系。
内容的提问来源于stack exchange,提问作者Floris

