Snakemake让我超级困惑。我有一些格式的文件。
indir/type/name_1/run_1/name_1_processed.out
indir/type/name_1/run_2/name_1_processed.out
indir/type/name_2/run_1/name_2_processed.out
indir/type/name_2/run_2/name_2_processed.out
哪儿 type
, name
而这些数字是可变的。我想把所有名字相同的文件都集中到一个目录下。
outdir/type/name/name_1-1.out
outdir/type/name/name_1-2.out
outdir/type/name/name_2-1.out
outdir/type/name/name_2-2.out
我如何写一个Snakemake规则来实现这个目标?我首先尝试了下面的方法
rule rename:
input:
"indir/{type}/{name}_{nameno}/run_{runno}/{name}_{nameno}_processed.out"
output:
"outdir/{type}/{name}/{name}_{nameno}-{runno}.out"
shell:
"cp {input} {output}"
# example command: snakemake --cores 1 outdir/type/name/name_1-1.out
这样做是可行的,但这样做并不省力,因为我必须提前知道输出文件是什么,所以基本上我必须把所有的输出文件作为一个参数列表传给 snakemake
,需要使用一些shell技巧来获取变量。
于是我尝试使用 directory
(以及放弃保存 runno
).
rule rename2:
input:
"indir/{type}/{name}_{nameno}"
output:
directory("outdir/{type}/{name}")
shell:
"""
for d in {input}/run_*; do
i=0
for f in ${{d}}/*processed.out; do
cp ${{f}} {output}/{wildcards.name}_{wildcards.nameno}-${{i}}.out
done
let ++i
done
"""
这给我带来了错误。Wildcards in input files cannot be determined from output files: 'nameno'
. 我明白了。{nameno}
不存在于 output
. 但我不希望它出现在目录名中,只希望它出现在被复制的文件名中。
另外,如果我删除 {nameno}
然后它就会因为找不到正确的输入文件而抱怨。
对于我想做的事情,最好的做法是什么?另外,如何理解在 snakemake 中,你指定的是输出,而不是输入?我想这后一个事实才是让人困惑的地方。
我想你需要的是 expand
职能。
rule all:
input: expand("outdir/{type}/{name}/{name}_{nameno}-{runno}.out",
type=TYPES,
name=NAMES,
nameno=NAME_NUMBERS,
runno=RUN_NUMBERS)
职能: TYPES
, NAMES
, NAME_NUMBERS
和 RUN_NUMBERS
是这些参数的所有可能值的列表。您需要硬编码或使用 glob_wildcards
函数来收集这些数据。
TYPES, NAMES, NAME_NUMBERS, RUN_NUMBERS, = glob_wildcards("indir/{type}/{name}_{nameno}/run_{runno}/{name}_{nameno}_processed.out")
但这样会产生重复的数据 如果不希望这样,请删除重复的数据。
TYPES, NAMES, NAME_NUMBERS, RUN_NUMBERS, = map(set, glob_wildcards("indir/{type}/{name}_{nameno}/run_{runno}/{name}_{nameno}_processed.out"))