Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I use a for loop to programmatically generate snakemake rules?

Tags:

snakemake

I would like to use a for loop to generate a set of rules, based on the contents of of the config.yaml file. Here's a minimum working example.

config={'person1': 'Amy', 'person2': 'Bruce'}
                       
for thiskey,thisperson in config.items():
    rule:  
        name: f'{thisperson}s_rule'
        output: f'{thiskey}.txt'
        run:
            print(f'\n{thiskey} is called {thisperson}!\n')
            
rule people:
    input: [this+'.txt' for this in config.keys()]

If you run it (use the -k switch to keep going and ignore the missing files), you'll see that the same thing is being written to the screen for both files (person2 is called Bruce!) whereas you would expect to see a similar messages for both Amy and Bruce.

Is this at all a sensible way to setup rules? Or should I find a workaround?

Thanks,

Mark

like image 914
Mark Payne Avatar asked Sep 20 '25 04:09

Mark Payne


2 Answers

config = {'person1': 'Amy', 'person2': 'Bruce'}

def create_rule(thiskey, thisperson):
    rule:
        name: f"{thisperson}s_rule"
        output: f"{thiskey}.txt"
        run:
            print(f'\n{thiskey} is called {thisperson}!\n')

# Generate rules
for thiskey, thisperson in config.items():
    create_rule(thiskey, thisperson)

rule people:
    input: [this + '.txt' for this in config.keys()]

TTry to pass the loop variables as default arguments to an inline function, forcing their evaluation at each iteration.

like image 105
AndreaCavallo.class Avatar answered Sep 22 '25 23:09

AndreaCavallo.class


Maybe your real case is more complex, but the typical way of implementing your example would not need to generate rules on the fly:

config={'person1': 'Amy', 'person2': 'Bruce'}
                       
rule people:
    input: 
        expand('{thiskey}.txt', thiskey=config.keys()), # same as: [this+'.txt' for this in config.keys()]

rule make_person:
    output:
        '{thiskey}.txt',
    params:
        thisperson=lambda wc: config[wc.thiskey],
    run:
        print(f'\n{wildcards.thiskey} is called {params.thisperson}!\n')
like image 29
dariober Avatar answered Sep 23 '25 00:09

dariober