2013-08-26 8 views
15

IOK quindi voglio essere in grado di prelevare valori da una distribuzione normale che solo mai cade tra 0 e 1. In alcuni casi, voglio essere in grado di basta restituire una distribuzione completamente casuale, e in altri casi voglio restituire valori che cadono nella forma di un gaussiano.Come specificare i limiti superiore e inferiore quando si usa numpy.random.normal

Al momento sto usando la seguente funzione:

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

Si preleva un valore da una distribuzione normale, quindi scarta se cade al di fuori della gamma 0 a 1, ma ritengo che ci deve essere un modo migliore per farlo.

+0

Se "blocco" valori < 0 and > 1, sarà ancora una distribuzione gaussiana? –

+0

non sarà una distribuzione gaussiana, ma in alcuni casi non voglio una distribuzione gaussiana. Voglio restituire una distribuzione che è sintonizzabile tra una distribuzione casuale (raccolta da un gaussiano molto ampio), qualcosa di molto vicino a una funzione delta (dove il gaussiano diventa molto stretto) –

risposta

21

Sembra che tu voglia un truncated normal distribution. Utilizzando SciPy, è possibile utilizzare scipy.stats.truncnorm per generare variabili casuali da tale distribuzione:

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

La figura superiore mostra la distribuzione normale troncata, il valore inferiore mostra la distribuzione normale con la stessa media mu e deviazione standard sigma.

+0

grazie perfetto. –

5

Mi sono imbattuto in questo post durante la ricerca di un modo per restituire una serie di valori campionati da una normale distribuzione troncata tra zero e 1 (cioè le probabilità). Per aiutare chiunque abbia lo stesso problema, volevo solo sottolineare che scipy.stats.truncnorm ha la capacità incorporata ".rvs".

Quindi, se si voleva 100.000 campioni con una media di 0,5 e la deviazione standard di 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) 

Questo dà un comportamento molto simile a numpy.random.normal, ma entro i limiti desiderato. L'utilizzo del built-in sarà sostanzialmente più veloce del ciclo per raccogliere campioni, in particolare per valori elevati di N.

3

Ho creato uno script di esempio dal seguente. Mostra come utilizzare le API per implementare le funzioni che volevamo, come generare campioni con parametri noti, come calcolare CDF, PDF, ecc. Allego anche un'immagine per mostrare questo.

#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') 

enter image description here

4

Nel caso in cui nessuno vuole una soluzione che utilizza NumPy solo, ecco un semplice implementazione mediante una funzione normal e (approccio della MacGyver) clip:

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

EDIT: NON usare questo !! questo è come non dovresti farlo !! per esempio,
a = truncated_normal(np.zeros(10000), 1, -10, 10)
può apparire come funziona, ma
b = truncated_normal(np.zeros(10000), 100, -1, 1)
sarà sicuramente non disegnare un tronco normale, come si può vedere nella seguente istogramma:

enter image description here

Spiacente per questo, spero che nessuno si sia fatto male!Credo che la lezione è, non cercare di emulare MacGyver alla codifica ... Cheers,
Andres

Problemi correlati