【问题标题】:Drawing random float from probability distribution with dynamic mean in python在python中使用动态平均值从概率分布中绘制随机浮点数
【发布时间】:2019-01-27 20:44:58
【问题描述】:

我正在寻找一种从概率分布中绘制随机浮点数的方法,该概率分布的均值或 loc 具有模型中节点的值。最重要的是,绘制的数字不应超过 [-1.0, 1.0] 的范围,并且不应超过 1 个小数。

Kinda like this

所以如果节点的值是 0.8,loc 应该是 0.8,但是 1 之外的值不能被绘制。我对编程真的很陌生,所以如果有人能给我任何关于这是否可以开始的提示,我将不胜感激。使用 normal([loc, scale, size] 我认为这是不可能的。在此先感谢


非常感谢您的回答。我的错,我的意思是小数点后两位!这使解决方案与我猜的您的建议不同,因为这样数字的数量就会增加很多,而且这种方法可能不可行?

现在我有这个:

def 随机数(位置,比例): return np.random.normal(loc, scale, size=None)

                elif node == 'is_po': 
                      for neig in graph.predecessors(node):
                        neig_w = graph.edges[neig, node]['weight']
                        neig_s = graph.node[neig]['status'][t - delta_t]

                        loc = neig_s
                        scale = 1

                        c = randomnumber(loc, scale)
                        graph.node[node]['status'][t] = c * delta_t

这似乎给了我一个围绕 neig_s 的值绘制的随机数,但我不知道是否有可能确保绘制的随机数不来自高于 1 或低于 -1。

【问题讨论】:

  • 所以你希望它们仍然像你的情节一样正常分布?
  • 如果提供的答案之一满足您的需求,您应该将其作为答案勾选。如果没有,您应该编辑您的问题以澄清哪些问题仍未得到解答。

标签: python random probability distribution


【解决方案1】:

鉴于您希望结果保留一位小数,您所描述的不是范围 [-1,1] 上的连续分布,而是缩放到该范围的离散分布。有 21 个允许值(-1.0、-0.9、-0.8、...、0.8、0.9、1.0),因此一种方法是使用离散分布,该分布在 [0,...,20] 范围内产生结果.然后,您将缩放您的 target 平均值并将其转换为 0 到 20 之间的相应 scaled_target 值,从具有该平均值的某个分布中生成一个值,并将结果缩放回范围 [-1,...,1 ].

通过关系scaled_target = 20 * (target + 1) / 2 实现正向缩放。例如,target = 0.8 将产生 scaled_target = 18,因此您将生成 0 到 20 之间的值,平均值为 18。然后通过从结果中减去 10 缩小到范围 [-1,...,1]并除以 10。

一个易于使用的分布是带有n = 20 的二项式分布,以产生所需的范围。由于二项式的平均值是n * p,而您想要平均值为 18,您可以使用p = 0.9,它可以直接推导出为(target_mean + 1.0) / 2.0——无需乘以 20 然后再除以 20。

正向和反向缩放只需要几行代码,您可以使用numpy(或scipy,如果您愿意)来生成二项分布:

import numpy

def generate_value(target_mean):
    scaled_target = (target_mean + 1.0) / 2.0
    return (numpy.random.binomial(n = 20, p = scaled_target) - 10.0) / 10.0

样本输出:

print([generate_value(target_mean = 0.8) for _ in range(10)])  # => [0.8, 0.9, 0.5, 0.7, 0.7, 0.9, 0.5, 0.8, 1.0, 0.8]
print([generate_value(target_mean = 0.0) for _ in range(10)])  # => [-0.1, 0.1, 0.2, 0.0, -0.3, -0.4, -0.2, -0.1, -0.2, 0.3]

如果您想要更广泛的结果,则需要选择不同的离散分布,但该方法非常简单地概括。


附录

将其修改为小数点后两位不会改变方法的结构,只会改变比例:

def generate_value(target_mean):
    scaled_target = (target_mean + 1.0) / 2.0
    return (numpy.random.binomial(n = 200, p = scaled_target) - 100.0) / 100.0

如果使用简单二项式生成的值对您来说过于聚集,您可以通过使用 beta distribution 动态生成二项式的 p 来将其替换为 beta-binomial distribution,并缩放 α 以产生预期值为scaled_target 和 β 缩放以产生适当的结果分散:

import numpy

BETA_SHAPE = 2.0    # larger values will yield more clustered outcomes

def generate_value(target_mean):
    scaled_target = (target_mean + 1.0) / 2.0
    alpha = BETA_SHAPE * scaled_target / (1.0 - scaled_target)
    beta_p = numpy.random.beta(a = alpha, b = BETA_SHAPE)
    return (numpy.random.binomial(n = 200, p = beta_p) - 100.0) / 100.0

样本输出:

lst = [generate_value(target_mean = 0.7) for _ in range(10000)]
print(numpy.mean(lst))    # => 0.695812
print(min(lst))           # => -0.37
print(max(lst))           # => 1.0

分配的具体选择取决于您,但方法是通用的。这也给出了您感兴趣的精确平均值,而基于截断或接受/拒绝的答案会改变平均值。

【讨论】:

  • 非常感谢您的详尽回答。我说小数点 1 是错误的,因为我想要两个(0.54,-0.74)。我认为这种方法不再可行,因为数字的数量急剧增加。尽管如此,我也会尝试这种方法。
【解决方案2】:

我假设您想要一个比例为 1 的正态分布数字。一个简单的解决方案如下:

import numpy as np
def func(loc):
    max_iter = 1000
    x = np.random.normal(loc)
    c = 0
    while c < max_iter:
        if x > -1 and x < 1:
            return x
        c += 1
        x = np.random.normal(loc)
    print('max iter exceeded')

请注意,如果您为 loc 参数指定高值或低值,则 while 循环将永远运行,因此我设置了一个限制 'max_iter'。

如果你想要一个更高级的解决方案,那么你已经定义了一个截断的正态分布(你可以在 scipy 中做到这一点)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-10-09
    • 1970-01-01
    • 2022-06-27
    • 2017-06-10
    • 2020-04-04
    • 1970-01-01
    • 1970-01-01
    • 2012-03-15
    相关资源
    最近更新 更多