首页 > 解决方案 > 如何对具有不同分布的少数数字进行抽样?

问题描述

假设我有自己的自定义分发 numpy 向量,称为p.

那么p满足以下条件:

np.ndim(p) == 1 & np.sum(p) == 1 & np.all(p >= 0)

使用该向量,我可以轻松地采样一个[0, p.shape)数字np.random.choice(np.arange(len(p)), p=p)

在我有很多这样p的 s 的情况下,我有一个矩阵(暗 2)P满足:

np.sum(P[:,i]) == 1   # for all i in P.shape[1]
np.all(P >= 0)

然后我希望以概率 PP.shape[1]对 0 到范围内的数字进行采样。P.shape[0]

例如下面的代码:

P = np.array([[0.2, 0.3],
              [0.5, 0.7],
              [0.3, 0]])
x = np.random.choice(np.arange(P.shape[0], P[:,0]))
y = np.random.choice(np.arange(P.shape[0], P[:,1]))

将产生我的意志(x=00.2x=10.5x=20.3y=00.3y=10.7)。

就我而言P,有很多列,我希望一次采样。

当然,我可以在 for 循环中执行此操作,例如:

random_values = np.empty(P.shape[1])
arange_arr = np.arange(P.shape[0])
for i in range(P.shape[1]):
    random_values[i] = np.random.choice(arange_arr, p=P[:,i])

试图找到一些优雅的方式来做到这一点。

标签: pythonnumpyrandomscipy

解决方案


你可以这样做:

P = np.array([[0.2, 0.3],
              [0.5, 0.7],
              [0.3, 0]])
P_upper = np.cumsum(P, axis=0)
P_lower = np.concatenate((np.zeros((1, P.shape[1])), P_upper[:-1, :]), axis=0)

这将创建一组您可以数字化的 bin。现在生成 0 到 1 之间的随机数:

r = np.random.rand(10, P.shape[1])

有几种方法可以将数据分配到正确的 bin。快速且相对低效的方法是使用布尔掩码:

mask = (r[None, ...] >= P_lower[:, None, :]) & (r[None, ...] < P_upper[:, None, :])
result = np.argmax(mask, axis=0)

一种更有效但更复杂的方法是为每列添加一个偏移量,并将np.digitizeornp.searchsorted应用于结果:

offset = np.arange(P.shape[1])
ind = np.searchsorted((P_upper + offset).ravel('F'), (r + offset).ravel('F')).reshape(r.shape, order='F')
result = ind - offset * P.shape[0]

TL;博士

def multi_sample(p, n):
    ps = np.cumsum(p, axis=0)
    r = np.random.rand(n, ps.shape[1])
    offset = np.arange(P.shape[1])
    ind = np.searchsorted((P_upper + offset).ravel('F'), (r + offset).ravel('F')).reshape(r.shape, order='F')
    return ind - offset * P.shape[0]

推荐阅读