I am trying to create a loop that will generate a series of probabilities against which a list of values can be compared. I need the probabilities to be in list form and the length of the list can vary. For example, if the list is lst = [1, 2, 3] and I want to set the probability of calling lst[2] at 80% with equal likelihood of the other two values, I would use p = [0.1, 0.1, 0.8].
I want to be able to cycle through all of the possible probabilities (with a definable step, in this case step = 0.1). My logic has got stuck at this point:
lst = [1, 2, 3]
step = 0.1
for p in probability_gen_function(lst, step):
print(p)
loop_1 -> p = [0.1, 0.1, 0.8]
loop_2 -> p = [0.1, 0.2, 0.7]
---
loop_n -> p = [0.8, 0.1, 0.1]
I can see ways for my probability_gen_function to generate random lists of probabilities, but not how to systematically cycle through them. Any suggestions would be appreciated.
You can use a recursive generator function:
lst = [1, 2, 3]
step = 0.1
def prob_gen(d, s, c = []):
if len(c) == len(d):
yield [round(i, 2) for i in c]
else:
r = 10-sum(int(k*10) for k in c)
for i in ([r] if len(c)+1 == len(d) else range(1, r)):
yield from prob_gen(d, s, c+[s*i])
print(list(prob_gen(lst, step)))
Output:
[[0.1, 0.1, 0.8], [0.1, 0.2, 0.7], [0.1, 0.3, 0.6], [0.1, 0.4, 0.5], [0.1, 0.5, 0.4], [0.1, 0.6, 0.3], [0.1, 0.7, 0.2], [0.1, 0.8, 0.1], [0.2, 0.1, 0.7], [0.2, 0.2, 0.6], [0.2, 0.3, 0.5], [0.2, 0.4, 0.4], [0.2, 0.5, 0.3], [0.2, 0.6, 0.2], [0.2, 0.7, 0.1], [0.3, 0.1, 0.6], [0.3, 0.2, 0.5], [0.3, 0.3, 0.4], [0.3, 0.4, 0.3], [0.3, 0.5, 0.2], [0.3, 0.6, 0.1], [0.4, 0.1, 0.5], [0.4, 0.2, 0.4], [0.4, 0.3, 0.3], [0.4, 0.4, 0.2], [0.4, 0.5, 0.1], [0.5, 0.1, 0.4], [0.5, 0.2, 0.3], [0.5, 0.3, 0.2], [0.5, 0.4, 0.1], [0.6, 0.1, 0.3], [0.6, 0.2, 0.2], [0.6, 0.3, 0.1], [0.7, 0.1, 0.2], [0.7, 0.2, 0.1], [0.8, 0.1, 0.1]]
We'll use numpy and itertools.
import numpy as np
from itertools import product
def probability_gen_function(lst, step):
# Enumerate range of probabilities.
# This array is like [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9]
probs = np.arange(step, 1, step)
# Run through all permutations
for p in product(probs, repeat=len(lst)):
# Check if sum is 1
# We check with some small number because of floating point errors
if np.abs(np.sum(p) - 1) > 1e-9:
continue
# Yield number
yield np.array(p)
lst = [1, 2, 3]
step = 0.1
for p in probability_gen_function(lst, step):
print(p.round(5))
>>>
[0.1 0.1 0.8]
[0.1 0.2 0.7]
...
[0.7 0.2 0.1]
[0.8 0.1 0.1]
There're some points to notice. First of all this function does not check step size. You should provide proper values. And, because we generate them with np.arange, there may be some floating point errors, you would want to normalize or round the probabilities. But I think this is enough to start.
This is perfect for recursiveness. I found it easier to work with a constant step size of 1 and make the normalisation of the probability vary inside the loop, but it doesn't change the output.
I also explicitly check that the step size you give allows valid solutions.
def normalised_prob_gen(num_outcomes, step_size):
num_steps = int(1 / step_size)
assert num_steps*step_size == 1, "Step size does not divide into one"
yield from probability_gen_function(num_outcomes, num_steps, num_steps)
def probability_gen_function(num_outcomes, num_steps, prob_left):
if num_outcomes == 1:
yield [prob_left / num_steps]
else:
for x in range(1, prob_left-num_outcomes+2):
for rest in probability_gen_function(num_outcomes-1, num_steps, prob_left-x):
yield [first / num_steps] + rest
for p in normalised_prob_gen(3, 0.1):
print(p)
Note that the items in lst don't matter, only the number of them, so I use that as the input.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With