Hwi Moon
Hwi Moon

Reputation: 13

Generate an array of random floats summing to 1 while fixing a few elements in python

I have something like below:

random_array = np.random.random(10)

scaled_array = random_array/np.sum(random_array)

This gives me a nice array with random floats that sum to 1. However, I am trying to take this a step further and do the following:

For example, fix the 2nd and 5th elements to be 0.04 and 0.09 respectively, and generate all other elements randomly. But the sum of the whole array still needs to be exactly 1.

Taking one more step, I want to provide an upper (lower) bound for all/each element(s). For example, I still want to fix the 4th element to be 0.09 but ALSO want to force ALL elements to be LESS THAN 0.1. (They will still add up to 1 because I have more than 10 elements.)

How can I achieve this?

Upvotes: 0

Views: 241

Answers (2)

shapiromatron
shapiromatron

Reputation: 627

If you want the values before scaling:

import numpy as np

random_array = np.random.random(10)
random_array[1] = 0.04
random_array[4] = 0.09
scaled_array  = random_array/np.sum(random_array)
assert np.isclose(1, scaled_array.sum())

If you want fixed values after scaling:

import numpy as np

random_array = np.random.random(10)
random_array[1] = 0
random_array[4] = 0

scaled_array  = (random_array/np.sum(random_array)) * (1.0 - (0.04 + 0.09))
scaled_array[1] = 0.04
scaled_array[4] = 0.09

assert np.isclose(1, scaled_array.sum())

Upvotes: 1

MSS
MSS

Reputation: 3633

Try the string cutting approach of dirichlet distribution:

N=7 # total number of elements in result
d = {2:0.04, 5:0.09} # dictionary with index as key and values
fixed_sum = 0.
result = np.zeros(N) # placeholder numpy array
# Put the fixed elements in their place and calculate their sum
for k,v in d.items():
    result[k] = v
    fixed_sum = fixed_sum + v

remaining_sum = 1 - fixed_sum
# Use dirichlet distribution to get elements which sum to 1.
# Multiply with remaining_sum to get elements which sum to "remaining_sum".
remaining_arr = np.random.default_rng().dirichlet(np.ones(N-len(d)))*remaining_sum 
# Get the index of result where elements are zero.
zero_indx = np.nonzero(result==0)[0]
# Place the elements of remaining_arr  in the result.
result[zero_indx] = remaining_arr

Upvotes: 0

Related Questions