Reputation: 6302
I want to create a normal distributed array with numpy.random.normal that only consists of positive values. For example the following illustrates that it sometimes gives back negative values and sometimes positive. How can I modify it so it will only gives back positive values?
>>> import numpy
>>> numpy.random.normal(10,8,3)
array([ -4.98781629, 20.12995344, 4.7284051 ])
>>> numpy.random.normal(10,8,3)
array([ 17.71918829, 15.97617052, 1.2328115 ])
>>>
I guess I could solve it somehow like this:
myList = numpy.random.normal(10,8,3)
while item in myList <0:
# run again until all items are positive values
myList = numpy.random.normal(10,8,3)
Upvotes: 18
Views: 41751
Reputation: 1
arr=np.random.normal(0,1,10)
arr[gdp_cap<0]=-arr[gdp_cap<0] #Just invert the elements less than 0
print(gdp_cap)
Upvotes: 0
Reputation: 222
You could use high loc with low scale:
np.random.normal(100, 10, 10) /100
[0.96568643 0.92123722 0.83242272 0.82323367 1.07532713 0.90125736
0.91226052 0.90631754 1.08473303 0.94115643]
Upvotes: 0
Reputation: 583
The question is reasonable. For motivation, consider simulations of biological cells. The distribution of the count of a type of molecule in a cell can be approximated by the normal distribution, but must be non-negative to be physically meaningful.
My whole-simulator uses this method to sample the initial distribution of a molecule's count:
def non_neg_normal_sample(random_state, mean, std, max_iters=1000):
""" Obtain a non-negative sample from a normal distribution
The distribution returned is normal for 0 <= x, and 0 for x < 0
Args:
random_state (:obj:`numpy.random.RandomState`): a random state
mean (:obj:`float`): mean of the normal dist. to sample
std (:obj:`float`): std of the normal dist. to sample
max_iters (:obj:`int`, optional): maximum number of draws of the true normal distribution
Returns:
:obj:`float`: a normal sample that is not negative
Raises:
:obj:`ValueError`: if taking `max_iters` normal sample does not obtain one that is not negative
"""
iter = 0
while True:
sample = random_state.normal(mean, std)
iter += 1
if 0 <= sample:
return sample
if max_iters <= iter:
raise ValueError(f"{iter} draws of a normal dist. with mean {mean:.2E} and std {std:.2E} "
f"fails to obtain a non-negative sample")
I expand on @gena-kukartsev 's answer in two ways: First, I avoid recursion which could overflow the call stack. (Let's avoid answers that can overflow the stack on stackoverflow!) Second, I catch possibly bad input by limiting the number of samples of the distribution.
Upvotes: 3
Reputation: 11
Or maybe you could just 'shift' your entire distribution to the 'right' by subtracting the min (or adding the abs val of your min):
y = np.random.normal(0.0, 1.0, 10)
y
array([-0.16934484, 0.06163384, -0.29714508, -0.25917105, -0.0395456 ,
0.17424635, -0.42289079, 0.71837785, 0.93113373, 1.12096384])
y - min(y)
array([0.25354595, 0.48452463, 0.12574571, 0.16371974, 0.38334519,
0.59713714, 0. , 1.14126864, 1.35402452, 1.54385463])
Upvotes: 1
Reputation: 197
data = np.random.randint(low=1,high=100,size=(4,4),dtype='int')
Upvotes: 2
Reputation: 307
what about using lognormal along these lines:
mu = np.mean(np.log(list))
sigma = np.std(np.log(list))
new_list = np.random.lognormal(mu, sigma, length_of_new_list)
Upvotes: 1
Reputation: 1705
I assume that what you mean is that you want to modify the probability density such that it is the same shape as normal in the positive range, and zero in negative. That is a pretty common practical case. In such case, you cannot simply take the absolute value of generated normal random variables. Instead, you have to generate a new independent normally distributed number until you come up with a positive one. One way to do that is recursively, see below.
import numpy as np
def PosNormal(mean, sigma):
x = np.random.normal(xbar,delta_xbar,1)
return(x if x>=0 else PosNormal(mean,sigma))
Upvotes: 7
Reputation: 649
You can offset your entire array by the lowest value (left most) of the array. What you get may not be truly "normal distribution", but within the scope of your work, dealing with finite array, you can ensure that the values are positive and fits under a bell curve.
>>> mu,sigma = (0,1.0)
>>> s = np.random.normal(mu, 1.0, 100)
>>> s
array([-0.58017653, 0.50991809, -1.13431539, -2.34436721, -1.20175652,
0.56225648, 0.66032708, -0.98493441, 2.72538462, -1.28928887])
>>> np.min(s)
-2.3443672118476226
>>> abs(np.min(s))
2.3443672118476226
>>> np.add(s,abs(np.min(s)))
array([ 1.76419069, 2.85428531, 1.21005182, 0. , 1.14261069,
2.90662369, 3.00469429, 1.3594328 , 5.06975183, 1.05507835])
Upvotes: 0
Reputation: 362717
The normal distribution, by definition, extends from -inf to +inf so what you are asking for doesn't make sense mathematically.
You can take a normal distribution and take the absolute value to "clip" to positive values, or just discard negative values, but you should understand that it will no longer be a normal distribution.
Upvotes: 16