numpy.random.uniform from a discontinuous set

Viewed 494

I want to generate a random number uniformly in the sets of the form (a,b)∪(c,d)∪...∪(e,f), where a < b < c ... and a > 0, and f < 1. Is this possible with the numpy.random.uniform function?

2 Answers

Note: This answer was written for the original version of the question, which asked for uniform samples from the set (0, 0.3) ∪ (0.7, 1).

There are many ways you could do this. Here's one that's very concise, but it depends on the particular form of the intervals you have given:

In [16]: rng = np.random.default_rng()                                                                                      

In [17]: n = 1000                                                                                                           

In [18]: x = rng.uniform(-0.3, 0.3, size=n) % 1  

x is the array of n samples.

The trick is that the samples are generated on the interval (-0.3, 0.3). Then by mod'ing the values with 1, the negative values "wrap around" to the interval (0.7, 1).

If you only need to pick it once you can use np.random.choice:

import numpy as np
a, b, c, d = 0, 0.3, 0.7, 1

# Specify relative probabilities
prob = np.array([b-a, d-c])
prob = prob/prob.sum() # Normalize to sum up to one

r = np.random.choice([np.random.uniform(a, b), np.random.uniform(c, d)],
                     p=prob)

r
0.9662186527199109

If you need to generate many values:

n=10
R = np.array([np.random.choice([np.random.uniform(a, b),np.random.uniform(c, d)], 
                               p=prob) 
              for _ in range(n)])

R
array([0.19130148, 0.24858629, 0.75106557, 0.11057559, 0.9276096 ,
       0.01849698, 0.89433504, 0.99455349, 0.10128313, 0.23325187])

We can see that adding the probability parameter yield the expected result:

a,b,c,d,e,f = 0, .1, .2,.25, .5, 1
prob = np.array([b-a, d-c, f-e])
prob = prob/prob.sum()
n=10_000
R = np.array([np.random.choice([np.random.uniform(a, b), 
                                np.random.uniform(c, d),
                                np.random.uniform(e, f)], 
                               p=prob) 
              for _ in range(n)])

print(prob)
array([0.15384615, 0.07692308, 0.76923077])

print(R[np.logical_and(R>a, R<b)].size/n, R[np.logical_and(R>c, R<d)].size/n, R[np.logical_and(R>e, R<f)].size/n)
0.1537 0.0709 0.7754
Related