【问题标题】:Draw small sample from large set with discrete distribution efficiently有效地从离散分布的大集合中抽取小样本
【发布时间】:2018-09-28 21:42:48
【问题描述】:

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

我需要一种高效的方法来处理大型列表。

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

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

会这样使用:

elements = [...]
weights = [...]

for(...):
    element = sample(elements, weights)
    *Some calculation with element and changing the values of weights*

【问题讨论】:

  • 权重没有标准化,对吧?
  • 默认没有。我用sum() 将它们标准化,然后是列表理解。
  • 而且很多都是零,对吧?

标签: python performance numpy random probability


【解决方案1】:

我用过类似下面的东西。使用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

【讨论】:

    【解决方案2】:

    @MarkBorgerding 的方法不错,但可以改进:

    W = weights.cumsum()
    W.searchsorted(np.random.uniform(0, W[-1], nsamples))
    

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

    时间安排:

    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 示例:

    >>> 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
    

    时间安排第 2 部分:

    # assume we connect to 10% of the nodes
    >>> neighbors = np.random.random(1000000) < 0.1
    >>> 
    # zeroing non connected weights
    >>> timeit("W = np.where(neighbors, weights, 0).cumsum(); W.searchsorted(np.random.uniform(0, W[-1], 10))", **kwds)
    0.553992060944438
    # outright removing them
    >>> timeit("nbidx, = np.where(neighbors); W = weights[nbidx].cumsum(); nbidx[W.searchsorted(np.random.uniform(0, W[-1], 10))]", **kwds)
    0.32569816312752664
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-03-04
      • 2019-02-04
      • 1970-01-01
      • 2022-11-23
      • 2020-11-01
      • 1970-01-01
      • 2012-04-14
      相关资源
      最近更新 更多