简体   繁体   中英

Python numpy.random.normal only positive values

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)

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.

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

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)

数据 = np.random.randint(low=1,high=100,size=(4,4),dtype='int')

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])

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])

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.

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]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM