Python 使用 numpy.random.normal 时如何指定上下限
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/18441779/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to specify upper and lower limits when using numpy.random.normal
提问by Catherine Georgia
I want to be able to pick values from a normal distribution that only ever fall between 0 and 1. In some cases I want to be able to basically just return a completely random distribution, and in other cases I want to return values that fall in the shape of a gaussian.
我希望能够从仅介于 0 和 1 之间的正态分布中选取值。在某些情况下,我希望能够基本上返回完全随机的分布,而在其他情况下,我希望返回落入高斯的形状。
At the moment I am using the following function:
目前我正在使用以下功能:
def blockedgauss(mu,sigma):
while True:
numb = random.gauss(mu,sigma)
if (numb > 0 and numb < 1):
break
return numb
It picks a value from a normal distribution, then discards it if it falls outside of the range 0 to 1, but I feel like there must be a better way of doing this.
它从正态分布中选取一个值,然后在它超出 0 到 1 的范围时将其丢弃,但我觉得必须有更好的方法来做到这一点。
采纳答案by unutbu
It sounds like you want a truncated normal distribution.
Using scipy, you could use scipy.stats.truncnorm
to generate random variates from such a distribution:
听起来您想要截断的正态分布。使用 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()
The top figure shows the truncated normal distribution, the lower figure shows the normal distribution with the same mean mu
and standard deviation sigma
.
上图显示截断的正态分布,下图显示具有相同均值mu
和标准差的正态分布sigma
。
回答by Greg Alushin
I came across this post while searching for a way to return a series of values sampled from a normal distribution truncated between zero and 1 (i.e. probabilities). To help anyone else who has the same problem, I just wanted to note that scipy.stats.truncnorm has the built-in capability ".rvs".
我在寻找一种方法来返回从零和 1 之间截断的正态分布(即概率)中采样的一系列值时遇到了这篇文章。为了帮助遇到同样问题的其他人,我只想指出 scipy.stats.truncnorm 具有内置功能“.rvs”。
So, if you wanted 100,000 samples with a mean of 0.5 and standard deviation of 0.1:
因此,如果您想要 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)
This gives a behavior very similar to numpy.random.normal, but within the bounds desired. Using the built-in will be substantially faster than looping to gather samples, especially for large values of N.
这给出了与 numpy.random.normal 非常相似的行为,但在所需的范围内。使用内置函数比循环收集样本要快得多,尤其是对于较大的 N 值。
回答by FlyFish
I have made an example script by the following. It shows how to use the APIs to implement the functions we wanted, such as generate samples with known parameters, how to compute CDF, PDF, etc. I also attach an image to show this.
我通过以下方式制作了一个示例脚本。它展示了如何使用 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')
回答by fr_andres
In case anybody wants a solution using numpy only, here is a simple implementation using a normalfunction and a clip(the MacGyver's approach):
如果有人想要只使用 numpy 的解决方案,这里有一个使用普通函数和剪辑的简单实现(MacGyver 的方法):
import numpy as np
def truncated_normal(mean, stddev, minval, maxval):
return np.clip(np.random.normal(mean, stddev), minval, maxval)
EDIT: do NOT use this!! this is how you shouldn't do it!!for instance,a = truncated_normal(np.zeros(10000), 1, -10, 10)
may look like it works, butb = truncated_normal(np.zeros(10000), 100, -1, 1)
will definitely not draw a truncated normal, as you can see in the following histogram:
编辑:不要使用这个!!这是你不应该这样做的方式!!例如,a = truncated_normal(np.zeros(10000), 1, -10, 10)
可能看起来像它的工作原理,但b = truncated_normal(np.zeros(10000), 100, -1, 1)
将肯定不会得出一个截断正常的,因为你可以在下面的柱状图看:
Sorry for that, hope nobody got hurt! I guess the lesson is, don't try to emulate MacGyver at coding...
Cheers,
Andres
对不起,希望没有人受伤!我想教训是,不要试图在编码时效仿 MacGyver...干杯,
安德烈斯