如何在numpy中获得指定范围内的正态分布?

49

在机器学习任务中,我们需要获取一组符合正态分布且有边界的随机数。我们可以使用np.random.normal()获取一个符合正态分布的随机数,但是它并没有提供任何边界参数。我想知道如何实现这个功能?


6
按定义,正态分布的随机样本不应该是无界的吗? - Tom
3
@Tom 在某些情况下,进行边界限制是有用的。例如,如果我想要对图像裁剪的中心位置进行抖动,但同时确保它仍在源图像的像素尺寸内。 - Addison Klinke
4个回答

80

truncnorm的参数化方法很复杂,因此这里提供了一个将参数化方法转换为更直观形式的函数:

from scipy.stats import truncnorm

def get_truncated_normal(mean=0, sd=1, low=0, upp=10):
    return truncnorm(
        (low - mean) / sd, (upp - mean) / sd, loc=mean, scale=sd)

如何使用它?

  1. 使用参数 平均数(mean)标准差(standard deviation)截断范围(truncation range) 来实例化生成器:

  2. >>> X = get_truncated_normal(mean=8, sd=2, low=1, upp=10)
    
    然后,您可以使用 X 生成一个值:
    >>> X.rvs()
    6.0491227353928894
    
    或者,一个包含N个生成的值的numpy数组:
    >>> X.rvs(10)
    array([ 7.70231607,  6.7005871 ,  7.15203887,  6.06768994,  7.25153472,
            5.41384242,  7.75200702,  5.5725888 ,  7.38512757,  7.47567455])
    

一个可视化例子

下面是三个不同的截断正态分布的图形:

X1 = get_truncated_normal(mean=2, sd=1, low=1, upp=10)
X2 = get_truncated_normal(mean=5.5, sd=1, low=1, upp=10)
X3 = get_truncated_normal(mean=8, sd=1, low=1, upp=10)

import matplotlib.pyplot as plt
fig, ax = plt.subplots(3, sharex=True)
ax[0].hist(X1.rvs(10000), normed=True)
ax[1].hist(X2.rvs(10000), normed=True)
ax[2].hist(X3.rvs(10000), normed=True)
plt.show()

enter image description here


2
值得注意的是,如果在函数内部立即使用get_truncated_normal.rvs()而不是在外部调用它,该函数会更快。当然,这只有在您需要随机抽样时才有帮助。 - KenHBS
这个并不总是百分之百地正常工作。使用 low=0,upp=1,有时会给你负数。 - maxbear123

20
如果您正在寻找截断正态分布,SciPy有一个名为truncnorm的函数。
这个分布的标准形式是在范围[a, b]内截断的标准正态分布 - 注意a和b是在标准正态分布的定义域上定义的。要将剪辑值转换为特定均值和标准偏差,请使用:
a,b =(myclip_a - my_mean)/ my_std,(myclip_b - my_mean)/ my_std
truncnorm将a和b作为形状参数。
>>> from scipy.stats import truncnorm
>>> truncnorm(a=-2/3., b=2/3., scale=3).rvs(size=10)
array([-1.83136675,  0.77599978, -0.01276925,  1.87043384,  1.25024188,
        0.59336279, -0.39343176,  1.9449987 , -1.97674358, -0.31944247])

上面的示例受到-2和2的限制,并返回10个随机变量(使用.rvs()方法)。
>>> min(truncnorm(a=-2/3., b=2/3., scale=3).rvs(size=10000))
-1.9996074381484044
>>> max(truncnorm(a=-2/3., b=2/3., scale=3).rvs(size=10000))
1.9998486576228549

这是一个-6到6的直方图绘图:

enter image description here


为什么你不使用truncnorm(a=-2, b=2, scale=1)? - maple
5
为了让读者明确,a和b是形状参数,否则读者可能会尝试使用比例系数不为1的-2、2,然后得到超出[-2, 2]范围的随机值。 - bakkal

1

0

您可以将目标范围(按照惯例)细分为相等的分区,然后计算每个区域的积分,然后根据表面在每个分区上调用均匀方法。

这是Python实现的:

quad_vec(eval('scipy.stats.norm.pdf'), 1, 4,points=[0.5,2.5,3,4],full_output=True)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接