Create Array with Binomial Values (Most Time Efficient) based on conditions

Viewed 141

Currently I am working on a project that involves creating an array with 10 binomial values 0 and 1 and a given success rate (= ci_rate[i]/1'000).

Due to the fact that the rate is different for each of the 10 years, I run a loop 10 times that is creating 20'000 binomial values each time (for 20'000 scenarios).

The success rate for the binomial values is very small, but is an absorbing state for the following years. Simplified for only 10 scenarios and 10 years I would like to output the following:

[1,0,0,0,0,0,0,0,0,0]
[1,0,0,0,0,0,0,1,0,0]
[1,0,0,1,0,0,0,1,0,0]
[1,0,0,1,0,0,0,1,0,0]
[1,0,0,1,0,0,0,1,0,0]
[1,0,0,1,0,0,0,1,0,0]
[1,0,0,1,0,1,0,1,0,0]
[1,0,0,1,0,1,0,1,0,0]
[1,0,0,1,0,1,0,1,0,0]
[1,0,0,1,0,1,0,1,0,0]

Currently I am solving the problem in this way:

for j in range(20000):
    tem = np.zeros(len(ci_rate))
    for i in range(len(ci_rate)):
        if i == 0:
            tem[0] = (np.random.binomial(1, p = ci_rate[i] / 1000))
        else:
            tem[i]= int(np.where(tem[i-1]==1, 1, np.random.binomial(1, p = ci_rate[i] / 1000)))

    ci_sim.append(tem)

Is anyone creative enough to solve this more time efficient?

3 Answers

This solution first ignores the persistence rule and enforces it afterwards using maximum.accumulate.

ci_rate = np.random.uniform(0, 0.1, 10)
res = np.maximum.accumulate(np.random.random((20000, ci_rate.size))<ci_rate, axis=1).view(np.int8)
res[:20]
# 
# array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
#        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
#        [0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
#        [0, 0, 0, 0, 0, 1, 1, 1, 1, 1],
#        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
#        [0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
#        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
#        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
#        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
#        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
#        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
#        [0, 1, 1, 1, 1, 1, 1, 1, 1, 1],
#        [0, 0, 0, 0, 0, 1, 1, 1, 1, 1],
#        [0, 0, 0, 0, 0, 0, 1, 1, 1, 1],
#        [0, 0, 0, 1, 1, 1, 1, 1, 1, 1],
#        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
#        [0, 1, 1, 1, 1, 1, 1, 1, 1, 1],
#        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
#        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
#        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], dtype=int8)

My attempt would be:

import numpy as np
ci_rate = np.random.normal(size=20)
ci_rate = (ci_rate - min(ci_rate)) /(max(ci_rate) - min(ci_rate)) - 0.7
ci_rate[ci_rate < 0] = 0
r = []
for i in range(100):
    t = np.random.binomial(1, ci_rate)
    r += [t.tolist()]
    ci_rate = [1 if j == 1 else i for i, j in zip(ci_rate, t)]


#output 

[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0],

I am suggesting geometric distribution, since it looks like you are trying to see the number of trials for first success.

I am comparing the usefulness of using the geomentric distribution in terms of computation time

EDIT:
%%timeit
ci_rate = np.random.uniform(0, 0.1, nb_years)
successful_trail = np.random.geometric(p=ci_rate)

ci_sim=np.zeros((nb_scenarios,nb_years))
for i in range(nb_years):
    ci_sim[i,successful_trail[i]:]=1

## 10000 loops, best of 3: 41.4 µs per loop

%%timeit
ci_rate = np.random.uniform(0, 0.1, nb_years)
res = np.maximum.accumulate(np.random.random((nb_scenarios, ci_rate.size))<ci_rate, axis=1).view(np.int8)

## 100 loops, best of 3: 2.97 ms per loop
Related