BetaPERT Distribution

Beta-PERT is just the Beta distribution with the PERT heuristic to compute its $$\alpha$$ and $$\beta$$ parameters from three-point-estimates of A - the minimum, M - the most likely, and B - the maximum expected value. The normal distribution, due to its immutable symmetry around the mean and median, is not always the best model, when we know a little more about the distribution of values. Let's derive this useful tool.

beta

from fxy.s import *
from sympy.stats import Beta, density

# α, β
a = Symbol('α', positive=True)
b = Symbol('β', positive=True)
B = Beta('B', a, b)

Let's get its general distribution (pdf) formula, and print specific parametrization:

beta_pdf = density(B)(x)

my_beta_pdf = beta_pdf.subs({a: 2, b: 5})

plot(my_beta_pdf, xlim=[0, 1], ylim=[0,5]);

Continuation: https://github.com/mindey/plandf/blob/master/docs/beta-PERT.ipynb

PERT

PERT stands for Program Evaluation and Review Technique by Malcolm et al, 1959. It was initially known as the Program Evaluation and Review Task. The task to which this refers was assessing the uncertainty in the plans for the development schedule and cost for the Polaris weapon system [*]. However, over time, it had found its was into project planning in general.

It provides a kind of heuristic to compute parameters $$\alpha$$ and $$\beta$$ for the Beta distribution, based on 3 pieces of information for every asset:

  • A -- the min amount

  • M -- the most likely amount

  • B -- the max amount

Continuation: https://github.com/mindey/plandf/blob/master/docs/beta-PERT.ipynb

betaPERT

While PERT gives mean and variance to use with any distribution, if you would try the above with Beta distribution, you would get errors. To use PERT with Beta distribution for intervals other than $$[0, 1]$$, we should need to normalize and shift data to unitary interval before computing PERT.

from sympy.stats import Beta, density
from sympy import Symbol

def BetaPERT(A, M, B, normalize=True, info=False):
    """
    A: beginning of interval
    M: most likely value
    B: end of interval

    Returns a Beta distribution with PERT parameters.
    """

    a = Symbol('α', positive=True)
    b = Symbol('β', positive=True)

    def PERT(A, M, B, normalize=False):
        """
        Use normalize=True, if you want to go around [-1, 1] interval,
        (if you want to look at the distribution shape without caring about its location.)
        """

        if normalize:
            try:
                Max = max(A, M, B)
            except:
                import pdb; pdb.set_trace()
            Min = min(A, M, B)
            standardize = -(Min + Max) / 2.
            normalize = Max - Min

            A = (float(A) + standardize) / normalize + 0.5
            M = (float(M) + standardize) / normalize + 0.5
            B = (float(B) + standardize) / normalize + 0.5

            if info:
                print({'A': A, 'M': M, 'B': B})

        # PERT Heuristics:
        mean = (A + (4. * M) + B) / 6.
        variance = ((A - B) / 6.)**2.

        return {'mean': mean, 'variance': variance}

    def beta_params(mean, variance):
        miu = Symbol('μ')
        sigma = Symbol('σ')
        alpha = -b*miu / (miu - 1)
        beta = (miu - 1 ) * (miu**2 - miu + sigma) / sigma

        beta = beta.subs({miu: mean,sigma: variance})
        alpha = alpha.subs({miu:mean, b:beta})

        return {a:alpha, b:beta}

    def pert_beta(A, M, B):


        beta_cdf = Beta('B', a, b)

        # x = Symbol('x')
        # beta_pdf = density(B)(x)

        return beta_cdf.subs(
            beta_params(
                **PERT(A, M, B, normalize)
            )
        )

    return pert_beta(A, M, B)

Then use it:

from fxy.s import *

D = BetaPERT(10., 13.8, 15.)
p = density(D)(x)
plot(p, xlim=[0, 1], ylim=[0, 3])

# Try:
# plot(density(BetaPERT(0., 0.2, 1.))(x))
# plot(density(BetaPERT(0., 0.8, 1.))(x))

Last updated