[Numpy-discussion] Multivariate hypergeometric distribution?
josef.pktd at gmail.com
josef.pktd at gmail.com
Mon Jul 2 21:35:14 EDT 2012
On Mon, Jul 2, 2012 at 8:08 PM, <josef.pktd at gmail.com> wrote:
> On Mon, Jul 2, 2012 at 4:16 PM, Fernando Perez <fperez.net at gmail.com> wrote:
>> Hi all,
>>
>> in recent work with a colleague, the need came up for a multivariate
>> hypergeometric sampler; I had a look in the numpy code and saw we have
>> the bivariate version, but not the multivariate one.
>>
>> I had a look at the code in scipy.stats.distributions, and it doesn't
>> look too difficult to add a proper multivariate hypergeometric by
>> extending the bivariate code, with one important caveat: the hard part
>> is the implementation of the actual discrete hypergeometric sampler,
>> which lives inside of numpy/random/mtrand/distributions.c:
>>
>> https://github.com/numpy/numpy/blob/master/numpy/random/mtrand/distributions.c#L743
>>
>> That code is hand-written C, and it only works for the bivariate case
>> right now. It doesn't look terribly difficult to extend, but it will
>> certainly take a bit of care and testing to ensure all edge cases are
>> handled correctly.
>
> My only foray into this
>
> http://projects.scipy.org/numpy/ticket/921
> http://projects.scipy.org/numpy/ticket/923
>
> This looks difficult to add without a good reference and clear
> description of the algorithm.
>
>>
>> Does anyone happen to have that implemented lying around, in a form
>> that would be easy to merge to add this capability to numpy?
>
> not me, I have never even heard of multivariate hypergeometric distribution.
>
>
> maybe http://hal.inria.fr/docs/00/11/00/56/PDF/perm.pdf p.11
> with some properties http://www.math.uah.edu/stat/urn/MultiHypergeometric.html
>
> I've seen one other algorithm, that seems to need N (number of draws
> in hypergeom) random variables for one multivariate hypergeometric
> random draw, which seems slow to me.
>
> But maybe someone has it lying around.
Now I have a pure num/sci/python version around.
A bit more than an hour, so no guarantees, but freq and pmf look close enough.
Josef
>
> Josef
>
>>
>> Thanks,
>>
>> f
>> _______________________________________________
>> NumPy-Discussion mailing list
>> NumPy-Discussion at scipy.org
>> http://mail.scipy.org/mailman/listinfo/numpy-discussion
-------------- next part --------------
# -*- coding: utf-8 -*-
"""
Created on Mon Jul 02 20:23:08 2012
Author: Josef Perktold
"""
import numpy as np
n_balls = [5, 3, 4]
n_sample = 5
size = 100000
p = len(n_balls)
n_all = sum(n_balls)
rvs = np.zeros((size, p), int)
n_bad = n_all
n_remain = n_sample * np.ones(size, int)
for ii in range(p-1):
n_good = n_balls[ii]
n_bad = n_bad - n_good
rvs_ii = rvs[:,ii]
mask = n_remain >= 1
need = mask.sum()
rvs_ii[mask] = np.random.hypergeometric(n_good, n_bad, n_remain[mask], size=need)
rvs[:,ii] = rvs_ii
n_remain = np.maximum(n_remain - rvs_ii, 0)
rvs[:, -1] = n_sample - rvs.sum(1)
#print rvs
print rvs.mean(0) * n_all / n_sample
u, idx = np.unique(rvs.view([('', int)]*3), return_inverse=True)
u_arr = u.view(int).reshape(len(u), -1)
count = np.bincount(idx)
freq = count * 1. / len(idx)
from scipy.misc import comb
def pmf(x, n_balls):
x = np.asarray(x)
n_balls = np.asarray(n_balls)
#p = len(n_balls)
ret = np.product(comb(n_balls, x)) / comb(n_balls.sum(), x.sum())
return ret
print
print freq
for x,fr in zip(u_arr, freq):
th = pmf(x, n_balls)
print x, np.round(th, 5), fr, np.round(fr - th, 10)
More information about the NumPy-Discussion
mailing list