How to write a recursion function for all the possible parameter combinations in python
Question: I am trying to write a piece of code to traverse all the possible parameter
combinations for a algorithm with python.

    
    
    import numpy as np
    parameter={'alpha1':np.linspace(0.3,0.4,10),'alpha2':np.linspace(0.9,2,100),...'alpha5':np.linspace(5,10,100)}
    

the problem is that I just couldn't write 5-nested for loop.Can anyone give a
demo on how to write a recursion function to give a list of all possible
combinations of the parameters? Thanks


Answer: You could use the `itertools.product` function, which takes a list of
iterators and creates the iterator of their cartesian product (see
[documentation](https://docs.python.org/2/library/itertools.html#itertools.product)).

In my solution I use the values from the `parameter` dictionary as the
iterators. I go over the product of the options for each key (`for
values_option in product(*parameter.values())`) and create a new dictionary
with the original keys)

    
    
    from itertools import product
    import numpy as np
    
    parameter={'alpha1':np.linspace(0.3,0.4,10),'alpha2':np.linspace(0.9,2,100)}
    
    def parameter_options(parameter):
        for values_option in product(*parameter.values()):
            yield dict(zip(parameter.keys(), values_option))
    
    for opt in parameter_options(parameter):
        print opt
    

* * *

Let's go over this piece by piece:

**`parameter.values()`**

This gives a list of the values for each key-value in the dictionary.

For example, if we have `dictionary = {a: (1, 2, 3), b: (4, 5, 6)}`, using
`dictionary.values()` will return `[(1, 2, 3), (4, 5, 6)]`. Doing
`dictionary.keys()` will give `['a', 'b']`.

_Note_ : There's a difference here between Python 2 and Python 3 - in Python 2
these methods (`keys()` and `values()`) will return normal lists, whereas in
Python 3 they return a special iterator. This shouldn't change the solution.

**`product(*parameter.values())`**

We use the asterisk to "unpack" lists. Simply put, `itertools.product`
receives any number of arguments. We want to use all the `values` as input:

    
    
    vals = parameter.values()
    product(vals[0], vals[1], vals[2], ..., vals[len(vals)])
    

Python has an easy way to pass lists as input to functions that receive an
arbitrary number of arguments. This last line is the same as `product(*vals)`.

**`for values_option in product(*parameter.values()):`**

We go over all the options for the values of the `parameter` dictionary.

The first iteration will give the first option for all arguments. The second
iteration will give the first option for all but one argument, which will have
its second option. This goes on all the way until we have the last option
where each argument has its last option.

**`zip(parameter.keys(), values_option)`**

This takes the two lists (of keys and possible values) and basically
transposes them. it gives a list of the same length which includes pairs: the
first element from the first list and the second from the second list. Like
so:

    
    
    keys = ['a', 'b', 'c', 'd']
    vals = [1, 2, 3, 4]
    zip(keys, vals) = [('a', 1), ('b', 2), ('c', 3), ('d', 4)]
    

**`dict(...)`**

Now we can use this zipped list to create a dictionary. This is a different
way to initiate a dict

    
    
    {'a': 1, 'b': 2, 'c': 3} == dict([('a', 1), ('b', 2), ('c', 3)])
    

**`yield ...`**

This is what we use in python to create iterators. It's a complicated subject
but instead of this you could write before the `for` loop this line

    
    
    options = []
    

and instead of `yield dict(...)` write `options.append(dict(...))`. and in the
end of the function `return options`.

Voila.

