python random sample for samples larger than population

Viewed 1426

I have a list of integers which represent the number of applications submitted per day for a 60 day period. I need to randomly generate a list of 288 integers that sum to the number of applications per day. I have the following code:

import random as r

issued = [1000,200,344...]
def random_sum_to(n, num_terms = None):
    num_terms = (num_terms or r.randint(2, n)) - 1
    a = r.sample(range(1, n), num_terms) + [0, n]
    list.sort(a)
    return [a[i+1] - a[i] for i in range(len(a) - 1)]

for i in issued:
    print(random_sum_to(i,288))

Where issued is the list of integers that are the sum of the applications submitted per day. This code works great for numbers greater than 288 but crashes for numbers less than 288. Reading on here i saw that random.choice should be used but I cannot figure out how to implement it correctly. Looking at the results it looks like 0 is never printed so that is clearly a potential source for the problem. Any suggestions?

2 Answers

Frankly, all this zip stuff, list comprehension etc might look like a clever thing, but why don't you use Multinomial sampling?

One liner, really, and sum is automatically, by definition, equal to N

import numpy as np

t = np.random.multinomial(200, [1/288.]*288, size=1) # sample 288 numbers summed to 200
print(t)
print(sum(t[0]))

t = np.random.multinomial(1000, [1/288.]*288, size=1) # sample 288 numbers summed to 1000
print(t)
print(sum(t[0]))

Obviously, if you want more than n numbers to add to n, some of those numbers will have to be zero. Thus, you can not pick distinct numbers as the separators as you do with sample. Instead, just use choice to pick any values as separator, including duplicates, add 0 and n as the start and endpoint, and get the differences.

def random_sum_to(n, num_terms = None):
    num_terms = (num_terms or r.randint(2, n)) - 1
    a = sorted([r.randrange(n) for _ in range(num_terms)])
    return [y-x for x, y in zip([0]+a, a+[n])]

This way, a possible result for random_sum_to(200, 288) might look like this:

[0, 2, 1, 0, 0, 0, 1, 0, 2, 2, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 2, 0, 0, 2, 0, 2, 1, 0, 1, 0, 0, 1, 0, 1, 0, 2, 0, 0, 1, 0, 1, 0, 2, 0, 0, 1, 4, 0, 1, 2, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 2, 0, 2, 1, 0, 0, 1, 1, 2, 0, 0, 4, 1, 0, 1, 1, 0, 2, 0, 1, 0, 2, 0, 2, 0, 1, 2, 0, 1, 2, 1, 1, 0, 0, 1, 0, 1, 1, 1, 2, 1, 1, 2, 0, 0, 2, 0, 0, 0, 1, 0, 0, 1, 0, 1, 2, 2, 0, 0, 3, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 3, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 2, 1, 0, 0, 1, 1, 1, 0, 0, 2, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 3, 0, 0, 1, 0, 1, 0, 1, 2, 0, 0, 0, 1, 2, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 2, 0, 1, 0, 0, 0, 4, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 3, 1, 0, 2, 0, 0, 1, 0, 1, 2, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 3, 3, 0, 1, 1, 0, 1, 0, 2, 0, 1, 0, 2, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 2]
Related