Как указать верхний и нижний пределы при использовании numpy.random.normal

IOK, поэтому я хочу иметь возможность выбирать значения из нормального распределения, которое когда-либо падает между 0 и 1. В некоторых случаях я хочу, чтобы в принципе просто возвращал полностью случайное распределение, а в других случаях я хочу возвращающие значения, которые попадают в форму гауссова.

В настоящее время я использую следующую функцию:

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

Он выбирает значение из нормального распределения, затем отбрасывает его, если он выходит за пределы диапазона от 0 до 1, но я чувствую, что должен быть лучший способ сделать это.

Ответы

Ответ 1

Похоже, вы хотите усеченное нормальное распространение. Используя 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()

enter image description here

Верхний рисунок показывает усеченное нормальное распределение, нижняя цифра показывает нормальное распределение с тем же средним значением mu и стандартным отклонением sigma.

Ответ 2

Я столкнулся с этим сообщением при поиске способа вернуть ряд значений, выбранных из нормального распределения, усеченного между нулем и 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.

Ответ 3

Если кто-то хочет решение только с использованием numpy, вот простая реализация, использующая функцию normal и clip (подход 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)
будет определенно не нарисовать усеченный нормальный, как вы можете видеть на следующей гистограмме:

введите описание изображения здесь

Извините за это, надеюсь, никто не пострадал! Думаю, урок: не пытайтесь подражать MacGyver при кодировании... Cheers,
Andres

Ответ 4

Я сделал пример script следующим. Он показывает, как использовать 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')

введите описание изображения здесь