The following is an excerpt from Cautionary Tales in Designed Experiments by David Salsburg. This book is available to download for free from SAS Press. The book aims to explain statistical design of experiments (DOE) to readers with minimal mathematical knowledge and skills. In this excerpt, you will learn about the origin of Thomas Bayes’ Theorem, which is the basis for Bayesian analysis.

Source: Wikipedia

The Reverend Thomas Bayes (1702–1761) was a dissenting minister of the Anglican Church, which means he did not subscribe to the full body of doctrine espoused by the Church. We know of Bayes in the 21st century, not because of his doctrinal beliefs, but because of a mathematical discovery, which he thought made no sense whatsoever. To understand Bayes’ Theorem, we need to refer to this question of the meaning of probability.

In the 1930s, the Russian mathematician Andrey Kolomogorov (1904–1987) proved that probability was a measure on a space of “events.” It is a measure, just like area, that can be computed and compared. To prove a theorem about probability, one only needed to draw a rectangle to represent all possible events associated with the problem at hand. Regions of that rectangle represent classes of sub-events.

For instance, in Figure 1, the region labeled “C” covers all the ways in which some event, C, can occur. The probability of C is the area of the region C, divided by the area of the entire rectangle. Anticipating Kolomogorov’s proof, John Venn (1834–1923) had produced such diagrams (now called “Venn diagrams”).

Figure 1: Venn Diagram for Events C and D

Figure 1 shows a Venn diagram for the following situation: We have a quiet wooded area. The event C is that someone will walk through those woods sometime in the next 48 hours. There are many ways in which this can happen. The person might walk in from different entrances and be any of a large number of people living nearby. For this reason, the event C is not a single point, but a region of the set of all possibilities. The event D is that the Toreador Song from the opera Carmen will resound through the woods. Just as with event C, there are a number of ways in which this could happen. It could be whistled or sung aloud by someone walking through the woods, or it could have originated from outside the woods, perhaps from a car radio on a nearby street. Some of these possible events are associated with someone walking through the woods, and those possible events are in the overlap between the regions C and D. Events associated with the sound of the Toreador Song that originate outside the woods are in the part of region D that does not overlap region C.

The area of region C (which we can write P(C) and read it as “P of C”) is the probability that someone will walk through the woods. The area of region D (which we can write P(D)) is the probability that the Toreador Song will be heard in the woods. The area of the overlap between C and D (which we can write P(C and D) is the probability that someone will walk through the woods and that the Toreador Song will be heard.

If we take the area P(C and D) and divide it by the area P(C), we have the probability that the Toreador Song will be heard when someone walks through the woods. This is called the conditional probability of D, given C. In symbols

P(D|C) = P(C and D)÷ P(C)

Some people claim that if the conditional probability, P(C|D), is high, then we can state “D causes C.” But this would get us into the entangled philosophical problem of the meaning of “cause and effect.”

To Thomas Bayes, conditional probability meant just that—cause and effect. The conditioning event, C, (someone will walk through the woods in the next 48 hours) comes before the second event D, (the Toreador Song is heard). This made sense to Bayes. It created a measure of the probability for D when C came before.

However, Bayes’ mathematical intuition saw the symmetry that lay in the formula for conditional probability:

P(D|C) = P(D and C)÷ P(C) means that

P(D|C)P(C) = P(D and C) (multiply both sides of the equation by P(C)).

But just manipulating the symbols shows that, in addition,

P(D and C) = P(C|D) P(D), or

P(C|D) = P(C and D)÷ P(D).

This made no sense to Bayes. The event C (someone walks through the woods) occurred first. It had already happened or not before event D (the Toreador Song is heard). If D is a consequence of C, you cannot have a probability of C, given D. The event that occurred second cannot “cause” the event that came before it. He put these calculations aside and never sent them to the Royal Society. After his death, friends of Bayes discovered these notes and only then were they sent to be read before the Royal Society of London. Thus did Thomas Bayes, the dissenting minister, become famous—not for his finely reasoned dissents from church doctrine, not for his meticulous calculations of minor problems in astronomy, but for his discovery of a formula that he felt was pure nonsense.

P(C|D) P(D) = P(C and D) = P(D|C) P(C)

For the rest of the 18th century and for much of the 19th century, Bayes’ Theorem was treated with disdain by mathematicians and scientists. They called it “inverse probability.” If it was used at all, it was as a mathematical trick to get around some difficult problem. But since the 1930s, Bayes’ Theorem has proved to be an important element in the statistician’s bag of “tricks.”

Bayes saw his theorem as implying that an event that comes first “causes” an event that comes after with a certain probability, and an event that comes after “causes” an event that came “before” (foolish idea) with another probability. If you think of Bayes’ Theorem as providing a means of improving on prior knowledge using the data available, then it does make sense.

In experimental design, Bayes’ Theorem has proven very useful when the experimenter has some prior knowledge and wants to incorporate that into his or her design. In general, Bayes’ Theorem allows the experimenter to go beyond the experiment with the concept that experiments are a means of continuing to develop scientific knowledge.

Thomas Bayes’ theorem and “inverse probability” was published on SAS Users.

What are the odds of winning the lottery?  This seems like a simple question (and yes, there is a simple answer), but there are a few technical details to work out first... Which lottery?  Let's say the Powerball Lottery. When? The number of balls used in the Powerball lottery has [...]

Christian Haxholdt, an analytics consultant in Global Professional Services and Delivery, has a passion for probability. Originally from Denmark, Christian is a former professor of statistics, and although he no longer teaches, he’s still an avid learner. Read on for his spirited interview, and be sure to check out the [...]

The so-called birthday paradox or birthday problem is simply the counter-intutitive discovery that the probability of (at least) two people in a group sharing a birthday goes up surprisingly fast as the group size increases. If the group is only 23 people, there is a 50% chance that two of them share a birthday, and with 40 people it's about 90%. There is an excellent wikipedia page discussing this.

However, this analytically derived probability is based on the assumption that births are equally likely on any day of the year. (It also ignores the occasional February 29th, and any social factors that lead people born at the same time of year to seek like spouses, and so forth.) But this assumption does not appear to be true, as laid out anecdotally and in press.

As noted in the latter link, any disparity in the probability of birth between days will improve the chances of a match. But how much? An analytic solution seems quite complex, even if we approximate the true daily distribution with a constant birth probability per month. Simulation will be simpler. While we're at it, we'll include leap days as well, since February 29th approaches.

SAS

Our approach here is based on the observation that the probability of at least one match among N people is equal to the sum of the probabilities of exactly one match in 2,...,N people. In addition, rather than simulating groups of 2, estimating the probability of a match, and repeating for groups of 3,...,N, we'll keep adding people to a group until we have a match, finding the probability of a match in all group sizes at once.

Here we use arrays (section 1.11.5) to keep track of the number of days in a month and of the people in our group. To reduce computation, we'll check for matches as we add people to the group, and only generate their birthdays if there is not yet a match. We also demonstrate the useful hyphen tool for referring to ranges of variables (1.11.4).
`data bd1;array daysmo [12] _temporary_  (31 28.25 31 30 31 30 31 31 30 31 30 31);array dob [367] dob1 - dob367;  * these variables will hold the birthdays                                * the hyphen includes all the variables in the                                * sequencedo group = 1 to 10000000;       * simulate this many groups;  match = 0;                    * initialize whether there's a match in this                                   group, yet;  do i = 1 to 367;              * loop through up to 367 subjects... the maximum                                  possible, obviously;    month = rantbl(0, 31*.0026123, 28*.0026785, 31*.0026838, 30*.0026426,        31*.0026702, 30*.0027424, 31*.0028655, 31*.0028954, 30*.0029407,        31*.0027705, 30*.0026842);                                * choose a month of birth, by probabilities reported                                  in the Science News link, which are daily by month;    day = ceil((4 * daysmo[month] * uniform(0))/4);                                  * choose a day within the month,                                  note the trick used to get Leap Days;     dob[i] = mdy(month, day, 1960);                                * convert month and day into a day in the year--                                  1960 is a convenient leap year; do j = 1 to (i-1) until (match gt 0);                                * compare each old person to the new one;   if dob[j] = dob[i] then match = i;                                * if there was a match, we needed i people in the                                   group to make it;   end; if match gt 0 then leave;                                  * no need to generate the other 367-i people;  end;  output;end;run;`

We note here that while we allow up to 367 birthdays before a match, the probability of more than 150 is so infinitesimal that we could save the space and speed up processing time by ignoring it. Now that the groups have been simulated, we just need to summarize and present them. We tabulate how many cases of groups of size N were recorded, generate the simple analytic answer, and merge them.
`proc freq data = bd1;tables match / out=bd2 outcum;  * the bd2 data set has the results;run;data simpreal;set bd2;prob = 1 - ((fact(match) * comb (365,match)) / 365**match);realprob = cum_freq/10000000;diff = realprob-prob;diffpct = 100 * (diff)/prob;run;`

It's easiest to interpret the results by way of a plot. We'll plot the absolute and the relative difference on the same image with two different axes. The axis and symbol statements will make it slightly prettier, and allow us to make 0 appear at the same point on both axes.
`axis1 order = (-.75 to .75 by .25) minor = none;axis2 order = (-.00025 to .00025 by .00005) minor = none;symbol1 v = dot h = .75 c = blue;symbol2 font=marker v = U h = .5 c = red;proc gplot data= simpreal (obs = 89);plot diffpct * match / vref = 0 vaxis=axis1 legend;plot2 diff *match/ vaxis = axis2 legend;run; quit;`

The results, shown below, are very clear-- leap day and the disequilibrium in birth month probability does increase the probability of at least one match in any group of a given size, relative to the uniform distribution across days assumed in the analytic solution. But the difference is miniscule in both the absolute and relative scale.

R
Here we mimic the approach used above, but use the apply() function family in place of some of the looping.
`dayprobs = c(.0026123,.0026785,.0026838,.0026426,.0026702,.0027424,.0028655,    .0028954,.0029407,.0027705,.0026842,.0026864)daysmo = c(31,28,31,30,31,30,31,31,30,31,30,31)daysmo2 = c(31,28.25,31,30,31,30,31,31,30,31,30,31)# need both: the former is how the probs are reported, # while the latter allows leap daysmoprob = daysmo * dayprobs`

With the monthly probabilities established, we can sample a birth month for everyone, and then choose a birth day within month. We use the same trick as above to allow birth days of February 29th. Here we show code for 10,000 groups; with the simple cloud R this code was developed on, more caused a crash.

We've stopped referencing our book exhaustively, and doing so here would be tedious. Instead, we'll just comment that the tools we use here can be found in sections 1.4.5, 1.4.15, 1.4.16, 1.5.2, 1.8.3, 1.8.4, 1.9.1, 1.11.1, 5.2.1, 5.6.1, B.5.2, and probably others.
`mob = sample(1:12,10000 * 367,rep=TRUE,prob=moprob)dob = sapply(mob,function(x) ceiling(sample((4*daysmo2[x]),1)/4) )# The ceiling() function isn't vectorized, so we make the equivalent# using sapply().mobdob = paste(mob,dob)# concatenate the month and day to make a single variable to compare# between people.  The ISOdate() function would approximate the SAS mdy() # function but would be much longer, and we don't need it.# convert the vector into a matrix with the maximum# group size as the number of columns# as noted above, this could safely be truncated, with great savingsmdmat = matrix(mobdob, ncol=367, nrow=10000)`

To find duplicate birthdays in each row of the matrix, we'll write a function to compare the number of unique values with the length of the vector, then call it repeatedly in a for() loop until there is a difference. Then, to save (a lot) of computations, we'll break out of the loop and report the number needed to make the match. Finally, we'll call this vector-based function using apply() to perform it on each row of the birthday matrix.
`matchn = function(x) {  for (i in 1:367){    if (length(unique(x[1:i])) != i) break  }  return(i)}groups = apply(mdmat, 1, matchn)bdprobs = cumsum(table(groups)/10000)# find the N with each group number, divide by number of groups# and get the cumulative sumrgroups = as.numeric(names(bdprobs))# extract the group sizes from the tableprobs = 1 - ((factorial(rgroups) * choose(365,rgroups)) / 365**rgroups)# calculate the analytic answer, for any group size # for which there was an observed simulated valuediffs = bdprobs - probsdiffpcts = diffs/probs`

To plot the differences and percent differences in probabilities, we modify (slightly) the functions for a multiple-axis scatterplot we show in our book in section 5.6.1. You can find the code for this and all the book examples on the book web site.
`addsecondy <- function(x, y, origy, yname="Y2") {  prevlimits <- range(origy)  axislimits <- range(y)  axis(side=4, at=prevlimits[1] + diff(prevlimits)*c(0:5)/5,       labels=round(axislimits[1] + diff(axislimits)*c(0:5)/5, 3))  mtext(yname, side=4)  newy <- (y-axislimits[1])/(diff(axislimits)/diff(prevlimits)) +    prevlimits[1]  points(x, newy, pch=2)  abline(h=(-axislimits[1])/(diff(axislimits)/diff(prevlimits)) +    prevlimits[1])}plottwoy <- function(x, y1, y2, xname="X", y1name="Y1", y2name="Y2"){  plot(x, y1, ylab=y1name, xlab=xname)  abline(h=0)  addsecondy(x, y2, y1, yname=y2name)}plottwoy(rgroups, diffs, diffpcts, xname="Number in group",  y1name="Diff in prob", y2name="Diff in percent")legend(80, .0013, pch=1:2, legend=c("Diffs", "Pcts"))`

The resulting plot, (which is based on 100,000 groups, tolerable compute time on a laptop) is shown at the top. Aligning the 0 on each axis was more of a hassle than seemed worth it for today. However, the message is equally clear-- a clearly larger probability with the observed birth distribution, but not a meaningful difference.

We often simulate data in SAS or R to confirm analytical results. For example, consider the following problem from the excellent text by Rice:

Let U1, U2, and U3 be independent random variables uniform on [0, 1]. What is the probability that the roots of the quadratic U1*x^2 + U2*x + U3 are real?

Recall that for a quadratic equation A*x^2 + B*x + C to have real roots we need the discriminant (B^2-4AC) to be non-negative.

The answer given in the second and third editions of Rice is 1/9. Here's how you might get there:

Since, B^2 > 4*A*C <=> B > 2*sqrt(A*C), we need to integrate B over the range 2*sqrt(a*c) to 1, then integrate over all possible values for A and C (each from 0 to 1).

Another answer can be found by taking y = b^2 and w = 4ac and integrating over their joint distribution (they're independent, of course). That leads to an answer of approximately 0.254. Here's how to calculate this in R:
`f = function(x) {  A = x[1]; B = x[2]; C = x[3];  return(as.numeric(B^2 > 4*A*C))}library(cubature)adaptIntegrate(f, c(0,0,0), c(1,1,1), tol=0.0001, max=1000000)`

which generates the following output:
`\$integral[1] 0.2543692\$error[1] 0.005612558\$functionEvaluations[1] 999999\$returnCode[1] -1`

We leave the details of the calculations aside for now, but both seem equally plausible, at first glance. A quick simulation can suggest which is correct.

For those who want more details, here's a more complete summary of this problem and solution.

SAS

Neither the SAS nor the R code is especially challenging.

`data test;  do trial = 1 to 10000;    u1 = uniform(0); u2 = uniform(0); u3 = uniform(0);    res = u2**2 - 4*u1*u3;    realroot = (res ge 0);    output;  end;run;proc print data=test (obs=10); run;proc means data=test;  var realroot;run;`

`                        The MEANS Procedure                   Analysis Variable : realroot     N           Mean        Std Dev        Minimum        Maximum ----------------------------------------------------------------- 10000      0.2556000      0.4362197              0      1.0000000 -----------------------------------------------------------------`

R

`numsim = 10000u1 = runif(numsim); u2 = runif(numsim); u3 = runif(numsim)res = u2^2 - 4*u1*u3realroots = res>=0table(realroots)/numsim`

With the result
`realrootsFALSE  TRUE 0.747 0.253 `

The simulation demonstrates both that the first solution is incorrect. Here the simulation serves as a valuable check for complicated analysis.

Insights into where the 1/9 solution fails would be welcomed in the comments.

We often simulate data in SAS or R to confirm analytical results. For example, consider the following problem from the excellent text by Rice:

Let U1, U2, and U3 be independent random variables uniform on [0, 1]. What is the probability that the roots of the quadratic U1*x^2 + U2*x + U3 are real?

Recall that for a quadratic equation A*x^2 + B*x + C to have real roots we need the discriminant (B^2-4AC) to be non-negative.

The answer given in the second and third editions of Rice is 1/9. Here's how you might get there:

Since, B^2 > 4*A*C <=> B > 2*sqrt(A*C), we need to integrate B over the range 2*sqrt(a*c) to 1, then integrate over all possible values for A and C (each from 0 to 1).

Another answer can be found by taking y = b^2 and w = 4ac and integrating over their joint distribution (they're independent, of course). That leads to an answer of approximately 0.254. Here's how to calculate this in R:
`f = function(x) {  A = x[1]; B = x[2]; C = x[3];  return(as.numeric(B^2 > 4*A*C))}library(cubature)adaptIntegrate(f, c(0,0,0), c(1,1,1), tol=0.0001, max=1000000)`

which generates the following output:
`\$integral[1] 0.2543692\$error[1] 0.005612558\$functionEvaluations[1] 999999\$returnCode[1] -1`

We leave the details of the calculations aside for now, but both seem equally plausible, at first glance. A quick simulation can suggest which is correct.

For those who want more details, here's a more complete summary of this problem and solution.

SAS

Neither the SAS nor the R code is especially challenging.

`data test;  do trial = 1 to 10000;    u1 = uniform(0); u2 = uniform(0); u3 = uniform(0);    res = u2**2 - 4*u1*u3;    realroot = (res ge 0);    output;  end;run;proc print data=test (obs=10); run;proc means data=test;  var realroot;run;`

`                        The MEANS Procedure                   Analysis Variable : realroot     N           Mean        Std Dev        Minimum        Maximum ----------------------------------------------------------------- 10000      0.2556000      0.4362197              0      1.0000000 -----------------------------------------------------------------`
`numsim = 10000u1 = runif(numsim); u2 = runif(numsim); u3 = runif(numsim)res = u2^2 - 4*u1*u3realroots = res>=0table(realroots)/numsim`
`realrootsFALSE  TRUE 0.747 0.253 `