This is a test version of Biostars. For the public version, visit https://www.biostars.org.
Snakemake expand(): using a helper function (zip) and imputing wildcards?

I am wondering if it is possible to combine both helper functions and wildcards using snakemake's expand().

This is not what I want, but it works:

report = lambda wildcards: expand("{sample_directory}/{sample}.A.report.html", zip, sample_directory=config["sample_directory"], sample=config["sample"])

This works with the helper function zip() to tie the {sample_directory} and {sample} together into something like this:

sample_1/sample_1.A.report.html

Here's the issue: I want to keep {sample_directory} and {sample} zipped together for this output, but the letter A (hardcoded above) should actually be a wildcard itself like so:

report = lambda wildcards: expand("{sample_directory}/{sample}.{ABCDE}.report.html", zip, sample_directory=config["sample_directory"], sample=config["sample"])

Where an example of outputs would be:

  • sample_1/sample_1.A.report.html
  • sample_1/sample_1.B.report.html
  • sample_1/sample_1.C.report.html ...

Can expand() do something like this where there are some wildcards with a function applied and others without in the same expand()? I have tried masking the {ABCDE} wildcard like so {{ABCDE}}, but it doesn't work...

snakemake

lambda wildcards are for lookups, like when you have a dictionary and need to use a function to get the value. Where is your lookup function?

I agree with Jeremy that your question lacks context. You should provide at least the full rule in which the statement is to appear, including input and output. Otherwise, if you have single scalar values, you can just use python string concatenation with '+'. Also, in the beginning, one tends to overcomplicate things. More often than not there is an easier way than using lambda functions and simple problems should be rather addressed with rule logic.

Not totally sure I'm right about your use case (are those config entries lists of strings, which is why you need to zip them?) but, you can nest multiple calls to expand, and if you combine that with doubled-up braces in the string and zip in one of the two calls, I think it could do what you're looking for.

$ cat test.smk 
SAMPLE_DIRS = ["sample_1", "sample_2", "sample_3"]
SAMPLES = ["sample_1", "sample_2", "sample_3"]
for path in expand(
        expand("{sample_directory}/{sample}.{{ABCDE}}.report.html", zip, sample_directory=SAMPLE_DIRS, sample=SAMPLES),
        ABCDE=["A", "B", "C", "D", "E"]):
    print(path)
$ snakemake --quiet all -s test.smk
sample_1/sample_1.A.report.html
sample_1/sample_1.B.report.html
sample_1/sample_1.C.report.html
sample_1/sample_1.D.report.html
sample_1/sample_1.E.report.html
sample_2/sample_2.A.report.html
sample_2/sample_2.B.report.html
sample_2/sample_2.C.report.html
sample_2/sample_2.D.report.html
sample_2/sample_2.E.report.html
sample_3/sample_3.A.report.html
sample_3/sample_3.B.report.html
sample_3/sample_3.C.report.html
sample_3/sample_3.D.report.html
sample_3/sample_3.E.report.html

1 answer

Why not just:

report = expand(config["sample_directory"]+"/"+config["sample"]+".{letter}.report.html", letter = ["A", "B", "C"])

However, I noticed that without some context, this may not be worth much. Here is a minimal ruleset that illustrates the application:

LETTERS = ["A", "B", "C"]
rule all:
     input:
        report = expand(config["sample_directory"]+"/"+config["sample"]+".{letter}.report.html", letter = LETTERS )

rule generate_reports:
     input: "{dir}/{sample}.fastq"
     output: expand("{{dir}}/{{sample}}.{letter}.report.html", letter = LETTERS)
     shell:
               """
               for F in {output} 
               do 
                    touch $F 
               done                       
              """

Log in to answer this question.