【问题标题】:How to best get a sample from a truncated normal distribution?如何最好地从截断的正态分布中获取样本?
【发布时间】:2020-10-30 01:36:17
【问题描述】:

我已经进行了一些搜索,但似乎无法找到从截断的正态分布中采样的合理方法。

没有截断我在做:

samples = [np.random.normal(loc=x,scale=d) for (x,d) in zip(X,D)]

XD 是浮点数列表。

目前我正在执行截断:

def truncnorm(loc,scale,bounds):
  s = np.random.normal(loc,scale)
  if s > bounds[1]:
    return bounds[1]
  elif s < bounds[0]:
    return bounds[0]
  return s

samples = [truncnorm(loc=x,scale=d,bounds=b) for (x,d,b) in zip(X,D,bounds)]

bounds 是一个元组列表(min,max)

这种方法感觉有点别扭,不知道有没有更好的方法?

【问题讨论】:

  • 嗯,您所展示的并不是通常所说的截断正态分布。相反,它是截断法线与离散分布的混合,该分布在边界 [0] 处具有点质量,在边界 [1] 处具有另一个点质量。要获得截断的法线,您需要if s &gt; bounds[1]: return truncnorm(loc, scale, bounds),即,如果您掉到外面,请再试一次。根据界限,您可能必须尝试多次。如果大部分质量在界限之间,再试一次就可以了。但如果边界可能排除大部分质量,您可能需要一种不同的方法。
  • 另一种方法是反转 cdf,cdf 是未截断 cdf 的缩放版本(缩放因子等于边界之间的质量)。这将是一些涉及 erf 的表达式。我不知道逆 erf 是否是 numpy/scipy 中可用的函数之一,但如果不是,你可以通过二分法或类似的简单方法来反转它。
  • 你见过scipy.stats.truncnorm吗?它与您实现的不完全相同,因为(正如@RobertDodier 指出的那样),您的分布在边界上有点质量。通常的truncated normal distribution 在边界处没有那些点质量。
  • @WarrenWeckesser 我查看了 scipy 函数,但不知道如何设置范围。

标签: python numpy scipy


【解决方案1】:

为超出边界的样本返回边界值,将导致太多样本落在边界上。这并不代表实际分布。边界上的值需要被拒绝并替换为新样本。这样的代码可能是:

def test_truncnorm(loc, scale, bounds):
    while True:
        s = np.random.normal(loc, scale)
        if bounds[0] <= s <= bounds[1]:
            break
    return s

在狭窄的范围内,这可能会非常慢。 Scipy 的truncnorm 可以更有效地处理此类情况。有点令人惊讶的是,边界以标准法线的函数表示,因此您的调用将是:

s = scipy.stats.truncnorm.rvs((bounds[0]-loc)/scale, (bounds[1]-loc)/scale, loc=loc, scale=scale)

请注意,当使用 numpy 的 vectorization and broadcasting 时,scipy 的工作速度要快得多。一旦你习惯了这个符号,它看起来也更容易写和读。所有样本都可以一次性计算出来:

X = np.array(X)
D = np.array(D)
bounds = np.array(bounds)
samples = scipy.stats.truncnorm.rvs((bounds[:, 0] - X) / D, (bounds[:, 1] - X) / D, loc=X, scale=D)

【讨论】:

    猜你喜欢
    • 2019-12-20
    • 1970-01-01
    • 1970-01-01
    • 2013-08-08
    • 2020-08-07
    • 1970-01-01
    • 2017-05-10
    • 2020-02-13
    • 2012-12-11
    相关资源
    最近更新 更多