Search code examples
pythoniteratorpython-itertools

Multi-list iteration with default value in Python


I'm running an experiment with python simulation, and need to create a list of input cases.

Each case is an instance taken from a list of list of parameters, For example:

heights = [100,110,120]
alphas = [0.1,0.01,0.001]
C = [0.1,0.2,0.5,0.9]
B = [1,2]

and the list of lists is:

params = [heights,alphas,C,B]

The original amount is bigger (~30x100) so it's not efficient to address each list directly.

Eventually each input case is a tuple/namedtuple/dictionary of one parameter from each category, for example:

instance = {'height':100, 'alpha':0.1,'C:0.1, 'B':1}

I would like to iterate the parameter space in order to create a list of instances - but here is the catch:

Instead of making a cartesian product of all of them, per each parameter I want to iterate all the options, while the rest of categories are set to a default value (the first). For example:

params = [[1,2,3],[4,5][7,8]]

the expected set of instances (with no repeat) is:

[(1,4,7),(2,4,7),(3,4,7),(1,5,7)(1,4,8)]

where 1 is the default of first index, 4 is the default of the second index and 7 is the default of the 3rd.

Making a cartesian product is quite easy with itertools:

from itertools import product
params = [[1,2,3],[4,5],[7,8]]
list(product(*params))

[(1, 4, 7), (1, 4, 8), (1, 5, 7), (1, 5, 8), (2, 4, 7), (2, 4, 8), (2, 5, 7), (2, 5, 8), (3, 4, 7), (3, 4, 8), (3, 5, 7), (3, 5, 8)]

then I can filter out unnecessary instances, but it sounds ineficient to generate them at first place. Is there any elegant way to build this iteration?


Solution

  • params = [[1, 2, 3], [4, 5], [7, 8]]
    default = [par[0] for par in params]
    instances = set()
    
    for ii, pp in enumerate(params):
        for value in pp:
            new_instance = default[:ii] + [value] + default[ii + 1:]
            instances.add(tuple(new_instance))