© Robert D. Brown III 2018
Robert D. Brown IIIBusiness Case Analysis with Rhttps://doi.org/10.1007/978-1-4842-3495-2_3

3. Include Uncertainty in the Financial Analysis

Robert D. Brown III1 
(1)
Cumming, Georgia, USA
 

We incorporate potential effects of uncertainty in our analysis to account for the fact that we don’t know what will happen in the future. Monte Carlo simulation is the method that allows us to include these considerations of uncertainty analytically.

Why and How Do We Represent Uncertainty?

Looking forward to future outcomes that are important to consider in planning exercises , we realize rather quickly that we rarely know the exact representative values of those outcomes. We don’t know exactly how much something will cost, or how long it will take to accomplish a set of tasks, or what the actual adoption rate will be for a given product. The list goes on. Just because we don’t know what the actual outcomes will be, however, doesn’t give us license to avoid considering them in our planning. On the other hand, when we consider all of the multiple ways in which the various uncertain outcomes can be combined, the number of calculations required becomes intractably large. Most people recognize this, and they try to overcome the limitations with the tools they use for analysis (i.e., spreadsheets), or through their knowledge of how to treat the uncertainty via workarounds, usually in at least two steps.

The first step many people take to handle the problem of uncertainty is to try estimating most likely cases for the appropriate assumptions in their planning, or to consider worst or best case scenarios in an effort to consider conservative positions. What they usually fail to consider are the numerous ways in which bias creeps into their estimates of most likely outcomes (indeed, they might not even be aware of the subtle ways it happens), or the number of improbable conditions that are required to justify the particular caveats that define best or worst case scenarios.

The second step people take attempts to account for the effect of ranges around the assumptions on the objective function. Here again, bias creeps in because the range of sensitivity chosen is not usually tied to a likely range of variation. Instead, people use arbitrary ranges of variation, like ±20%, across all their assumptions as a way to accommodate what they think is a significant range.

Of course, there are also those analysts with a masochistic bent who attempt a brute force approach of testing dozens of scenarios sequentially, convincing themselves they are gaining a comprehensive and detailed understanding of their problem. All they are really doing is getting mired in analysis paralysis. The temptation to test “just one more scenario” never ends. The worst case of this mindset I have ever observed was a client who had produced 216 scenarios of a capital expansion plan over the course of two years, and they were no closer to making meaningful decisions than the day they began. Such wasted time only leads to value attrition and missed opportunity.

The net result of the first two steps is that people accommodate uncertainty with biased rationalizations, falsely communicating an unwarranted degree of precision by the single point numbers chosen in their analysis. Then they “stress test” their analysis with arbitrarily chosen relative ranges on assumptions. Bias, false precision, and arbitrariness don’t seem like the basis for clear and valid analysis to me.

The answer to this problem is actually similar to the brute force approach ; that is, we consider a large number of scenarios across the range of likely outcomes of the assumptions. However, instead of performing our calculations sequentially and only on the scenario combinations we tend to think matter most, we perform the calculations in parallel and according to the coherent and consistent rules of probability. This means that we must get used to thinking in ranges of outcomes (not single points) and corresponding degrees of likelihood across each assumption’s range. In short, we should treat uncertain assumptions as probability distributions. That’s the notional side of the effort. The functional side incorporates a process for conducting the calculations called Monte Carlo simulation .

How we actually gather information to represent assumptions as probability distributions and what that even means is presented in Part 3 of this book, which presents a process for eliciting distributions from SMEs. The following resources also provide excellent guidance in this area.
  • Peter MacNamee and John Celona, Decision Analysis for the Professional (San Jose, CA: SmartOrg, 2007).

  • Gregory S. Parnell, Handbook of Decision Analysis (Hoboken, NJ: Wiley, 2013).

  • Patrick Leach, Why Can't You Just Give Me the Number?: An Executive's Guide to Using Probabilistic Thinking to Manage Risk and Make Better Decisions (Sugar Land, TX: Probabilistic Publishing, 2014).

What Is Monte Carlo Simulation?

Before I delve into the technical details of Monte Carlo simulation , I provide a little analogy to help clarify the essential mechanism by which it operates. Imagine a BB gun (the Red Ryder Carbine Action 200-shot Range Model air rifle, to be exact) mounted to a slider on a vertical rail with a fixed height. The BB gun always points horizontally at a 90° angle to the vertical rail. When it fires a BB, the trajectory of the BB is perfectly flat until it hits a special obstacle that has been erected at a fixed distance from the vertical rail. The obstacle is like a wall, but it leans over at an angle such that its vertical height is exactly the same as the vertical rail that holds the BB gun, and it’s shaped a bit like a French curve.

The wall also has a special property so that when a BB hits it, the BB passes through it but falls down vertically to the ground. Underneath this wall are little cups sitting along the length of the wall such that each cup touches its adjacent cup, and each cup is numbered in sequence starting at some lowest value up to a highest value with equal step sizes. Now that we’ve accepted this setup, fantastic as it sounds, imagine that a gremlin randomly moves the BB gun up and down the vertical rail, firing a BB at each position that it selects. Over time, we notice that the cups underneath the wall are filling up with BBs, and the most BBs fill the cups closest to the section of the wall that is most vertical (see Figure 3-1).
../images/461101_1_En_3_Chapter/461101_1_En_3_Fig1_HTML.jpg
Figure 3-1

Monte Carlo simulation is like a gremlin shooting BBs at a wall from a random vertical postion. The wall records the horizontal position of where it was struck by dropping the BB into a cup underneath.

The process describing how our gremlin randomly shoots BBs at a wall is almost exactly how Monte Carlo simulation of a random variable works, except that instead of BBs being fired from a random vertical position, a computer selects random numbers strictly in the interval from 0 to 1. Each of these random numbers gets mapped to another value with a function that can produce numbers in any other arbitrary domain. For example, suppose we want to re-create a possible sample distribution of adult humans in a situation in which their heights are of interest to us. If we had a function that describes the distribution of the heights of the population of interest, we could randomly create a test population of their heights without any prejudice toward one side of the range or another.

Let’s take our analogy a little further. Imagine now that we play a game with of these shooting ranges, each with its own gremlin and uniquely shaped function wall. Let’s denote the function walls with the names A, B, and C. Our gremlins are coordinated such that they each fire their BB guns at the same time, but they independently choose the vertical position from which they fire. As it turns out, we are given a numerical relationship between A, B, and C, which is Y = A * (B + C). On each round of firing, we note the value of the cups the BBs fall into from their respective ranges, we perform the calculation A * (B + C), and we record the value of Y. The game we are playing is to try to predict where most of the values of all possible values of Y will occur after we’ve observed the gremlins shoot a sufficiently large—but not exhaustively large—number of BBs.

This situation is not as silly as it sounds. Suppose we want to estimate the range and prevalence of most of our costs for manufacturing berets, raspberry and lemon colored, and the manufacturing cost could vary over a range during the time we receive orders for berets, the actual number of which we won’t know until it’s time to close the books. Our formula for the exact cost of manufacturing in our period of concern will be mfg.cost = unit.mfg.cost * (number.raspberry + number.lemon).

The potential range and prevalence of the final manufacturing cost could be simulated using the gremlin method or we could use Monte Carlo simulation to simulate the range of possible values of the terms of our cost formula as long as we keep track of the outcomes of each of the terms. Once we’ve done such a thing, we would be in a better position to more accurately budget our operational costs for the year or write favorable contingent contracts with our suppliers if unit costs exceeded a certain value. The Monte Carlo simulation process gives us the ability to gain better clarity about how the world might turn out so that we can plan and design more desirable situations for ourselves and others.

A probability distribution for an uncertain assumption is defined within some real number domain and over a specific interval within that domain. Within the specific interval, probabilities are assigned to nonoverlapping subintervals that express our degrees of belief that the subintervals can occur or long-run frequencies that the subintervals will occur. We must conform to the inviolate principle that the sum of the probabilities equals exactly 1 within the interval.

For example, a cost for a project task might be defined in the domain from $0 to infinity, but the potential interval denoted by the context of the problem at hand might only cover $100,000 to $500,000. Within that interval, probabilities are assigned either in discrete buckets (e.g., Prob($100,000 < X <= $200,000) = 0.1; Prob($200,000 < X <= $300,000) = 0.25; Prob($300,000 < X <= $400,000) = 0.5; Prob($400,000 < X <= $500,000) = 0.15) or according to some mathematical function that assigns probabilities in a continuous form.1

Once these parameters are defined, the Monte Carlo algorithm randomly selects values from within an assumption’s interval according to the frequency implied by its internal probabilities over hundreds to thousands of iterations. This process is repeated in parallel for all the assumptions, some of them independently and others in a coordinated, or conditional, manner as required by the business logic. On each iteration, the intermediate calculations in the business case model are then performed as if only single point values were considered. Each iteration of the simulation represents a parallel universe to the other iterations. The outcomes on each iteration are stored, and the net results are examined in bulk. Consequently, through the combination of the business logic and the probabilities defined on the assumptions’ intervals, we can get a much less biased view of what the most likely range of conditions will be for our objective function. Of course, our assumptions’ definitions might be biased from the beginning. The books mentioned earlier describe methods for controlling for that condition, as do later sections of this book.

The Matrix Structure of Business Case Simulation in R

With the exception of the depreciation array, we based the deterministic model framework in one-dimensional vectors, as all the calculations produced results along a single index, year. To add the risk layer of the model, we need to add another dimension that handles the parallel scenarios. This dimension, run, will exist orthogonally to the year index. Its length, the number of iterations we choose to run (kSampsize), will be the same consistently for every calculation.

The following is an example of the first few rows of the phase calculation using uncertain values for the Phase 1 duration (p1.dur) and Phase 2 duration (p2.dur).
../images/461101_1_En_3_Chapter/461101_1_En_3_Figa_HTML.jpg

Notice that the year index elements produce the columns of the matrix, and the run index elements produce the rows. Each row represents a potential manifestation of the world in which a specific set of outcomes are realized for the Phase 1 and Phase 2 durations.

Before I explain how to set up this kind of matrix, I discuss some ways in which we can generate subjective distributions for each of our assumptions that represent the uncertainty we might face .

Useful Distributions for Expert Elicited Assumptions

Business case analysis often depends on making assumptions for which we possess little or no documented data from which we can develop appropriate empirical distributions . As I mentioned earlier, though, not having so-called hard data doesn’t excuse us from using a distribution. In these cases, we need to extract the information we need from SMEs and encode their knowledge, expertise, and degrees of uncertainty into a procedural function. I describe here four useful approaches for using expert elicited values from SMEs to produce distributions for uncertainties.

Discrete Distributions: McNamee-Celona and Swanson-Megill

In the early days of the decision analysis program, tractable ways to simulate uncertainty were handled by the Gaussian quadrature, McNamee-Celona,2 and later, the extended Swanson-Megill3 methods. These methods provide discrete approximations of single-mode continuous distributions in some domain of concern. Specifically, they derive three probability weightings that specify the frequency of discrete outcomes representing an expert’s 80th percentile prediction interval values that preserves the mean and variance of the continuous distributions implied by the expert elicitation. Gaussian quadrature or McNamee-Celona were used for symmetric distributions; extended Swanson-McGill was used for skewed distributions. The three weights are associated with the 10th, 50th, and 90th cumulative probability quantiles.

This simplification of continuous variable outcomes permitted a cheaper means of running a simulation when numerical processing was much more expensive than it is today. In fact, the simplification could be conveniently performed by calculator and hand-drawn decision trees .

The three methods typically use the following weights, or probabilities, for each of three discrete intervals in the represented event.
  • Gaussian quadrature, McNamee-Celona: Prob(X10, X50, X90) = (0.25, 0.5, 0.25)

  • Extended Swanson-Megill: Prob(X10, X50, X90) = (0.3, 0.4, 0.3)4

For example, let’s say we interview an SME for the potential range of outcomes for the annual production costs. Our elicitation yields the following values :
  • ProductionCosts10 = $2.75 million

  • ProductionCosts50 = $3.00 million

  • ProductionCosts90 = $3.75 million

These values reveal that, given the best information available to our SME, he believes that there is a 10% chance the outcome will be less than $2.75 million, and a 90% chance the outcome will be less than $3.75 million. He places even odds that the outcome could be higher or lower than $3.00 million. When we develop a simulation of values in this range, our algorithm should select the ProductionCosts10 = $2.75 million approximately 30% of the time; ProductionCosts50 = $3.00 million 40% of the time; and ProductionCosts90 = $3.75 million 30% of the time. The extended Swanson-Megill weightings are chosen because the range is skewed around ProductionCosts50.

Be aware that using a discrete method poses some problems, namely that it sacrifices accuracy for speed and ease of use. When the discrete method is used for many independent variables in a simulation, the loss of accuracy is minimized somewhat; however, if a simulation relies on only a few distributions represented by these discrete approximations, the loss of accuracy can be pronounced.

The following code provides the McNamee-Celona and Swanson-Megill approach.

CalcMCSM = function(p10, p50, p90, samples) {
# This function simulates a distribution from three quantile
# estimates for the probability intervals of the predicted outcome.
# p10 = the 10th percentile estimate
# p50 = the 50th percentile estimate
# p90 = the 90th percentile estimate
# samples = the number of runs used in the simulation
# Note, this function strictly requires that p10 < p50 < p90.
# If the distribution is skewed, the quantiles are selected with
# frequency:
# p10 -> 0.3, p50 -> 0.4, and p90 -> 0.3 from Swanson-Megill method.
# If the distribution is symmetric, the quantiles are selected with
# frequency p10 -> 0.25, p50 -> 0.5, and p90 -> 0.25 from
# McNamee-Celona method. The process of simulation is simple Monte
# Carlo. The returned result is a vector containing the values of the
# quantiles at frequencies represented by the method indicated by the skew.
  if (p10 >= p50 || p50 >= p90) {
    stop("Parameters not given in the correct order. Must be
    given as p10 < p50 < p90.")
  } else {
# Determine if the distribution is skewed or not.
    skew <- (p50 - p10 != p90 - p50)
# Create a uniform variate sample space in the interval (0,1).
    U <- runif(samples, 0, 1)
# Select the quantiles at the frequency indicated by the skew.
    X <- if (skew == T) {
      (U <= 0.3) * p10 + (U > 0.3 & U <= 0.7) * p50 + (U > 0.7) * p90
    } else {
      (U <= 0.25) * p10 + (U > 0.25 & U <= 0.75) * p50 + (U > 0.75) * p90
    }
    return(X)
  }
}
Using the values in the production costs example earlier gives the cumulative probability distribution in Figure 3-2 after some postprocessing on the returned sample values.
../images/461101_1_En_3_Chapter/461101_1_En_3_Fig2_HTML.jpg
Figure 3-2

The cumulative probability distribution of the discrete approximation of the continuously distributed production costs using the extended Swanson-Megill generator

Continuous Distributions

Triangular

The triangular distribution is a distribution frequently used for the simulation of uncertainties that preserves the continuous nature of the represented event. In this regard, the triangular is an incremental improvement over the discrete methods. Like the discrete methods, the distribution requires three parameters: a low estimate, a mode estimate, and a high estimate.

I rarely use this distribution (or its smoother close cousin, the Beta-PERT) unless the SME I consult knows specifically what the two extreme values are, usually defined by some constraint in the underlying system, and the mode (the most likely outcome across the distribution) is known with strong empirical evidence. I follow this guideline because my own experience has shown me that SMEs are not very good at estimating most likely outcomes (regardless of the distribution being considered) without importing a number of extremely powerful biases; we explore ways to deal with this in later sections of this book.

Biases arise from many cognitive and motivational drivers, including these:
  • Availability: Recalling values that are memorable or easily accessible.

  • Anchoring: Using the first best guess as a starting point for subsequent estimating.

  • Expert overconfidence: Failure of creativity or hubris (e.g., “I know this information and can’t be wrong because I’m the expert”).

  • Incentives: The SME experiences some benefit or cost in relationship to the outcome of the term being measured, adjusting the estimate in the direction of the preferred outcome.

  • Entitlement: The SME provides an estimate that reinforces his or her sense of personal value.

Therefore, if you must use a triangular distribution (or the Beta-PERT), SMEs should be aware that when they think about the low and high values, they believe the chance that any other outcome beyond these limits is nearly zero. Furthermore, they clearly understand that the most likely case is the mode, and not an average of the range of the interval or the median case (the point at which they believe there are equal odds that the outcome could either be higher or lower). The Beta-PERT distribution also suffers from some other limitations; namely, it often underestimates the uncertainty in the natural distribution (i.e., task durations) it is frequently employed to represent. For this reason, I never use the Beta-PERT, as I think it performs even less well than the triangular distribution to communicate the full effect of uncertainty on the final value function .

CalcTriang = function(n, l, mode, h, samponly=TRUE) {
# This function simulates a triangular distribution from three
# estimates for the interval of the predicted outcome.
# l = low value
# mode = the most likely value
# h = the high value
# n = the number of runs used in the simulation
# Note, this function strictly requires that p10 < p50 < p90.
# The process of simulation is simple Monte Carlo.
# The returned result is, by default, a vector of values for X if
# samponly=TRUE, else a (n x 2) matrix is returned such that
# the first column contains the domain samples in X, and the
# second column contains the uniform variate samples from U.
      if (l == mode && mode == h) {
             return(rep(mode, n))
      } else if (l > mode || mode > h) {
             stop ("Parameters not given in the correct order. Must be given
               as l <= mode <= h.")
      } else {
#Create a uniform variate sample space in the interval (0,1).
             U <- runif(n, 0, 1)
# The relative position of the mode in the interval (l, h).
             m <- (mode - l)/(h - l)
# The lower tail quadratic solution of x in a unit space.
             x1 <- (U <= m) * sqrt(U * m)
# The upper tail quadratic solution of x in a unit space.
             x2 <- (U > m)*((-sqrt((m - U)/(1 - m) + 1) + 1)*(1 - m) + m)
# Fits the solution to the full range indicated by the inputs.
             X <- l + (h - l) * (x1 + x2)
# Return the results.
             if (samponly == TRUE) {
             return(X)
       } else {
             X <- array( c(X, U), dim=c(n, 2))
             return(X)
       }
     }
}
Using the values in the production costs example again gives the cumulative probability distribution in Figure 3-3, also after some postprocessing on the returned sample values.
../images/461101_1_En_3_Chapter/461101_1_En_3_Fig3_HTML.jpg
Figure 3-3

The cumulative probability distribution of the production costs using the triangular distribution generator

However, you should not think of p10, p50, and p90 estimates as corresponding to low, mode, and high parameters for a triangular distribution. I did that here merely to demonstrate the shape of the resultant curve.

BrownJohnson Distribution

Some years ago I became frustrated with the limitations of the discrete and triangular parameterizations, the tedious process of assessing a full distribution from an expert when dozens of assumptions had to be assessed, or finding an approximate known distribution to fit a few data points gathered from an SME. To this latter point, it just wasn’t intellectually satisfying to use a distribution just because I could find a similar geometry to the data I had, regardless of whether the distribution actually matched the underlying process being assessed. Furthermore, in some sense, I am indifferent to the use of any known distributions for business case simulations (unless I have good empirical evidence and a clear rationale that convinces me otherwise), as all I really care about is representing the uncertainty as reported by an SME in a logically consistent and probabilistically coherent manner. My belief is that if the SME is properly calibrated, his or her report will conform to any underlying process as understood by his or her mind.

I derived a parametric guidance that approximates a full assessment using only the 10th, 50th, and 90th cumulative probability quantiles. Then I used a piecewise quadratic spline to build the associated cumulative probability distribution. The guidance goes as follows :
  • p0 = 2.5 * p10 - 1.5 * p50

  • p100 = 2.5 * p90 - 1.5 * p50

The p0 and p100 are not intended to represent the actual values beyond which no other outcome can occur. Rather, they are virtual endpoints that capture approximately 99.9% of the range of uncertainty implied by the p10, p50, and p90 with Gaussian-like tails.

If we use the values from our example in the Swanson-Megill section earlier,
  • ProductionCosts10 = $2.75 million

  • ProductionCosts50 = $3.00 million

  • ProductionCosts90 = $3.75 million

from the formula for the p0 and p100 tail points, we get these values:
  • p0 = 2.5 * $2.75 million - 1.5 * $3.00 million = $2.38 million

  • p100 = 2.5 * $3.75 million - 1.5 * $3.00 million = $4.88 million

If the p0 or p100 values fall outside allowable intervals (e.g., below 0), we can truncate the distribution at the desired boundaries using something like max(low.constraint, p0) or min(high.constraint, p100) .

My colleague, Eric Johnson, later replaced the piecewise quadratic spline with a piecewise linear because it was just easier to handle in this form. He also describes this approach in the book he recently coauthored, Handbook of Decision Analysis . 5

The following code provides the linear spline approach .

CalcBrownJohnson = function(minlim=-Inf, p10, p50, p90, maxlim=Inf, n, samponly=TRUE) {
# This function simulates a distribution from three quantile
# estimates for the probability intervals of the predicted outcome.
# p10 = the 10th percentile estimate
# p50 = the 50th percentile estimate
# p90 = the 90th percentile estimate
# n = the number of runs used in the simulation
# Note, this function strictly requires that p10 < p50 < p90.
# The process of simulation is simple Monte Carlo.
# The returned result is, by default, a vector of values for X if
# samponly=TRUE, else a (n x 2) matrix is returned such that the first
# column contains the domain samples in X, and the second column
# contains the uniform variate samples from U.
  if (p10 == p50 && p50 == p90) {
             return(rep(p50, n))
      } else if (p10 >= p50 || p50 >= p90) {
    stop("Parameters not given in the correct order. Must be
    given as p10 < p50 < p90.")
  } else {
#Create a uniform variate sample space in the interval (0,1).
    U <- runif(n, 0, 1)
# Calculates the virtual tails of the distribution given the p10, p50, p90
# inputs. Truncates the tails at the upper and lower limiting constraints.
    p0 <- max(minlim, 2.5 * p10 - 1.5 * p50)
    p100 <- min(maxlim, 2.5 * p90 - 1.5 * p50)
#This next section finds the linear coefficients of the system of linear
# equations that describe the linear spline, using linear algebra...
# [C](A) = (X)
# (A) = [C]^-1 * (X)
# In this case, the elements of (C) are found using the values (0, 0.1,
# 0.5, 0.9, 1) at the endpoints of each spline segment. The elements
# of (X) correspond to the values of (p0, p10, p10, p50, p50, p90,
# p90, p100). Solving for this system of linear equations gives linear
# coefficients that transform values in U to intermediate values in X.
# Because there are four segments in the linear spline, and each
# segment contains two unknowns, a total of eight equations are
# required to solve the system.
# The spline knot values in the X domain.
    knot.vector <- c(p0, p10, p10, p50, p50, p90, p90, p100)
# The solutions to the eight equations at the knot points required to
# describe the linear system.
    coeff.vals <- c(0, 1, 0, 0, 0, 0, 0, 0, 0.1, 1, 0, 0, 0, 0, 0, 0,
    0, 0, 0.1, 1, 0, 0, 0, 0, 0, 0, 0.5, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0.5,
    1, 0, 0, 0, 0, 0, 0, 0.9, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0.9, 1, 0, 0, 0,
    0, 0, 0, 1, 1)
# The coefficient matrix created from the prior vector looks like the
# following matrix:
  #   [, 1]   [, 2]   [, 3]   [, 4]   [, 5]   [, 6]   [, 7]  [, 8]
# [1,] 0.0     1.0     0.0     0.0     0.0     0.0     0.0    0.0
# [2,] 1.0     1.0     0.0     0.0     0.0     0.0     0.0    0.0
# [3,] 0.0     0.0     1.0     1.0     0.0     0.0     0.0    0.0
# [4,] 0.0     0.0     0.5     1.0     0.0     0.0     0.0    0.0
# [5,] 0.0     0.0     0.0     0.0     0.5     1.0     0.0    0.0
# [6,] 0.0     0.0     0.0     0.0     0.9     1.0     0.0    0.0
# [7,] 0.0     0.0     0.0     0.0     0.0     0.0     0.9    1.0
# [8,] 0.0     0.0     0.0     0.0     0.0     0.0     1.0    1.0
    coeff.matrix <- t(matrix(coeff.vals, nrow=8, ncol=8))
# The inverse of the coefficient matrix.
    inv.coeff.matrix <- solve(coeff.matrix)
# The solution vector of the linear coefficients.
    sol.vect <- inv.coeff.matrix %*% knot.vector
#Builds the response by the piecewise linear sections
    X <- (U <= 0.1) * (sol.vect[1, 1] * U + sol.vect[2, 1]) +
      (U > 0.1 & U <= 0.5) * (sol.vect[3, 1] * U + sol.vect[4, 1]) +
      (U > 0.5 & U <= 0.9) * (sol.vect[5, 1] * U + sol.vect[6, 1]) +
      (U > 0.9 & U <= 1) * (sol.vect[7, 1] * U + sol.vect[8, 1])
    if (samponly == TRUE) {
      return(X)
    } else {
      X <- array( c(X, U), dim=c(n, 2))
      return(X)
    }
  }
}
In this case, the resultant cumulative probability distribution in Figure 3-4 clearly demonstrates the linear spline that maps the uniform variate onto the production cost domain.
../images/461101_1_En_3_Chapter/461101_1_En_3_Fig4_HTML.jpg
Figure 3-4

The cumulative probability distribution of the production costs using the piecewise linear BrownJohnson generator

Lognormal Distribution

In some cases, using a known distribution is warranted, particularly when SMEs are assessing an outcome that they understand to conform to certain kinds of underlying, systematic processes, and a large body of evidence supports the selection of a given shape. For such situations in which the following are true, you should consider using a lognormal distribution.
  • Potential outcomes follow an asymmetric distribution.

  • The upper tail theoretically extends indefinitely.

  • The lower tail never extends below 0.

  • The effects that lead to the outcome are multiplicative in nature and not merely additive (i.e., not mean-reverting).

The lognormal distribution possesses the interesting characteristic that if two estimated quantile points are available that are symmetric around a p50 (e.g., the p10 and p90), then we can simulate the distribution easily from readily derived parameters that R recognizes for use in either the rnorm() or rlnorm() functions .

The following uses p10 and p90 estimates to generate a vector of normally distributed values in the log scale before returning the values in the measured domain. It uses the rnorm() function to do this.

CalcLognorm80N = function(p10, p90, samples) {
      lp10 <- log(p10)
      lp90 <- log(p90)
      p50 <- p10*sqrt(p90/p10)
      lp50 <- log(p50)
      lgs <- abs(lp90 - lp50)/qnorm(0.9, 0, 1)
      X <- rnorm(samples, lp50, lgs)
      return(exp(X))
}

This approach simply uses the rlnorm() function.

CalcLognorm80L = function(p10, p90, samples) {
      lp10 <- log(p10)
      lp90 <- log(p90)
      p50 <- p10*sqrt(p90/p10)
      lp50 <- log(p50)
      lgs <- abs(lp90 - lp50)/qnorm(0.9, 0, 1)
      X <- rlnorm(samples, lp50, lgs)
      return(X)
}

Which one you choose to use is entirely up to your discretion.

Using only the p10 and p90 values of the production costs estimates, we get the cumulative probability distributions shown in Figure 3-5 using both of the lognormal distribution functions.
../images/461101_1_En_3_Chapter/461101_1_En_3_Fig5_HTML.jpg
Figure 3-5

The cumulative probability distributions of the production costs using two slightly different approaches to implement a lognormal distribution generator

Note the high degree of coherence between them. We can attribute the small observable difference to simulation error.

When petroleum reservoir engineers use this type of function to estimate the amount of reserves in a field, they typically truncate the upper tail at the 99th percentile to accommodate the fact that a reservoir cannot be infinite in size, which the lognormal distribution would theoretically predict.

Now we compare all four distribution types in Figure 3-6.
../images/461101_1_En_3_Chapter/461101_1_En_3_Fig6_HTML.jpg
Figure 3-6

Comparison of the four example distribution generators discussed here

Modify the Influence Diagram to Reflect the Risk Layer

Now we need to modify the influence diagram to account for the competitive risk considerations.

To simplify the visual complexity of the diagram, we can collapse the operational expenses into a single node called Annual OPEX Module. Of course we can refer to the prior diagram to explain what the underlying components contain.

Whether RoadRunner appears on the market or not is represented by the node RoadRunner Comes to Market. On the condition that RoadRunner does successfully bring a product to market, they will do so over some period of time, reflected by the node Time Frame RoadRunner Comes to Market. Notice that in our diagram (Figure 3-7), now, we communicate our understanding that an uncertainty conditionally affects another uncertainty. This is also true of the Available Market Share, as it is conditionally related to the difference between RoadRunner’s time frame of development and the construction duration of Phase 1. Finally, we also indicate that the Price Discount will be conditionally related to RoadRunner Comes to Market.
../images/461101_1_En_3_Chapter/461101_1_En_3_Fig7_HTML.jpg
Figure 3-7

The influence diagram modified to account for competitive risk with operating expenses aggregated into one intermediate node called the Annual OPEX Module

Include the Run Index

Accommodating the risk layer of the model requires modifying our deterministic model in some important ways. First, we need to recognize that the single point p50 assumptions should be expanded to include an SME’s belief about the likely range of each variable through the entire simulation. Remember that the three columns p10, p50, and p90 are the cumulative probability quantiles representing the SMEs’ 80th percentile prediction interval for the outcome of our assumptions. Recall, too, that we will use the lines from the CSV data file (lines 15–19 in Figure 3-8) that contain the assumptions that reflect the competitive risk considerations.
../images/461101_1_En_3_Chapter/461101_1_En_3_Fig8_HTML.jpg
Figure 3-8

The data table used for the deterministic business case in .csv format rendered in a spreadsheet

The header lines of our new uncertainty-enabled R model ( Risk_Model.R ) look like this now:

# Read source data and function files. Modify the path names to match your
# directory structure and file names.
source("/Applications/R/RProjects/BizSimWithR/data/global_assumptions.R")
d.data <- read.csv("/Applications/R/RProjects/BizSimWithR/data/risk_assumptions.csv")
source("/Applications/R/RProjects/BizSimWithR/libraries/My_Functions.R")
# Slice the values from data frame d.data.
d.vals <- d.data[, 2:4]

Next we define a probability distribution for the variables that will change with each iteration of the simulation. For this example, I used the BrownJohnson distribution described earlier. Notice, too, that I limited the range of the distributions to truncate at natural boundaries using the pmin() and pmax() functions as necessary. For example, because the Phase 1 capital cannot be negative, I surrounded the p1.capex definition with pmax(..., 0) to keep any stray simulation samples from going below 0. For definitions that need to be constrained between 0 and 1, I nested the pmin() and pmax() functions, as in

early.market.share <- pmin( pmax(..., 0) 100).
# Assign values to variables using appropriate distributions.
p1.capex <- CalcBrownJohnson(0, d.vals[1, 1], d.vals[1, 2],
  d.vals[1, 3], , kSampsize)
p1.dur <- round(CalcBrownJohnson(1, d.vals[2, 1], d.vals[2, 2],
  d.vals[2, 3], , kSampsize), 0)
p2.capex <- CalcBrownJohnson(0, d.vals[3, 1], d.vals[3, 2],
  d.vals[3, 3], , kSampsize)
p2.dur <- round(CalcBrownJohnson(1, d.vals[4, 1], d.vals[4, 2],
  d.vals[4, 3], , kSampsize), 0)
maint.capex <- CalcBrownJohnson(0, d.vals[5, 1], d.vals[5, 2],
  d.vals[5, 3], , kSampsize)
fixed.prod.cost <- CalcBrownJohnson(0, d.vals[6, 1], d.vals[6, 2],
  d.vals[6, 3], , kSampsize)
prod.cost.escal <- CalcBrownJohnson( , d.vals[7, 1], d.vals[7, 2],
  d.vals[7, 3], , kSampsize)
var.prod.cost <- CalcBrownJohnson(0, d.vals[8, 1], d.vals[8, 2],
  d.vals[8, 3], , kSampsize)
var.cost.redux <- CalcBrownJohnson( , d.vals[9, 1], d.vals[9, 2],
  d.vals[9, 3], , kSampsize)
gsa.rate <- CalcBrownJohnson(0, d.vals[10, 1], d.vals[10, 2],
  d.vals[10, 3], 100, kSampsize)
time.to.peak.sales <- round(CalcBrownJohnson(1, d.vals[11, 1],
  d.vals[11, 2], d.vals[11, 3], ,kSampsize), 0)
mkt.demand <- CalcBrownJohnson(0, d.vals[12, 1], d.vals[12, 2],
  d.vals[12, 3], ,kSampsize)
price <- CalcBrownJohnson(0, d.vals[13, 1], d.vals[13, 2],
  d.vals[13, 3], ,kSampsize)
rr.comes.to.market <- rbinom(kSampsize, 1, d.vals[14, 2])
rr.time.to.market <- round(CalcBrownJohnson(1, d.vals[15, 1],
  d.vals[15, 2], d.vals[15, 3], ,kSampsize))
early.market.share <- CalcBrownJohnson(0, d.vals[16, 1],
  d.vals[16, 2], d.vals[16, 3], 100, kSampsize)
late.market.share <- CalcBrownJohnson(0, d.vals[17, 1],
  d.vals[17, 2], d.vals[17, 3], 100,kSampsize)
price.redux <- CalcBrownJohnson(0, d.vals[18, 1], d.vals[18, 2],
  d.vals[18, 3], ,kSampsize)

The first few samples from p1.capex look like this:

[1] 43959855 56002835 35500058 36685885 40846491 44401842 40763622 38461247 38454413
[10] 39002984 37501273 37931937 43875385 36893128 29870863 36169428 40210438 44370703

After you have set the variables in your own code file, load the source into the R console and run a few of the variables to get a feel for how their results appear and make sure that all results conform to the distributions’ definitions. Anomalies will indicate that you have possibly misdefined a variable. Of course, they can also indicate that your definition, although working just fine, is producing results that do not conform to the context of the business case. Instead of repairing the definition, you will likely need to rethink the definition employed or reassess the inputs.

We need to make one exception to using the BrownJohnson distribution for rr.comes.to.market , which represents the probability that RoadRunner comes to market. Although a broad discussion about the nature of probabilities is beyond the scope of this document, I maintain here without discussion that probabilities are not expressed as distributions. Only the uncertainty about event outcomes or measurable conditions are expressed as distributions. Because an event either occurs or not, it makes sense to describe a binary event with a single probability.6 The appropriate distribution for this is the Bernoulli distribution, which is a special limiting case of the binomial distribution. So, for rr.comes.to.market , we can define it as rbinom(kSampsize, 1, d.vals[14, 2]), where the prob parameter is satisfied with the corresponding probability value in the CSV data file.

You will notice, however, that I placed a value of 0 for the rr.comes.to.market p10, and 1 for the p90. This is not to indicate an uncertainty about the probability in question. Rather, I use these values here to serve as placeholders to compare the effects in later sensitivity analysis of the value of no competition (rr.comes.to.market = 0) to being certain that competition will occur (rr.comes.to.market = 1).

Now, each variable defined earlier will simulate a series of outcomes. The length of each series is kSampsize . You can think of these series as existing in the run index. To run our simulation model, we want our R code to select a single value from each variable starting at run = 1, step through all the calculations in the same order as the deterministic model in the last chapter, and then repeat the process through each step in the run index until our simulation reaches run = kSampsize. Essentially, we want the sample values in the run index to operate orthogonally to the year index .

This now presents a little problem, though, because of R’s vectorizing and recycling nature, which is usually a very powerful characteristic of the language. We naturally want to think of the run index as being orthogonal to the year index, but unless we take some steps to force that condition, R will recycle the values in the run index of each variable through the year index, completely messing up the indexing of the resulting calculations. As you might have guessed, we can use the sapply() function to address this issue.

To run the simulation, we wrap each variable calculation from our original deterministic model with the sapply() function, starting at the CAPEX module to the NPV, in which arrays of different shapes are applied to each other. Also, because sapply() wants to reorient the resulting calculations such that the year index becomes rows and the run index becomes columns, we transpose each calculation with the t() function to maintain the calculations in the run x year orientation so that we can easily read results of intermediate calculations in the R console when the run index is longer than the year index , as will most likely always be the case.

# CAPEX module
phase <- t(sapply(run, function(r) (year <= p1.dur[r]) * 1 +
  (year > p1.dur[r] & year <= (p1.dur[r] + p2.dur[r])) * 2 +
  (year > (p1.dur[r] + p2.dur[r])) *3))
capex <- t(sapply(run, function(r) (phase[r, ] == 1) * p1.capex[r]/p1.dur[r] +
  (phase[r, ] == 2) * p2.capex[r]/p2.dur[r] +
  (phase[r, ] == 3) * maint.capex[r]))
# Depreciation module
depr.matrix <- array(sapply(run, function(r) sapply(year, function(y)
ifelse(y <= p1.dur[r] & year>0, 0,
  ifelse(y == (p1.dur[r]+1) & year<y+kDeprPer & year>=y, p1.capex[r] / kDeprPer,
    ifelse((year >= y) & (year < (y + kDeprPer)), capex[r, y - 1] / kDeprPer, 0)
    )
  )
)), dim=c(kHorizon, kHorizon, kSampsize))
depr <- t(sapply(run, function(r) sapply(year, function(y)
  sum(depr.matrix[y, , r]))))
# Competition module
market.share <- (rr.comes.to.market ==1) * ((rr.time.to.market <= p1.dur) *
  early.market.share/100 + (rr.time.to.market > p1.dur) *
    late.market.share/100 ) +
  (rr.comes.to.market == 0)*1
# Sales module
mkt.adoption <- t(sapply(run, function(r) market.share[r] *
  pmin(cumsum(phase[r, ] > 1) / time.to.peak.sales[r], 1)))
sales <- t(sapply(run, function(r) mkt.adoption[r, ] * mkt.demand[r] *
  1000 * 2000))
revenue <- t(sapply(run, function(r) sales[r, ] * price[r] *
  (1 - rr.comes.to.market[r] * price.redux[r]/100)))
# OPEX module
fixed.cost <- t(sapply(run, function(r) (phase[r, ] > 1) * fixed.prod.cost[r] *
  (1 + prod.cost.escal[r]/100)^(year - p1.dur[r] -1)))
var.cost <- t(sapply(run, function(r) var.prod.cost[r] *
  (1 - var.cost.redux[r]/100)^(year - p1.dur[r] -1 ) * sales[r, ]))
gsa <- t(sapply(run, function(r) (gsa.rate[r]/100) * revenue[r, ]))
opex <- fixed.cost + var.cost
# Value
gross.profit <- revenue - gsa
op.profit.before.tax <- gross.profit - opex - depr
tax <- op.profit.before.tax * kTaxRate/100
op.profit.after.tax <- op.profit.before.tax - tax
cash.flow <- op.profit.after.tax + depr - capex
cum.cash.flow <- t(sapply(run, function(r) cumsum(cash.flow[r, ])))
# Following the convention for when payments are counted as occurring
# at the end of a time period.
discount.factors <- 1/(1 + kDiscountRate/100)^year
discounted.cash.flow <- t(sapply(run, function(r) cash.flow[r, ] *
  discount.factors))
npv <- sapply(run, function(r) sum(discounted.cash.flow[r, ]))
mean.npv <- mean(npv)

As we discussed earlier in the development of the depreciation matrix in Chapter 2, the sapply() function effectively iterates across an index just like a for loop, except it does so in one line of code. For example with phase, as R steps through the run index, a sample value from each of p1.dur and p2.dur is selected out of their respective distribution arrays, and the logic performed against the year index is as if the deterministic model were in play. When the calculation is complete, the resultant value is placed in the rth row of the phase array . The logic is similar for capex and all the other intermediate calculations through mean.npv . You can follow the entire R code for the risk model in Appendix B.

Once we reach NPV simulation, we can easily find the mean NPV:

mean.npv <- mean(npv)

No other function is required but the mean() function because all indexes but the run index have been reduced out, leaving only the samples in run. The mean() function simply finds the average of all the values in a given vector. The result gives us a depressing -$5,793,909. If we made decisions based on average values alone, we would stop here and go no further with our analysis. However, Chapter 4 will show us that there might yet be a reason to consider how to improve the value of this opportunity to our economic benefit.

When I calculate the mean NPV, I get -$5,793,909 on this particular run of the model. Running the model again, I get -$5,451,535. You will notice this same effect as well because of the simulation error inherent in simple Monte Carlo routines. One way to overcome this error, other than employing more advanced simulation routines (e.g., median Latin hypercube sampling), is to use more samples. We defined the sample size in our global_assumptions.R file as kSampsize <- 1000. Just change this value to a larger number to observe that the predicted mean NPV converges to a more stable position over several runs. On my computer, 1,000 iterations requires 1.542 seconds. I determined this value using the system.time() function.

system.time({source("/Applications/R/RProjects/BizSimWithR/Risk_Model.R")})

Changing kSampsize to 5,000 requires 7.307 seconds. The desire for improved precision comes at the price of a slightly longer runtime.

Just to illustrate how powerful the vectorizing sapply() function is compared to running the risk model in one big for loop, a similar model constructed in a for loop requires 2.701 seconds using a sample size of 1,000—an increase of 1.75-fold. The simulation using 5,000 samples requires 31.237 seconds, a greater than fourfold increase over the model using the sapply() function , using the same number of samples. Of course, the exact numbers you get will vary by your computing configuration (i.e., operating system, processor speed, RAM), but the relative differences between the vector versus the looping approach should be similar.

Moving along, we can follow the same sapply() logic in the construction of the pro forma statement based on the mean values of the pro forma elements.

# Pro forma
# Create a data frame of the variables' mean values to be used in the pro
# forma.
pro.forma.vars <- array(
  c(
    sales,
    revenue,
    -gsa,
    gross.profit,-fixed.cost,
    -var.cost,
    -opex,
    -depr,
    op.profit.before.tax,
    -tax,
    op.profit.after.tax,
    depr,
    -capex,
    cash.flow
  ),
  dim = c(kSampsize, kHorizon, 14)
)
# Finds the annual mean of each pro forma element.
mean.pro.forma.vars <- array(0, c(14, kHorizon))
for (p in 1:14) {
  mean.pro.forma.vars[p,] <- sapply(year, function(y)
    mean(pro.forma.vars[, y, p]))
}
pro.forma <- data.frame(mean.pro.forma.vars)
# Assign text names to a vector. These will be the column headers of
# the data frame.
pro.forma.headers <-
  c(
    "Sales [lbs]",
    "Revenue",
    "GS&A",
    "Gross Profit",
    "Fixed Cost",
    "Variable Cost",
    "OPEX",
    "-Depreciation",
    "Operating Profit Before Tax",
    "Tax",
    "Operating Profit After Tax",
    "+Depreciation",
    "CAPEX",
    "Cash Flow"
  )
# Coerces the default column headers to be the headers we like.
colnames(pro.forma) <- year
rownames(pro.forma) <- pro.forma.headers

We will discuss the preparation for graphing of the cash flow and cumulative cash flow with confidence bands using the sapply() function in the next chapter.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.224.56.216