【问题标题】:Sample a truncated integer power law in Python?在 Python 中采样截断的整数幂律?
【发布时间】:2014-08-26 02:04:40
【问题描述】:

如果我想对截断的整数幂律进行采样,我可以在 Python 中使用什么函数?

也就是说,给定两个参数am,在[1,m) 范围内生成一个随机整数x,它遵循与1/x^a 成比例的分布。

我一直在搜索numpy.random,但没有找到这个分布。

【问题讨论】:

  • 为什么不使用内置的幂律分布进行拒绝抽样?

标签: python numpy random distribution


【解决方案1】:

AFAIK,NumPy 和 Scipy 都没有为你定义这个分布。但是,使用 SciPy 很容易使用 scipy.rv_discrete 定义您自己的离散分布函数:

import numpy as np
import scipy.stats as stats
import matplotlib.pyplot as plt

def truncated_power_law(a, m):
    x = np.arange(1, m+1, dtype='float')
    pmf = 1/x**a
    pmf /= pmf.sum()
    return stats.rv_discrete(values=(range(1, m+1), pmf))

a, m = 2, 10
d = truncated_power_law(a=a, m=m)

N = 10**4
sample = d.rvs(size=N)

plt.hist(sample, bins=np.arange(m)+0.5)
plt.show()

【讨论】:

  • 看起来你正在整合 pmf,就好像它是连续的一样,并取 1 到 2 之间的区域来得出 p(1),取 2 到 3 之间的区域来得出 p(2),等等。, 那正确吗?如果是这样,对于您的示例,我认为您需要模拟 Spinal Tap 并转到 11 以获得 p(10)。您的const 将通过在分母中添加(m+1)**k 进行调整。还是我误会了?
  • @pjs:我将 pdf 作为 continous 函数 1/x**a。所以在区间 [1,2]、[2,3] 等上没有积分。但是,我确实(手动)积分以找到 const_ppf 的公式,cdf 的倒数.我认为我是对的,但我可能是错的。 (我确实尝试过你的建议,但它会将域转移到[1, 11],所以如果我理解正确,那并没有通过基本的健全性检查。)顺便问一下,这里的 Spinal Tap 指的是什么?跨度>
  • Spinal Tap 是一部关于重金属乐队的模拟电影。他们的功放达到了 11,从而与其他乐队区分开来。
  • 我不是pythonista所以我不能直接检查你的结果,但是我对a,m = 2,10做了直接计算,p(1)应该是0.6452579827864142。这就是你得到的吗?
  • 我已经修改了计算离散 pmf 的答案。现在pmf[0] = p(1) = 0.64525798.
【解决方案2】:

我不使用 Python,因此我将尝试从算法上描述解决方案,而不是冒语法错误的风险。这是一个蛮力的离散反演。它应该很容易翻译成 Python。我假设数组的索引是从 0 开始的。

设置:

  1. 生成大小为m 的数组cdf,第一个条目为cdf[0] = 1,其余条目为cdf[i] = cdf[i-1] + 1/(i+1)**a

  2. 通过将 cdf[m-1] 划分为每个条目来缩放所有条目 - 现在它们实际上是 CDF 值。

用法:

  • 通过生成 Uniform(0,1) 和 搜索cdf[],直到找到大于您的条目 制服。返回索引 + 1 作为您的 x-value。

重复任意数量的x-values。

例如,对于a,m = 2,10,我直接计算概率为:

[0.6452579827864142, 0.16131449569660355, 0.07169533142071269, 0.04032862392415089, 0.02581031931145657, 0.017923832855178172, 0.013168530260947229, 0.010082155981037722, 0.007966147935634743, 0.006452579827864143]

CDF 是:

[0.6452579827864142, 0.8065724784830177, 0.8782678099037304, 0.9185964338278814, 0.944406753139338, 0.9623305859945162, 0.9754991162554634, 0.985581272236501, 0.9935474201721358, 1.0]

生成时,如果我得到 0.90 的统一结果,我将返回 x=4,因为 0.918... 是第一个大于我的统一的 CDF 条目。

如果您担心速度,您可以构建一个别名表,但由于几何衰减,通过数组的线性搜索提前终止的可能性非常高。例如,对于给定的示例,您将在几乎 2/3 的时间内在第一次查看时终止。

【讨论】:

  • Doh,我只花了两个小时(并阅读了您的答案)就意识到 OP 要求的是 离散 概率分布...
  • 这就是为什么我要问取范围区域来产生离散值。
【解决方案3】:

使用 numpy.random.zipf 并拒绝任何大于或等于 m 的样本

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-23
    • 1970-01-01
    • 2011-01-07
    相关资源
    最近更新 更多