Python 在Snakemake中组合多个通配符

Python 在Snakemake中组合多个通配符,python,snakemake,fastq,Python,Snakemake,Fastq,我想按样本计数读取次数,然后按目录连接所有计数。 我创建了一个字典,将示例1和2链接到目录1,将示例3和4链接到目录2 ├── DIR1 │ ├── smp1.fastq.gz │ ├── smp1_fastqc/ │ ├── smp2.fastq.gz │ └── smp2_fastqc/ └── DIR2 ├── smp3.fastq.gz ├── smp3_fastqc/ ├── smp4.fastq.gz └── smp4_fastqc/

我想按样本计数读取次数,然后按目录连接所有计数。
我创建了一个字典,将示例1和2链接到目录1,将示例3和4链接到目录2

├── DIR1
│   ├── smp1.fastq.gz
│   ├── smp1_fastqc/
│   ├── smp2.fastq.gz
│   └── smp2_fastqc/
└── DIR2
    ├── smp3.fastq.gz
    ├── smp3_fastqc/
    ├── smp4.fastq.gz
    └── smp4_fastqc/
然后,我创建了一个规则来按样本计算我的读取次数

DIRS,SAMPLES = glob_wildcards(INDIR+'/{dir}/{smp}.fastq.gz')

# Create samples missing
def filter_combinator(combinator, authlist):
    def filtered_combinator(*args, **kwargs):
        for wc_comb in combinator(*args, **kwargs):
            if frozenset(wc_comb) in authlist:
                yield wc_comb
    return filtered_combinator

# Authentification
combine_dir_samples = []

for dir in DIRS:
    samples, = glob_wildcards(INDIR+'/'+dir+'/{smp}.fastq.gz')
    for smp in samples:
        combine_dir_samples.append( { "dir" : dir, "smp" : smp} )
       
combine_dir_samples = { frozenset( x.items() ) for x in combine_dir_samples }
dir_samples = filter_combinator(product, combine_dir_samples)
但是,我想添加一个规则来按目录连接我的
smp\u nreds.txt
文件

rule all:
    input:
        expand(INDIR+'/{dir}/{smp}_Nreads.txt', dir_samples, dir=DIRS, smp=SAMPLES)

rule countReads:
    input:
        INDIR+'/{dir}/{smp}_fastqc/fastqc_data.txt'
    output:
        INDIR+'/{dir}/{smp}_Nreads.txt'
    shell:
        "grep 'Total\ Sequences' {input} | awk '{{print {wildcards.dir},$3}}' > {output}"

---------------------------------------------------------------
# result ok

├── DIR1
│   ├── smp1_Nreads.txt
│   └── smp2_Nreads.txt
└── DIR2
    ├── smp3_Nreads.txt
    └── smp4_Nreads.txt

> cat smp1_Nreads.txt
DIR1 15082186
我尝试使用不同的输入语法来实现concat规则

rule concatNreads:
    input:
        expand(INDIR+'/{dir}/{smp}_Nreads.txt', dir_samples, dir=DIRS, smp=SAMPLES)
    output:
        INDIR+'/{dir}/Nreads_{dir}.txt'
    shell:
        "cat {input} > {output}"
------------------------------------------------------------------
# result

├── DIR1
│   └── Nreads_DIR1.txt
└── DIR2
    └── Nreads_DIR2.txt

# but both files are identical
> cat Nreads_DIR1.txt
DIR1 15082186
DIR1 22326081
DIR2 11635831
DIR2 45924459

# I would like to have
> cat Nreads_DIR1.txt
DIR1 15082186
DIR1 22326081
> cat Nreads_DIR2.txt
DIR2 11635831
DIR2 45924459
我找不到任何解决办法,就好像这条规则不在乎我的措辞


编辑

我试着用一个单词来代替我的组合
filter\u combinator
,并用一个函数作为规则的输入来获取样本

expand(OUTFastq+'/{dir}/FastQC/{{smp}}_Nreads.txt', dir_samples, dir=DIRS)
lambda wildcards: expand(OUTFastq+'/{dir}/FastQC/{wildcards.smp}_Nreads.txt', dir_samples, dir=DIRS, smp=SAMPLES)
expand(OUTFastq+'/{dir}/FastQC/{wildcards.smp}_Nreads.txt', dir_samples, dir=DIRS, smp=SAMPLES)

首先,我认为您已经将解决方案过度复杂化,使其对Snakemake不那么惯用。因此,您在执行规则时遇到了问题。无论如何,让我以你提出的形式回答你的问题

两个
Nreads\u DIRx.txt
文件完全相同,这一点也不奇怪,因为输入不依赖于输出中的任何通配符:

rule concatnreds:
输入:
展开(INDIR+'/{dir}/{smp}\u nreds.txt',dir\u samples,dir=DIRS,smp=samples)
在这里,
expand
函数解析产生完全指定文件名列表的
dir
smp
变量。您需要的是真正取决于输出中的通配符的东西:

rule concatnreds:
输入:
lambda通配符:。。。
{dir}
是用输出中的通配符完全指定的,因此您不需要从
DIRS
变量为其赋值:

rule concatnreds:
输入:
lambda通配符:展开(INDIR+'/{dir}/{smp}\nreds.txt',dir=wildcards.dir,smp=func(wildcards.dir))
现在的问题是如何实现这个生成目录样本列表的
func
函数。我花了一段时间才理解您使用
combine\u dir\u samples
filter\u combinator
的技巧,因此我将把这一点留给您使用该代码实现
func
函数。但您真正需要的是来自DIR->SAMPLES的映射:

dir_to_samples={“DIR1”:[“smp1”,“smp2”],“DIR2”:[“smp3”,“smp4”]}
def func(目录):
将目录返回到样本[dir]
这个
dir_to_samples
可能更容易评估,但下面是您修改后的解决方案:

dir_to_samples = {"DIR1": ["smp1", "smp2"], "DIR2": ["smp3", "smp4"]}

def func(dir):
    return dir_to_samples[dir]

rule all:
    input:
        lambda wildcards: expand(OUTDIR+'/{dir}/FastQC/{smp}_fastqc.zip', dir=wildcards.dir, smp=func(wildcards.dir))

rule fastQC:
    input:
        lambda wildcards: expand(INDIR+'/{dir}/{smp}.fastq.gz', dir=wildcards.dir, smp=func(wildcards.dir))
    output:
        OUTDIR+'/{dir}/FastQC/{smp}_fastqc.zip'
    shell:
        "fastqc {input} -o {OUTDIR}/{wildcards.dir}/FastQC/" 

> AttributeError: 'Wildcards' object has no attribute 'dir'

combine_dir_samples
的技巧来自这里:我不想使用通配符
{dir}
中的所有目录,这就是为什么我更喜欢将我的{dir}通配符扩展到我自己选择的列表。我用函数中返回的
dir\u to\u samples
尝试了您的方法。但是:
错误:列表索引必须是整数或切片,而不是str
,因此我无法直接返回它。关于您的函数:我尝试在列表中循环并测试索引是否等于dir:
如果dir\u to\u samples[I]==dir:return dir\u to\u samples[I]
,也不起作用,因为这不是我应该测试的指标,而是它的关键。我不知道怎么做。关于我的
combined\u dir\u sample
如果您查看我的
规则all
我使用此组合展开第一个规则的输出
count reads
,效果很好,我的输出中没有相同的文件。我想做同样的输入。但是,如果你能找到正确的方法来使用你的函数,而且它更简单,那就更好了。对于你来说,让我的
combine_dir_示例
如下所示:
{frozenset({('dir','DIR1'),('smp','smp1')}),frozenset({('dir','DIR1'),('smp','smp2')),frozenset({('dir dir dir 2'),('smp','smp3')),frozenset({('dir dir,'dir,'dir 2'),dir,('smp',smp4')}
@Elysire,
dir_to_samples
必须是一个
dict
(参见我的示例
dir_to_samples={“DIR1”:[“smp1”,“smp2”],“DIR2”:[“smp3”,“smp4”]}
)。您遇到XY问题。您尝试重用的解决方案基于这样一个问题:当不需要某些特定的通配符组合时,如何在snakemake中使用expand?但使用
expand
函数并不是唯一的方法。
for dir in DIRS:
    samples, = glob_wildcards(INDIR+'/'+dir+'/{smp}.fastq.gz')
    dir_to_samples.append({dir: samples})