42

我希望能够从仅介于 0 和 1 之间的正态分布中选择值。在某些情况下,我希望能够基本上只返回一个完全随机的分布,而在其他情况下,我想返回落在高斯的形状。

目前我正在使用以下功能:

def blockedgauss(mu,sigma):
    while True:
        numb = random.gauss(mu,sigma)
        if (numb > 0 and numb < 1):
            break
    return numb

它从正态分布中选择一个值,然后在它超出 0 到 1 的范围时将其丢弃,但我觉得必须有更好的方法来做到这一点。

4

7 回答 7

51

听起来您想要截断的正态分布。使用 scipy,您可以scipy.stats.truncnorm从这样的分布中生成随机变量:

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

lower, upper = 3.5, 6
mu, sigma = 5, 0.7
X = stats.truncnorm(
    (lower - mu) / sigma, (upper - mu) / sigma, loc=mu, scale=sigma)
N = stats.norm(loc=mu, scale=sigma)

fig, ax = plt.subplots(2, sharex=True)
ax[0].hist(X.rvs(10000), normed=True)
ax[1].hist(N.rvs(10000), normed=True)
plt.show()

在此处输入图像描述

上图显示截断的正态分布,下图显示均值mu和标准差相同的正态分布sigma

于 2013-08-26T12:57:27.503 回答
18

我在寻找一种方法来返回从在 0 和 1 之间截断的正态分布采样的一系列值(即概率)时遇到了这篇文章。为了帮助遇到同样问题的其他人,我只想指出 scipy.stats.truncnorm 具有内置功能“.rvs”。

因此,如果您想要 100,000 个样本,平均值为 0.5,标准差为 0.1:

import scipy.stats
lower = 0
upper = 1
mu = 0.5
sigma = 0.1
N = 100000

samples = scipy.stats.truncnorm.rvs(
          (lower-mu)/sigma,(upper-mu)/sigma,loc=mu,scale=sigma,size=N)

这给出了与 numpy.random.normal 非常相似的行为,但在所需的范围内。使用内置将比循环收集样本快得多,尤其是对于较大的 N 值。

于 2015-01-18T19:19:08.513 回答
8

如果有人想要仅使用 numpy 的解决方案,这里有一个使用普通函数和剪辑的简单实现(MacGyver 的方法):

    import numpy as np
    def truncated_normal(mean, stddev, minval, maxval):
        return np.clip(np.random.normal(mean, stddev), minval, maxval)

编辑:不要使用这个!!这是你不应该这样做的!例如,
a = truncated_normal(np.zeros(10000), 1, -10, 10)
它可能看起来有效,但
b = truncated_normal(np.zeros(10000), 100, -1, 1)
绝对不会绘制截断的 normal,如下面的直方图所示:

在此处输入图像描述

对不起,希望没有人受伤!我想教训是,不要试图在编码方面模仿 MacGyver……干杯,
安德烈斯

于 2017-06-17T09:38:31.333 回答
6

我通过以下方式制作了一个示例脚本。它展示了如何使用 API 来实现我们想要的功能,例如生成具有已知参数的样本,如何计算 CDF、PDF 等。我还附上一张图片来展示这一点。

#load libraries   
import scipy.stats as stats

#lower, upper, mu, and sigma are four parameters
lower, upper = 0.5, 1
mu, sigma = 0.6, 0.1

#instantiate an object X using the above four parameters,
X = stats.truncnorm((lower - mu) / sigma, (upper - mu) / sigma, loc=mu, scale=sigma)

#generate 1000 sample data
samples = X.rvs(1000)

#compute the PDF of the sample data
pdf_probs = stats.truncnorm.pdf(samples, (lower-mu)/sigma, (upper-mu)/sigma, mu, sigma)

#compute the CDF of the sample data
cdf_probs = stas.truncnorm.cdf(samples, (lower-mu)/sigma, (upper-mu)/sigma, mu, sigma)

#make a histogram for the samples
plt.hist(samples, bins= 50,normed=True,alpha=0.3,label='histogram');

#plot the PDF curves 
plt.plot(samples[samples.argsort()],pdf_probs[samples.argsort()],linewidth=2.3,label='PDF curve')

#plot CDF curve        
plt.plot(samples[samples.argsort()],cdf_probs[samples.argsort()],linewidth=2.3,label='CDF curve')


#legend
plt.legend(loc='best')

在此处输入图像描述

于 2016-05-20T05:13:56.850 回答
0

我开发了一个简单的函数,用于使用 numpy.random.normal 和一些额外的代码创建一个范围内的值列表。

def truncnormal(meanv, sd, minv, maxv, n):
    finallist = []
    initiallist = []
    while len(finallist) < n:
        initiallist = list(np.random.normal(meanv, sd, n))
        initiallist.sort()
        indexmin = 0
        indexmax = 0
        for item in initiallist:
            if item < minv:
                indexmin = indexmin + 1
            else:
                break
        for item in initiallist[::-1]:
            if item > maxv:
                indexmax = indexmax + 1
            else:
                break
        indexmax = -indexmax
        finallist = finallist + initiallist[indexmin:indexmax]
    shuffle(finallist)
    finallist = finallist[:n] 
    print(len(finallist), min(finallist), max(finallist))

truncnormal(10, 3, 8, 11, 10000)
于 2022-02-24T03:25:54.863 回答
0

实际上,您可以规范化数据,然后将其传输到您需要的范围。不好意思第一次用,不知道怎么直接显示图片 功能显示

于 2021-06-18T10:32:14.243 回答
0

我已经使用 numpy 测试了一些解决方案。通过试错法,我发现± variation除以 3 是标准差的一个很好的猜测。

以下是一些示例:

基础知识

import numpy as np
import matplotlib.pyplot as plt

val_min = 1000
val_max = 2000
variation = (val_max - val_min)/2
std_dev = variation/3
mean = (val_max + val_min)/2
dist_normal = np.random.normal(mean, std_dev,  1000)
print('Normal distribution\n\tMin: {0:.2f}, Max: {1:.2f}'
      .format(dist_normal.min(), dist_normal.max()))
plt.hist(dist_normal, bins=30)
plt.show()

直方图 1

比较案例

import numpy as np
import matplotlib.pyplot as plt

val_min = 1400
val_max = 2800
variation = (val_max - val_min)/2
std_dev = variation/3
mean = (val_max + val_min)/2
fig, ax = plt.subplots(3, 3)
plt.suptitle("Histogram examples by Davidson Lima (github.com/davidsonlima)", 
             fontweight='bold')
i = 0
j = 0
pos = 1
while (i < 3):
    while (j < 3):
        dist_normal = np.random.normal(mean, std_dev,  1000)
        max_min = 'Min: {0:.2f}, Max: {1:.2f}'.format(dist_normal.min(), dist_normal.max())
        ax[i, j].hist(dist_normal, bins=30, label='Dist' + str(pos))
        ax[i, j].set_title('Normal distribution ' + str(pos))
        ax[i, j].legend()
        ax[i, j].text(mean, 0, max_min, horizontalalignment='center', color='white',
                      bbox={'facecolor': 'red', 'alpha': 0.5})
        print('Normal distribution {0}\n\tMin: {1:.2f}, Max: {2:.2f}'
              .format(pos, dist_normal.min(), dist_normal.max()))
        j += 1
        pos += 1
    j = 0
    i += 1
plt.show()

直方图 2

如果有人对 numpy 有更好的方法,请在下面发表评论。

于 2021-02-16T20:27:58.347 回答