Python numpy.random.normal仅产生正值

17

我想用numpy.random.normal创建一个正态分布的数组,但只包含正值。例如,以下示例说明它有时会返回负值,有时会返回正值。我该如何修改它才能仅返回正值?

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

我想我可以以某种方式解决它:

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)

1
根据定义,正态分布覆盖了所有可能的值,包括正数和负数。你不能将“正态分布”与“仅有正数值”调和,所以我问你... 你真正想要什么? - Patashu
1
我需要正常分布的值来输入到一个函数中。这个函数只能接受正数值。 - ustroetz
2
正态分布覆盖了所有可能的值,包括正数和负数。如果你防止它返回负值,那么它就不再是一个正态分布。因此,无论你将什么分布输入到函数中,根据定义都不能为负数。考虑到上述情况,你想要哪种分布? - Patashu
1
二项分布与正态分布类似,但是是离散的,并且仅在正值范围内变化: http://en.wikipedia.org/wiki/Binomial_distribution - Patashu
请参考以下链接:https://dev59.com/TGMl5IYBdhLWcg3wZGPo - Sparkler
显示剩余3条评论
9个回答

12

正态分布的定义从负无穷到正无穷延伸,因此您所要求的在数学上是没有意义的。

您可以取一个正态分布并取绝对值来“裁剪”为正值,或者只丢弃负值,但您应该理解这将不再是正态分布。


1
正态分布的定义是从负无穷到正无穷,但这并不意味着你不能在数值上获得截断/偏移/x-平移的分布。这种情况可能会出现在典型的光谱探测器中。在使用np.random.normal(fix_mean,fix_sigma)生成每个数字后,您仍然可以检查非负条件并重新生成一个新的数字。 - jaydeepsb
1
@jaydeepsb...这正是我在答案中提到的吗? - wim
2
这个问题适用于你想要从正态分布中仅保留正值,并在定义中定义数组的大小的情况。这个问题是有意义且有效的。 - aerijman

6
我理解你的意思是,你希望修改概率密度,使其在正值范围内与正态分布相同,并在负值范围内为零。这是一个非常常见的实际情况。在这种情况下,你不能简单地对生成的正态随机变量取绝对值。相反,你必须生成一个新的独立正态分布数,直到得到一个正数为止。一种方法是使用递归,如下所示。
``` 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)) ```

这对于某些用例可能是合理的,但请注意,从该分布中进行采样将偏向于更高的值,特别是如果均值接近零。此外,如果你运气不好,可能会出现堆栈溢出的情况。 - vroomfondel
是的,这不再是正常分布了,我们改变了它,它有一个不同的概率密度函数,在零处被截断。但我认为那就是问题所在,而且通常这样做是很实用的。关于你对堆栈溢出的担忧,在这种情况下,除非你只有几个字节的RAM,否则没有人会那么倒霉。 - Gena Kukartsev
关于您对此情况的堆栈溢出的担忧,如果只对分布的几个西格玛右尾感兴趣,即如果分布的平均值为负数,则您是正确的。在这种情况下,该解决方案的性能将恶化并最终溢出。但我怀疑这不是预期的用例。这更像是一个快速的hack。 - Gena Kukartsev
看起来非常低效,因为你也可以使用 abs() - user1406177
不,使用abs()会改变分布的形状。区别在于截断负尾巴并“翻转”它以及将其添加到分布的其余部分之间。这将不成比例地增加接近零的概率密度。你可以认为它满足了原始问题:它将产生仅为正数,但来自不同且相当奇怪的分布。 - Gena Kukartsev
1
你不需要使用递归函数来实现相同的结果 x = np.random.normal(mean, sigma)``` - Corvince

2
这个问题很有道理。为了激励,可以考虑生物细胞的模拟。细胞中一种分子计数的分布可以用正态分布来近似,但必须是非负的才能具有物理意义。
我的整体模拟器使用这种方法来对分子计数的初始分布进行抽样:
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")

我会在 @gena-kukartsev 的答案基础上进行两方面的拓展:首先,我避免使用可能会导致调用栈溢出的递归方式。(让我们避免在stackoverflow上可能会导致堆栈溢出的答案!)其次,通过限制分布样本数量,我可以捕获可能的错误输入。

当P [0 <= sample]非常低时,我的答案当然是低效的。正态分布可用于拒绝高度可能失败的“non_neg_normal_sample”调用。但是,“non_neg_normal_sample”的结构适用于包括负值和正值的任何分布。 - Arthur

1

或者您可以通过减去最小值(或加上最小值的绝对值)来将整个分布“向右”移动:

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

1

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

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

这正是我正在寻找的。谢谢! - Tensigh
3
如果问题是获取非负正态分布的数字,那么您的解决方案是错误的。因为np.random.randint从均匀分布中抽取随机数,所以您仍需要使用np.random.normal。如ustroetz所建议的,解决方法是不断抽取新的数字(具有相同的平均值和标准差),直到它是非负的,然后将其包含在您的数组中。 - jaydeepsb

1
什么样使用对数正态分布呢:
    mu = np.mean(np.log(list))
    sigma = np.std(np.log(list))

    new_list = np.random.lognormal(mu, sigma, length_of_new_list)

0
你可以通过数组中最小值(左侧)将整个数组偏移。你得到的可能不是真正的“正态分布”,但在处理有限数组的工作范围内,你可以确保值为正,并适合于钟形曲线。
>>> 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])

0

您可以使用高局部性与低规模:

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]

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

2
您的答案可能需要附加支持信息。请编辑以添加更多详细信息,例如引用或文件,以便其他人可以确认您的答案是否正确。您可以在帮助中心上找到有关如何撰写良好答案的更多信息。 - moken
你从哪里得到了GDP_cap? - soggypants

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接