1

I am making a python program in which random values are generated n times, to be used as parameter values for model simulation.

I have a dictionary defining the boundaries for each parameter, for example:

parameters = {'A': random.uniform(1,10), 'B': random.uniform(20,40)}

I want to add some parameters the sum of which has to be 1, something like:

params = {'C1': random.uniform(0.0,1.0), 'C2': 1 - params['C1']}

This latter obviously doesn't work producing the KeyError: 'C1'

I also tried something like:

params = {'A': random.uniform(1,10), 'B': random.uniform(20,40), 'C': {'C1': None,'C2': None}}

def class_fractions():
    for key in params['C']:
        if key == 'C1':
            params['C'][key] = random.uniform(0.0,1.0)
        if key == 'C2':
            params['C'][key] = 1.0 - params['C'][key]

but after calling the function I get the TypeError

TypeError: unsupported operand type(s) for -: 'float' and 'NoneType'

Any suggestion?

amr125
  • 73
  • 1
  • 1
  • 6

3 Answers3

2

The issue with your code is because dict are not ordered in Python. When you do:

for key in params['C']

You are getting C2 key before than C1. Actually you do not even need to iterate the dict simply set values in the dict like:

def class_fractions():
    params['C']['C1'] = random.uniform(0.0,1.0)
    params['C']['C2'] = 1.0 - params['C']['C1']

Yo do not even need separate function as it updates only the same dict, simply you do it like:

params = {'A': random.uniform(1,10), 'B': random.uniform(20,40)} # create partial dict
c_dict = {'C1': random.uniform(1,10)}  # create sub-dict to store random value
c_dict['C2'] = 1 - c_dict['C1']  # get value that you want
params['C'] = c_dict       # add entry into parent dict
Moinuddin Quadri
  • 46,825
  • 13
  • 96
  • 126
1

From your use of the for loop it looks like you may really have many more parameters than just two. In that case you can generate a list of values filled with random numbers, and then scale it to sum to one, as described in this other answer. Then iterate over a zipped view of your dictionary keys and the list items and assign the items to the dictionary.

Or, operating directly on the dictionary:

params = {k: random.uniform(0, 1) for k in ('C1', 'C2', 'C3')}
total = sum(params.values())
params = {k: (v / total) for k, v in params.items()}
Community
  • 1
  • 1
z0r
  • 8,185
  • 4
  • 64
  • 83
1

If you really want ot use your class_fraction, and initialize params at once, then need to use OrderDict:

import random
from collections import OrderedDict

params = {'A': random.uniform(1,10), 'B': random.uniform(20,40), 'C':OrderedDict([('C1', None),('C2', None)])}


def class_fractions():
    for key in params['C']:
        if key == 'C1':
            params['C'][key] = random.uniform(0.0,1.0)
        if key == 'C2':
            params['C'][key] = 1.0 - params['C']['C1']


class_fractions()
print(params)

Results in:

{'B': 37.3088618142464, 'A': 2.274415152225316, 'C': OrderedDict([('C1', 0.12703200100786471), ('C2', 0.8729679989921353)])}
Marcin
  • 215,873
  • 14
  • 235
  • 294