从具有离散分布的大型集合中抽取小样本

时间:2018-04-18 15:10:42

标签: python performance numpy random probability

我有两个相同大小的列表,我们称之为elementsweights。我想选择elements列表的一个元素,其中weights给出了离散概率分布weight[i]对应于选择elements[i]的概率。 elements永远不会更改,但在每次采样后,weights都会更改(仅限值,而不是大小)。

我需要一种高效方式来执行大型列表。

我在Python中使用numpy.random.choice(elements, p=weights)实现了一个实现,但是从一组k中获取大小为n的样本,其中k << n的效率非常低。欢迎使用任何语言实现,但我主要使用Python。

(这用于使用 networkx的社交网络模拟。我有一个加权图和一个节点i,我想从{{选择一个节点j 1}}邻居,其中每个节点的概率与i和给定节点之间边缘的权重成比例。如果我将非邻居的概率设置为0,我不必生成每次邻居列表,我只需要一个所有节点的列表。)

它将像这样使用:

i

2 个答案:

答案 0 :(得分:0)

我使用了以下内容。使用cumsum将权重形成为累积分布函数,然后从反向cdf中进行采样。

wcs = weights.cumsum()
wcs = wcs / wcs[-1] # non-decreasing in (0:1]
u = np.random.uniform()
chosen = weights[(u < wcs).argmax()]  # the first index above u

答案 1 :(得分:0)

@MarkBorgerding的方法很好,但可以改进:

getNumber

此外,它最终取决于实际数字,但不是将非邻居的概率归零,否则删除这些概率可能更有效;见下面的Timings第2部分。

时序:

W = weights.cumsum() W.searchsorted(np.random.uniform(0, W[-1], nsamples)) 选项,单个样本:

1000000

>>> from timeit import timeit >>> kwds = dict(globals=globals(), number=100) >>> weights = np.random.random(1000000) >>> >>> timeit("np.random.choice(1000000, 1, p=weights/weights.sum())", **kwds) 1.606048938119784 >>> timeit("W = weights.cumsum(); W/=W[-1]; (np.random.uniform()<W).argmax()", **kwds) 0.6634919850621372 >>> timeit("W = weights.cumsum(); W.searchsorted(np.random.uniform(0, W[-1]))", **kwds) 0.30993065400980413 个选项,1000000个样本:

10

计时第2部分:

>>> timeit("np.random.choice(1000000, 10, p=weights/weights.sum())", **kwds)
1.606177378911525
>>> timeit("W = weights.cumsum(); W/=W[-1]; (np.random.uniform(0, 1, (10, 1))<W).argmax(axis=1)", **kwds)
1.4421172500588
>>> timeit("W = weights.cumsum(); W.searchsorted(np.random.uniform(0, W[-1], 10))", **kwds)
0.3154504559934139