Search code examples
pythonrandomproductpython-itertoolscartesian-product

Python - Cartesian product on lists with random variables in some of the list


I want to do Cartesian product between some lists.

Some of the lists might contain random variables that should be randomized when the product perform.

I have tried the following method.

import random


a_list = ['low', 'medium', 'high']
b_list = [0, 1]

dict_list = []
for a in a_list:
    for b in b_list:
        if a == 'low':
            a_random = random.uniform(0, 3)
        elif a == 'medium':
            a_random = random.uniform(3, 6)
        elif a == 'high':
            a_random = random.uniform(6, 100)
        dict_list.append({'a': a_random, 'b': b})

print(dict_list)

Output:

[{'a': 2.5067206438005165, 'b': 0},
{'a': 2.846737783049243, 'b': 1},
{'a': 4.515841550661135, 'b': 0},
{'a': 5.570169974274982, 'b': 1},
{'a': 26.509898440764896, 'b': 0},
{'a': 57.48321086944802, 'b': 1}]

The above example is just a toy example.

There will be more lists and more random variables in my actual situation. e.g.:

# a_list = [random.uniform(0, 3), random.uniform(3, 6), random.uniform(6, 100)]
a_list = ['low', 'medium', 'high'] 
b_list = [0, 1]
# c_list = [random.uniform(0, 10), random.uniform(10, 100), random.uniform(100, 1000)]
c_list = ['low', 'medium', 'high']
d_list = ['A', 'B', 'C', 'D']

Is there better ways to achieve the same result?

Maybe using itertools? or generators?

Thanks!


Solution

  • Handle the randoms after you create the list.

    def lookup1(val):
        if val == 'low':
            return random.uniform(0,3)
        elif val == 'medium':
            return random.uniform(3,6)
        else:
            return random.uniform(6,100)
    
    want = []
    for a,b in itertools.product(a_list, b_list):
        want.append( [lookup1(a), b] )
    

    You could probably parameterize that even more. Maybe create a Distribution class that holds the distribution and generates the right ranges.

    Followup

    Here's how you might embed the distribution points into a class:

    class Distribution:
        def __init__( self, limits ):
            self.limits = limits
    
        def lookup(self, val):
            l = self.limits
            if val == 'low':
                return random.uniform(l[0],l[1])
            elif val == 'medium':
                return random.uniform(l[1],l[2])
            else:
                return random.uniform(l[2],l[3])
    
    distr_a = Distribution( [0, 3, 6, 100] )
    distr_b = Distribution( [0, 10, 100, 1000] )
    
    want = []
    for a,b in itertools.product(a_list, b_list):
        want.append( [distr_a.lookup(a), b] )
    

    It is left as an exercise for the reader to extend this to lists other than ['low','medium','high']. Even that isn't hard.