Вот один из способов, которым вы можете это сделать.Он не полностью векторизован, но цикл Python превышает значения p
.Если длина вашего p
вектора (ов) не слишком велика, это может быть достаточно быстрым для вас.
Полиномиальное распределение реализовано с использованием повторных вызовов np.random.binomial
, который реализует передачу своих аргументов.
import numpy as np
def multinomial_rvs(n, p):
"""
Sample from the multinomial distribution with multiple p vectors.
* n must be a scalar.
* p must an n-dimensional numpy array, n >= 1. The last axis of p
holds the sequence of probabilities for a multinomial distribution.
The return value has the same shape as p.
"""
count = np.full(p.shape[:-1], n)
out = np.zeros(p.shape, dtype=int)
ps = p.cumsum(axis=-1)
# Conditional probabilities
with np.errstate(divide='ignore', invalid='ignore'):
condp = p / ps
condp[np.isnan(condp)] = 0.0
for i in range(p.shape[-1]-1, 0, -1):
binsample = np.random.binomial(count, condp[..., i])
out[..., i] = binsample
count -= binsample
out[..., 0] = count
return out
Вот пример, где «сетка» имеет форму (2, 3), а полиномиальное распределение является четырехмерным (то есть каждый p
вектор имеет длину 4).
In [182]: p = np.array([[[0.25, 0.25, 0.25, 0.25],
...: [0.01, 0.02, 0.03, 0.94],
...: [0.75, 0.15, 0.05, 0.05]],
...: [[0.01, 0.99, 0.00, 0.00],
...: [1.00, 0.00, 0.00, 0.00],
...: [0.00, 0.25, 0.25, 0.50]]])
In [183]: sample = multinomial_rvs(1000, p)
In [184]: sample
Out[184]:
array([[[ 249, 260, 233, 258],
[ 3, 21, 33, 943],
[ 766, 131, 55, 48]],
[[ 5, 995, 0, 0],
[1000, 0, 0, 0],
[ 0, 273, 243, 484]]])
In [185]: sample.sum(axis=-1)
Out[185]:
array([[1000, 1000, 1000],
[1000, 1000, 1000]])
В комментарии вы сказали: «Вектор p имеет вид: p = [p_s, (1-p_s) / 4, (1-p_s) / 4, (1-p_s) / 4»), (1-p_s) / 4], причем p_s варьируется от сайта к сайту. "Вот как вы можете использовать вышеуказанную функцию, учитывая массив, содержащий значения p_s
.
Сначала создайте некоторые данные для примера:
In [73]: p_s = np.random.beta(4, 2, size=(2, 3))
In [74]: p_s
Out[74]:
array([[0.61662208, 0.6072323 , 0.62208711],
[0.86848938, 0.58959038, 0.47565799]])
Создайте массив p
, содержащийполиномиальные вероятности по формуле p = [p_s, (1-p_s)/4, (1-p_s)/4, (1-p_s)/4, (1-p_s)/4]
:
In [75]: p = np.expand_dims(p_s, -1) * np.array([1, -0.25, -0.25, -0.25, -0.25]) + np.array([0, 0.25, 0.25, 0.25, 0.25])
In [76]: p
Out[76]:
array([[[0.61662208, 0.09584448, 0.09584448, 0.09584448, 0.09584448],
[0.6072323 , 0.09819192, 0.09819192, 0.09819192, 0.09819192],
[0.62208711, 0.09447822, 0.09447822, 0.09447822, 0.09447822]],
[[0.86848938, 0.03287765, 0.03287765, 0.03287765, 0.03287765],
[0.58959038, 0.1026024 , 0.1026024 , 0.1026024 , 0.1026024 ],
[0.47565799, 0.1310855 , 0.1310855 , 0.1310855 , 0.1310855 ]]])
Теперь сделайте то же самое, что и раньше, чтобы сгенерировать выборку (измените значение 1000 на то, что подходит для вашей задачи):
In [77]: sample = multinomial_rvs(1000, p)
In [78]: sample
Out[78]:
array([[[618, 92, 112, 88, 90],
[597, 104, 103, 101, 95],
[605, 100, 95, 98, 102]],
[[863, 32, 43, 27, 35],
[602, 107, 108, 94, 89],
[489, 130, 129, 129, 123]]])
In [79]: sample.sum(axis=-1)
Out[79]:
array([[1000, 1000, 1000],
[1000, 1000, 1000]])