基于Python中的P10,P50和P90值生成概率密度函数

时间:2018-01-03 13:24:09

标签: python python-2.7 montecarlo probability-density

我想使用P10,P50和P90值作为输入: A)生成一个概率密度函数(这感觉就像一个Myerson发行版,但是我无法弄清楚如何在Python中做到这一点。虽然Excel中有一个插件,但确实如此; SIPMath) B)在PDF上运行模拟(蒙特卡罗?)

示例:我想模拟从A运行到B所需的时间。

P10 = 1 hour
P50 = 1.5 hours
P90 = 2.5 hours

意思是我在1小时或更短时间内从A跑到B的尝试的10%,在1.5小时或更短时间内从A跑到B的尝试的50%(即1.5是平均值)。 10%的尝试我花费超过2.5小时。

谢谢

3 个答案:

答案 0 :(得分:1)

假设用Myerson分布模拟这个系统是合适的,那么根据Frontline Solvers,“[i] f指定的百分位数是等距的(通过下面的参数b'测量),那么Myerson分布是相当于正态分布。“你很喜欢一个简单的案例。

当然这不可能是正确的,因为法线具有无限的尾巴。您需要从左侧截断的正常总体中抽取样本。

您需要的(未截断的)正态分布平均为1.5小时,并将其质量的40%放在1小时和1.5小时之间。标准法线将其质量的40%放在-1.2815515655446004和0之间。然后,给定标准正态随机偏差的供应,z我们可以将它们转换为(未截断的)通过缩放它们所需类型的偏差{{1}其中0.5是1小时到1.5小时之间的“距离”,而1.28155是标准法线的相应“距离”。

作为正态分布,可能可能会生成一些小于零的随机变量。但是,使用scipy库我发现了,

0.5*(z+1.5)/1.28155

我会说这是不可能的,因此将这视为截断的正常情况并不值得花时间。

因此,要获得问题中定义的Myerson偏差样本,您可以这样做。

>>> norm.cdf(0, loc=1.5, scale=0.5/1.28)
6.151715518325519e-05

我们已经讨论过>>> from scipy.stats import norm >>> sample = norm.rvs(loc=1.5, scale=0.5/1.28, size=100) loc的值。 scale的值将是您需要的任何样本大小。

答案 1 :(得分:1)

这是我尝试解决方案。如果是b' = 1,数据是对称的,我们应该将其视为正态分布。随着样本数量的增加,pX_out接近pX_in。我本来希望能够设置上下障碍,但我还没弄明白该怎么做。任何建议,将不胜感激。谢谢。

def myerson(p10, p50, p90, number_of_samples):
    b_mark = ((float(p90) - float(p50)) / (float(p50) - float(p10)))
    samples = []
    for i in range(number_of_samples):
        rand_numb = random.random()
        factor = norm.ppf(rand_numb, 0, 0.780304146072379)
        if 0.9999 < b_mark < 1.0001: 
            sample = p50 + (p90 - p50) * factor
        else:
            sample = p50 + (p90 - p50)*((b_mark**factor - 1)/(b_mark - 1))
        samples.append(sample)
    return samples

p10_in = 90
p50_in = 100
p90_in = 111
numberofsamples = 10000
data = myerson(p10_in, p50_in, p90_in, numberofsamples)

p10_out = np.percentile(data,10)
p50_out = np.percentile(data,50)
p90_out = np.percentile(data,90)

答案 2 :(得分:0)

事实证明,metalog是在这种情况下使用的正确分布。一种非常灵活的分布,可以处理4种不同的场景:未绑定,下限(具有最小值),上限(最大值)和绑定(最小值和最大值)。

def metalog_multi(p10, p50, p90, numberofsamples, p0 = None, p100 = None):
    p10 = float(p10)
    p50 = float(p50)
    p90 = float(p90)
    if p0 != None:
        p0 = float(p0)
    if p100 != None:
        p100 = float(p100)


    samples = []
    for i in range(numberofsamples):
        x = random.random()
        if p0 == None and p100 == None:
            # unbound

            sample = p50 + 0.5 * (log((1 - 0.1) / 0.1)) ** (-1) * (p90 - p10) * log(x / (1 - x)) + ((1 - 2 * 0.1) * (log((1 - 0.1) / 0.1))) ** -1 * (1 - 2 * (p50 - p10) / (p90 - p10)) * (p90 - p10) * (x - 0.5) * log(x / (1 - x))

        elif p100 == None:
            # lower bound
            sample = p0 + e ** (log(p50 - p0) + 0.5 * (log((1 - 0.1) / 0.1)) ** -1 * log((p90 - p0) / (p10 - p0)) * log(x / (1 - x)) + ((1 - 2 * 0.1) * (log((1 - 0.1) / 0.1))) ** -1 * log(((p90 - p0) * (p10 - p0)) / (p50 - p0) ** 2) * (x - 0.5) * log(x / (1 - x)))
        elif p0 == None:
            # upper bound
            sample = p100 - e ** (-(-log(p100 - p50) - (0.5) * (log((1 - 0.1) / 0.1)) ** -1 * log((p100 - p90) / (p100 - p10)) * log(x / (1 - x)) - ((1 - 2 * 0.1) * (log((1 - 0.1) / 0.1))) ** -1 * log(((p100 - p90) * (p100 - p10)) / (p100 - p50) ** 2) * (x - 0.5) * log(x / (1 - x))))
        else:
            # bound
            sample = (p0 + p100 * e ** (log((p50 - p0) / (p100 - p50)) + (0.5) * (log((1 - 0.1) / 0.1)) ** -1 * log(((p90 - p0) / (p100 - p90)) / ((p10 - p0) / (p100 - p10))) * log(x / (1 - x)) + ((1 - 2 * 0.1) * (log((1 - 0.1) / 0.1))) ** -1 * log((((p90 - p0) / (p100 - p90)) * ((p10 - p0) / (p100 - p10))) / ((p50 - p0) / (p100 - p50)) ** 2) * (x - 0.5) * log(x / (1 - x)))) / (1 + e ** (log((p50 - p0) / (p100 - p50)) + (0.5) * (log((1 - 0.1) / 0.1)) ** -1 * log(((p90 - p0) / (p100 - p90)) / ((p10 - p0) / (p100 - p10))) * log(x / (1 - x)) + ((1 - 2 * 0.1) *(log((1 - 0.1) / 0.1))) ** -1 * log((((p90 - p0) / (p100 - p90)) * ((p10 - p0) / (p100 - p10))) / ((p50 - p0) / (p100 - p50)) ** 2) * (x - 0.5) * log(x / (1 - x))))
        samples.append(sample)
    return samples


p0_in = 10
p10_in = 20
p50_in = 40
p90_in = 80
p100_in = 250
numberofsamples = 10000
data = metalog_multi(p10_in, p50_in, p90_in, numberofsamples, p0 = p0_in)

p10_out = np.percentile(data,10)
p50_out = np.percentile(data,50)
p90_out = np.percentile(data,90)