5

I want to generate an integer random number with a probability distribution function given as a list. For example if pdf=[3,2,1] then I like rndWDist(pdf) to return 0,1, and 2, with probabilities of 3/6, 2/6, and 1/6. I wrote my own function for that since I couldn't find it in the random module.

def randintWDist(pdf):
    cdf=[]
    for x in pdf:
        if cdf:
            cdf.append(cdf[-1]+x)
        else:
            cdf.append(x)
    a=random.randint(1,cdf[-1])
    i=0
    while cdf[i]<a:
        i=i+1
    return i

Is there any shorter method to achieve the same result?

Gabriel
  • 40,504
  • 73
  • 230
  • 404
Hooman
  • 147
  • 1
  • 2
  • 8
  • 1
    possible duplicate of [Generate random numbers with a given (numerical) distribution](http://stackoverflow.com/questions/4265988/generate-random-numbers-with-a-given-numerical-distribution) – juankysmith Sep 04 '14 at 14:26

3 Answers3

6

This is a duplicate question: Generate random numbers with a given (numerical) distribution

As the first answer there suggest, you might want to use scipy.stats.rv_discrete.

You might use it like that:

from scipy.stats import rv_discrete
numbers = (1,2,3)
distribution = (1./6, 2./6, 3./6)
random_variable = rv_discrete(values=(numbers,distribution))
random_variable.rvs(size=10)

This returns a numpy array with 10 random values.

Community
  • 1
  • 1
Meppel
  • 106
  • 6
1

Given the format of your input, you could do:

def randint_with_dist(pdf):
    choices = []
    for index, value in enumerate(pdf):
        choices.extend(index for _ in range(value))
    return random.choice(choices)

As the same list will be used every time the same pdf is passed, you could consider caching the list for greater efficiency (at the cost of space):

def randint_with_dist(pdf, choices={}):
    pdf = tuple(pdf)
    if pdf not in choices:
        choices[pdf] = []
        for index, value in enumerate(pdf):
            choices[pdf].extend(index for _ in range(value))
    return random.choice(choices[pdf])
jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
1

Using numpy (version 1.7 or newer), you could also use np.random.choice:

In [27]: import numpy as np

In [28]: distribution = (1./6, 2./6, 3./6)

In [29]: np.random.choice(np.arange(len(distribution)), p=distribution)
Out[29]: 0

In [30]: np.random.choice(np.arange(len(distribution)), p=distribution, size=10)
Out[30]: array([2, 1, 1, 2, 2, 0, 1, 0, 1, 0])
unutbu
  • 842,883
  • 184
  • 1,785
  • 1,677