Probabilistic Programming
=====
and Bayesian Methods for Hackers
========

##### Version 0.1

`Original content created by Cam Davidson-Pilon`

`Ported to Python 3 and PyMC3 by Max Margenot (@clean_utensils) and Thomas Wiecki (@twiecki) at Quantopian (@quantopian)`
___

Welcome to *Bayesian Methods for Hackers*. The full Github repository is available at [github/Probabilistic-Programming-and-Bayesian-Methods-for-Hackers](https://github.com/CamDavidsonPilon/Probabilistic-Programming-and-Bayesian-Methods-for-Hackers). The other chapters can be found on the project's [homepage](https://camdavidsonpilon.github.io/Probabilistic-Programming-and-Bayesian-Methods-for-Hackers/). We hope you enjoy the book, and we encourage any contributions!

Chapter 1
======
***

The Philosophy of Bayesian Inference
------

> You are a skilled programmer, but bugs still slip into your code. After a particularly difficult implementation of an algorithm, you decide to test your code on a trivial example. It passes. You test the code on a harder problem. It passes once again. And it passes the next, *even more difficult*, test too! You are starting to believe that there may be no bugs in this code...

If you think this way, then congratulations, you already are thinking Bayesian! **Bayesian inference is simply updating your beliefs after considering new evidence.** A Bayesian can rarely be certain about a result, but he or she can be very confident. Just like in the example above, **we can never be 100% sure that our code is bug-free unless we test it on every possible problem**; something rarely possible in practice. Instead, we can test it on a large number of problems, and if it succeeds we can feel more *confident* about our code, but still not certain.  Bayesian inference works identically: we update our beliefs about an outcome; rarely can we be absolutely sure unless we rule out all other alternatives.

### The Bayesian state of mind

Bayesian inference differs from more traditional statistical inference by **preserving *uncertainty***. At first, this sounds like a bad statistical technique. Isn't statistics all about deriving *certainty* from randomness? To reconcile this, we need to start thinking like Bayesians.

The Bayesian world-view interprets probability as measure of *believability in an event*, that is, how confident we are in an event occurring. In fact, we will see in a moment that this is the natural interpretation of probability.

For this to be clearer, we consider an alternative interpretation of probability: ***Frequentist*, known as the more *classical* version of statistics, assume that probability is the long-run frequency of events (hence the bestowed title)**. For example, the *probability of plane accidents* under a frequentist philosophy is interpreted as the *long-term frequency of plane accidents*. This makes logical sense for many probabilities of events, but becomes more difficult to understand when events have no long-term frequency of occurrences. Consider: we often assign probabilities to outcomes of presidential elections, but the election itself only happens once! Frequentists get around this by invoking alternative realities and saying across all these realities, the frequency of occurrences defines the probability.

Bayesians, on the other hand, have a more intuitive approach. Bayesians interpret a probability as measure of ***belief***, or **confidence**, of an event occurring. Simply, a probability is a summary of an opinion. An individual who assigns a belief of 0 to an event has no confidence that the event will occur; conversely, assigning a belief of 1 implies that the individual is absolutely certain of an event occurring. Beliefs between 0 and 1 allow for weightings of other outcomes. This definition agrees with the probability of a plane accident example, for having observed the frequency of plane accidents, an individual's belief should be equal to that frequency, excluding any outside information. Similarly, under this definition of probability being equal to beliefs, it is meaningful to speak about probabilities (beliefs) of presidential election outcomes: how confident are you candidate *A* will win?

Notice in the paragraph above, **I assigned the belief (probability) measure to an *individual*, not to Nature**. This is very interesting, as this definition leaves room for conflicting beliefs between individuals. Again, this is appropriate for what naturally occurs: different individuals have different beliefs of events occurring, because they possess different *information* about the world. The existence of different beliefs does not imply that anyone is wrong. Consider the following examples demonstrating the relationship between individual beliefs and probabilities:

- I flip a coin, and we both guess the result. We would both agree, assuming the coin is fair, that the probability of Heads is 1/2. Assume, then, that I peek at the coin. Now I know for certain what the result is: I assign probability 1.0 to either Heads or Tails (whichever it is). Now what is *your* belief that the coin is Heads? My knowledge of the outcome has not changed the coin's results. Thus we assign different probabilities to the result.

-  Your code either has a bug in it or not, but we do not know for certain which is true, though we have a belief about the presence or absence of a bug.

-  A medical patient is exhibiting symptoms $x$, $y$ and $z$. There are a number of diseases that could be causing all of them, but only a single disease is present. A doctor has beliefs about which disease, but a second doctor may have slightly different beliefs.

This philosophy of treating beliefs as probability is natural to humans. We employ it constantly as we interact with the world and only see partial truths, but gather evidence to form beliefs. Alternatively, you have to be *trained* to think like a frequentist.

To align ourselves with traditional probability notation, **we denote our belief about event $A$ as $P(A)$. We call this quantity the *prior probability***.

John Maynard Keynes, a great economist and thinker, said "When the facts change, I change my mind. What do you do, sir?" This quote reflects the way a Bayesian updates his or her beliefs after seeing evidence. Even — especially — if the evidence is counter to what was initially believed, the evidence cannot be ignored. We denote our updated belief as $P(A |X )$, interpreted as the probability of $A$ given the evidence $X$. We call the updated belief the *posterior probability* so as to contrast it with the prior probability. For example, consider the posterior probabilities (read: posterior beliefs) of the above examples, after observing some evidence $X$:

1\. $P(A): \;\;$ the coin has a 50 percent chance of being Heads. $P(A | X):\;\;$ You look at the coin, observe a Heads has landed, denote this information $X$, and trivially assign probability 1.0 to Heads and 0.0 to Tails.

2\.   $P(A): \;\;$  This big, complex code likely has a bug in it. $P(A | X): \;\;$ The code passed all $X$ tests; there still might be a bug, but its presence is less likely now.

3\.  $P(A):\;\;$ The patient could have any number of diseases. $P(A | X):\;\;$ Performing a blood test generated evidence $X$, ruling out some of the possible diseases from consideration.

**It's clear that in each example we did not completely discard the prior belief after seeing new evidence $X$, but we *re-weighted the prior* to incorporate the new evidence** (i.e. we put more weight, or confidence, on some beliefs versus others).

By introducing prior uncertainty about events, we are already admitting that any guess we make is potentially very wrong. After observing data, evidence, or other information, we update our beliefs, and our guess becomes *less wrong*. This is the alternative side of the prediction coin, where typically we try to be *more right*.

### Bayesian Inference in Practice

 If frequentist and Bayesian inference were programming functions, with inputs being statistical problems, then the two would be different in what they return to the user. The frequentist inference function would return a number, representing an estimate (typically a summary statistic like the sample average etc.), whereas the Bayesian function would return *probabilities*.

For example, in our debugging problem above, calling the frequentist function with the argument "My code passed all $X$ tests; is my code bug-free?" would return a *YES*. On the other hand, asking our Bayesian function "Often my code has bugs. My code passed all $X$ tests; is my code bug-free?" would return something very different: probabilities of *YES* and *NO*. The function might return:

>    *YES*, with probability 0.8; *NO*, with probability 0.2

This is very different from the answer the frequentist function returned. Notice that the Bayesian function accepted an additional argument:  *"Often my code has bugs"*. This parameter is the *prior*. By including the prior parameter, we are telling the Bayesian function to include our belief about the situation. Technically this parameter in the Bayesian function is optional, but we will see excluding it has its own consequences.

#### Incorporating evidence

As we acquire more and more instances of evidence, our prior belief is *washed out* by the new evidence. This is to be expected. For example, if your prior belief is something ridiculous, like "I expect the sun to explode today", and each day you are proved wrong, you would hope that any inference would correct you, or at least align your beliefs better. Bayesian inference will correct this belief.

Denote $N$ as the number of instances of evidence we possess. As we gather an *infinite* amount of evidence, say as $N \rightarrow \infty$, our Bayesian results (often) align with frequentist results. Hence for large $N$, statistical inference is more or less objective. On the other hand, **for small $N$, inference is much more *unstable*: frequentist estimates have more variance and larger confidence intervals. This is where Bayesian analysis excels. By introducing a prior, and returning probabilities (instead of a scalar estimate), we *preserve the uncertainty*** that reflects the instability of statistical inference of a small $N$ dataset.

One may think that for large $N$, one can be indifferent between the two techniques since they offer similar inference, and might lean towards the computationally-simpler, frequentist methods. An individual in this position should consider the following quote by Andrew Gelman (2005)[1], before making such a decision:

> Sample sizes are never large. If $N$ is too small to get a sufficiently-precise estimate, you need to get more data (or make more assumptions). But once $N$ is "large enough," you can start subdividing the data to learn more (for example, in a public opinion poll, once you have a good estimate for the entire country, you can estimate among men and women, northerners and southerners, different age groups, etc.). $N$ is never enough because if it were "enough" you'd already be on to the next problem for which you need more data.

### Are frequentist methods incorrect then?

**No.**

Frequentist methods are still useful or state-of-the-art in many areas. Tools such as least squares linear regression, LASSO regression, and expectation-maximization algorithms are all powerful and fast. Bayesian methods complement these techniques by solving problems that these approaches cannot, or by illuminating the underlying system with more flexible modeling.

#### A note on *Big Data*
Paradoxically, big data's predictive analytic problems are actually solved by relatively simple algorithms [2][4]. Thus we can argue that big data's prediction difficulty does not lie in the algorithm used, but instead on the computational difficulties of storage and execution on big data. (One should also consider Gelman's quote from above and ask "Do I really have big data?")

The much more difficult analytic problems involve *medium data* and, especially troublesome, *really small data*. Using a similar argument as  Gelman's above, if big data problems are *big enough* to be readily solved, then we should be more interested in the *not-quite-big enough* datasets.

### Our Bayesian framework

We are interested in beliefs, which can be interpreted as probabilities by thinking Bayesian. We have a *prior* belief in event $A$, beliefs formed by previous information, e.g., our prior belief about bugs being in our code before performing tests.

Secondly, we observe our evidence. To continue our buggy-code example: if our code passes $X$ tests, we want to update our belief to incorporate this. We call this new belief the *posterior* probability. Updating our belief is done via the following equation, known as Bayes' Theorem, after its discoverer Thomas Bayes:

\begin{align}
 P( A | X ) = & \frac{ P(X | A) P(A) } {P(X) } \\\\[5pt]
& \propto P(X | A) P(A)\;\; (\propto \text{is proportional to })
\end{align}

The above formula is not unique to Bayesian inference: it is a mathematical fact with uses outside Bayesian inference. Bayesian inference merely uses it to connect prior probabilities $P(A)$ with an updated posterior probabilities $P(A | X )$.

##### Example: Mandatory coin-flip example

Every statistics text must contain a coin-flipping example, I'll use it here to get it out of the way. Suppose, naively, that you are unsure about the probability of heads in a coin flip (spoiler alert: it's 50%). You believe there is some true underlying ratio, call it $p$, but have no prior opinion on what $p$ might be.

We begin to flip a coin, and record the observations: either $H$ or $T$. This is our observed data. An interesting question to ask is how our inference changes as we observe more and more data? More specifically, what do our posterior probabilities look like when we have little data, versus when we have lots of data.

Below we plot a sequence of updating posterior probabilities as we observe increasing amounts of data (coin flips).

```python
"""
The book uses a custom matplotlibrc file, which provides the unique styles for
matplotlib plots. If executing this book, and you wish to use the book's
styling, provided are two options:
    1. Overwrite your own matplotlibrc file with the rc-file provided in the
       book's styles/ dir. See http://matplotlib.org/users/customizing.html
    2. Also in the styles is  bmh_matplotlibrc.json file. This can be used to
       update the styles in only this notebook. Try running the following code:

        import json
        s = json.load(open("../styles/bmh_matplotlibrc.json"))
        matplotlib.rcParams.update(s)

"""

# The code below can be passed over, as it is currently not important, plus it
# uses advanced topics we have not covered yet. LOOK AT PICTURE, MICHAEL!
%matplotlib inline
from IPython.core.pylabtools import figsize
import numpy as np
from matplotlib import pyplot as plt
figsize(11, 9)

import scipy.stats as stats

dist = stats.beta
n_trials = [0, 1, 2, 3, 4, 5, 8, 15, 50, 500]
data = stats.bernoulli.rvs(0.5, size=n_trials[-1])
x = np.linspace(0, 1, 100)

# For the already prepared, I'm using Binomial's conj. prior.
for k, N in enumerate(n_trials):
    sx = plt.subplot(len(n_trials)/2, 2, k+1)
    plt.xlabel("$p$, probability of heads") \
        if k in [0, len(n_trials)-1] else None
    plt.setp(sx.get_yticklabels(), visible=False)
    heads = data[:N].sum()
    y = dist.pdf(x, 1 + heads, 1 + N - heads)
    plt.plot(x, y, label="observe %d tosses,\n %d heads" % (N, heads))
    plt.fill_between(x, 0, y, color="#348ABD", alpha=0.4)
    plt.vlines(0.5, 0, 4, color="k", linestyles="--", lw=1)

    leg = plt.legend()
    leg.get_frame().set_alpha(0.4)
    plt.autoscale(tight=True)

plt.suptitle("Bayesian updating of posterior probabilities",
             y=1.02,
             fontsize=14)

plt.tight_layout()
```

The posterior probabilities are represented by the curves, and **our uncertainty is proportional to the width of the curve**. As the plot above shows, as we start to observe data our posterior probabilities start to shift and move around. Eventually, as we observe more and more data (coin-flips), our probabilities will tighten closer and closer around the true value of $p=0.5$ (marked by a dashed line).

Notice that the plots are not always *peaked* at 0.5. There is no reason it should be: recall we assumed we did not have a prior opinion of what $p$ is. In fact, if we observe quite extreme data, say 8 flips and only 1 observed heads, our distribution would look very biased *away* from lumping around 0.5 (with no prior opinion, how confident would you feel betting on a fair coin after observing 8 tails and 1 head?). As more data accumulates, we would see more and more probability being assigned at $p=0.5$, though never all of it.

The next example is a simple demonstration of the mathematics of Bayesian inference.

##### Example: Bug, or just sweet, unintended feature?

Let $A$ denote the event that our code has **no bugs** in it. Let $X$ denote the event that the code passes all debugging tests. For now, we will leave the prior probability of no bugs as a variable, i.e. $P(A) = p$.

We are interested in $P(A|X)$, i.e. the probability of no bugs, given our debugging tests $X$. To use the formula above, we need to compute some quantities.

What is $P(X | A)$, i.e., the probability that the code passes $X$ tests *given* there are no bugs? Well, it is equal to 1, for a code with no bugs will pass all tests.

$P(X)$ is a little bit trickier: The event $X$ can be divided into two possibilities, event $X$ occurring even though our code *indeed has* bugs (denoted $\sim A\;$, spoken *not $A$*), or event $X$ without bugs ($A$). $P(X)$ can be represented as:

\begin{align}
P(X ) & = P(X \text{ and } A) + P(X \text{ and } \sim A) \\\\[5pt]
 & = P(X|A)P(A) + P(X | \sim A)P(\sim A)\\\\[5pt]
& = P(X|A)p + P(X | \sim A)(1-p)
\end{align}

We have already computed $P(X|A)$ above. On the other hand, $P(X | \sim A)$ is subjective: our code can pass tests but still have a bug in it, though the probability there is a bug present is reduced. Note this is dependent on the number of tests performed, the degree of complication in the tests, etc. Let's be conservative and assign $P(X|\sim A) = 0.5$. Then

\begin{align}
P(A | X) & = \frac{1\cdot p}{ 1\cdot p +0.5 (1-p) } \\\\
& = \frac{ 2 p}{1+p}
\end{align}
This is the posterior probability. What does it look like as a function of our prior, $p \in [0,1]$?

```python
figsize(12.5, 4)
p = np.linspace(0, 1, 50)
plt.plot(p, 2*p/(1+p), color="#348ABD", lw=3)
#plt.fill_between(p, 2*p/(1+p), alpha=.5, facecolor=["#A60628"])
plt.scatter(0.2, 2*(0.2)/1.2, s=140, c="#348ABD")
plt.xlim(0, 1)
plt.ylim(0, 1)
plt.xlabel("Prior, $P(A) = p$")
plt.ylabel("Posterior, $P(A|X)$, with $P(A) = p$")
plt.title("Are there bugs in my code?");
```

We can see the biggest gains if we observe the $X$ tests passed when the prior probability, $p$, is low. Let's settle on a specific value for the prior. I'm a strong programmer (I think), so I'm going to give myself a realistic prior of 0.20, that is, there is a 20% chance that I write code bug-free. To be more realistic, this prior should be a function of how complicated and large the code is, but let's pin it at 0.20. Then my updated belief that my code is bug-free is 0.33.

Recall that the prior is a probability: $p$ is the prior probability that there *are no bugs*, so $1-p$ is the prior probability that there *are bugs*.

Similarly, our posterior is also a probability, with $P(A | X)$ the probability there is no bug *given we saw all tests pass*, hence $1-P(A|X)$ is the probability there is a bug *given all tests passed*. What does our posterior probability look like? Below is a chart of both the prior and the posterior probabilities.

```python
figsize(12.5, 4)
colours = ["#348ABD", "#A60628"]

prior = [0.20, 0.80]
posterior = [1./3, 2./3]
plt.bar([0, .7], prior, alpha=0.70, width=0.25,
        color=colours[0], label="prior distribution",
        lw="3", edgecolor=colours[0])

plt.bar([0+0.25, .7+0.25], posterior, alpha=0.7,
        width=0.25, color=colours[1],
        label="posterior distribution",
        lw="3", edgecolor=colours[1])

plt.xticks([0.20, .95], ["Bugs Absent", "Bugs Present"])
plt.title("Prior and Posterior probability of bugs present")
plt.ylabel("Probability")
plt.legend(loc="upper left");
```

Notice that after we observed $X$ occur, the probability of bugs being absent increased. By increasing the number of tests, we can approach confidence (probability 1) that there are no bugs present.

This was a very simple example of Bayesian inference and Bayes rule. Unfortunately, the mathematics necessary to perform more complicated Bayesian inference only becomes more difficult, except for artificially constructed cases. We will later see that this type of mathematical analysis is actually unnecessary. First we must broaden our modeling tools. The next section deals with *probability distributions*. If you are already familiar, feel free to skip (or at least skim), but for the less familiar the next section is essential.

_______

## Probability Distributions

**Let's quickly recall what a probability distribution is:** Let $Z$ be some random variable. Then associated with $Z$ is a *probability **distribution** function* that assigns probabilities to the different outcomes $Z$ can take. Graphically, a probability distribution is a curve where the probability of an outcome is proportional to the height of the curve. You can see examples in the first figure of this chapter.

We can divide random variables into three classifications:

-   **$Z$ is discrete**: Discrete random variables may only assume values on a specified list. Things like populations, movie ratings, and number of votes are all discrete random variables. Discrete random variables become more clear when we contrast them with...

-   **$Z$ is continuous**: Continuous random variable can take on arbitrarily exact values. For example, temperature, speed, time, color are all modeled as continuous variables because you can progressively make the values more and more precise.

- **$Z$ is mixed**: Mixed random variables assign probabilities to both discrete and continuous random variables, i.e. it is a combination of the above two categories.

### Discrete Case
If $Z$ is discrete, then its distribution is called a *probability **mass** function*, which measures the probability $Z$ takes on the value $k$, denoted $P(Z=k)$. Note that the probability mass function completely describes the random variable $Z$, that is, if we know the mass function, we know how $Z$ should behave. There are popular probability mass functions that consistently appear: we will introduce them as needed, but let's introduce the first very useful probability mass function. We say $Z$ is *Poisson*-distributed if:

$$P(Z = k) =\frac{ \lambda^k e^{-\lambda} }{k!}, \; \; k=0,1,2, \dots $$

$\lambda$ is called a parameter of the distribution, and it controls the distribution's shape. For the Poisson distribution, $\lambda$ can be any positive number. By increasing $\lambda$, we add more probability to larger values, and conversely by decreasing $\lambda$ we add more probability to smaller values. One can describe $\lambda$ as the *intensity* of the Poisson distribution.

Unlike $\lambda$, which can be any positive number, the value $k$ in the above formula must be a non-negative integer, i.e., $k$ must take on values 0,1,2, and so on. This is very important, because if you wanted to model a population you could not make sense of populations with 4.25 or 5.612 members.

If a random variable $Z$ has a Poisson mass distribution, we denote this by writing

$$Z \sim \text{Poi}(\lambda) $$

One useful property of the Poisson distribution is that its expected value is equal to its parameter, i.e.:

$$E\large[ \;Z\; | \; \lambda \;\large] = \lambda $$

**We will use this property often, so it's useful to remember**. Below, we plot the probability mass distribution for different $\lambda$ values. The first thing to notice is that by increasing $\lambda$, we add more probability of larger values occurring. Second, notice that although the graph ends at 15, the distributions do not. They assign positive probability to every non-negative integer.

```python
figsize(12.5, 4)

import scipy.stats as stats
a = np.arange(16)
poi = stats.poisson
lambda_ = [1.5, 4.25]
colours = ["#348ABD", "#A60628"]

plt.bar(a, poi.pmf(a, lambda_[0]), color=colours[0],
        label="$\lambda = %.1f$" % lambda_[0], alpha=0.60,
        edgecolor=colours[0], lw="3")

plt.bar(a, poi.pmf(a, lambda_[1]), color=colours[1],
        label="$\lambda = %.1f$" % lambda_[1], alpha=0.60,
        edgecolor=colours[1], lw="3")

plt.xticks(a + 0.4, a)
plt.legend()
plt.ylabel("probability of $k$")
plt.xlabel("$k$")
plt.title("Probability mass function of a Poisson random variable; differing \
$\lambda$ values");
```

### Continuous Case
Instead of a probability mass function, a continuous random variable has a *probability **density** function*. This might seem like unnecessary nomenclature, but the density function and the mass function are very different creatures. An example of continuous random variable is a random variable with *exponential density*. The density function for an exponential random variable looks like this:

$$f_Z(z | \lambda) = \lambda e^{-\lambda z }, \;\; z\ge 0$$

Like a Poisson random variable, an exponential random variable can take on only non-negative values. But unlike a Poisson variable, the exponential can take on *any* non-negative values, including non-integral values such as 4.25 or 5.612401. This property makes it a poor choice for count data, which must be an integer, but a great choice for time data, temperature data (measured in Kelvins, of course), or any other precise *and positive* variable. The graph below shows two probability density functions with different $\lambda$ values.

When a random variable $Z$ has an exponential distribution with parameter $\lambda$, we say *$Z$ is exponential* and write

$$Z \sim \text{Exp}(\lambda)$$

Given a specific $\lambda$, the expected value of an exponential random variable is equal to the inverse of $\lambda$, that is:

$$E[\; Z \;|\; \lambda \;] = \frac{1}{\lambda}$$

```python
a = np.linspace(0, 4, 100)
expo = stats.expon
lambda_ = [0.5, 1]

for l, c in zip(lambda_, colours):
    plt.plot(a, expo.pdf(a, scale=1./l), lw=3,
             color=c, label="$\lambda = %.1f$" % l)
    plt.fill_between(a, expo.pdf(a, scale=1./l), color=c, alpha=.33)

plt.legend()
plt.ylabel("PDF at $z$")
plt.xlabel("$z$")
plt.ylim(0,1.2)
plt.title("Probability density function of an Exponential random variable;\
 differing $\lambda$");
```

### But what is $\lambda \;$?

**This question is what motivates statistics**. In the real world, $\lambda$ is hidden from us. We see only $Z$, and must go backwards to try and determine $\lambda$. The problem is difficult because there is no one-to-one mapping from $Z$ to $\lambda$. Many different methods have been created to solve the problem of estimating $\lambda$, but since $\lambda$ is never actually observed, no one can say for certain which method is best!

Bayesian inference is concerned with *beliefs* about what $\lambda$ might be. Rather than try to guess $\lambda$ exactly, we can only talk about what $\lambda$ is likely to be by assigning a probability distribution to $\lambda$.

This might seem odd at first. After all, $\lambda$ is fixed; it is not (necessarily) random! How can we assign probabilities to values of a non-random variable? Ah, we have fallen for our old, frequentist way of thinking. Recall that under Bayesian philosophy, we *can* assign probabilities if we interpret them as beliefs. And it is entirely acceptable to have *beliefs* about the parameter $\lambda$.

##### Example: Inferring behaviour from text-message data

Let's try to model a more interesting example, one that concerns the rate at which a user sends and receives text messages:

>  You are given a series of daily text-message counts from a user of your system. The data, plotted over time, appears in the chart below. You are curious to know if the user's text-messaging habits have changed over time, either gradually or suddenly. How can you model this? (This is in fact my own text-message data. Judge my popularity as you wish.)

```python
figsize(12.5, 3.5)
count_data = np.loadtxt("data/txtdata.csv")
n_count_data = len(count_data)
plt.bar(np.arange(n_count_data), count_data, color="#348ABD")
plt.xlabel("Time (days)")
plt.ylabel("count of text-msgs received")
plt.title("Did the user's texting habits change over time?")
plt.xlim(0, n_count_data);
```

Before we start modeling, see what you can figure out just by looking at the chart above. Would you say there was a change in behaviour during this time period?

How can we start to model this? Well, as we have conveniently already seen, a Poisson random variable is a very appropriate model for this type of *count* data. Denoting day $i$'s text-message count by $C_i$,

$$ C_i \sim \text{Poisson}(\lambda)  $$

We are not sure what the value of the $\lambda$ parameter really is, however. Looking at the chart above, it appears that the rate might become higher late in the observation period, which is equivalent to saying that $\lambda$ increases at some point during the observations. (Recall that a higher value of $\lambda$ assigns more probability to larger outcomes. That is, there is a higher probability of many text messages having been sent on a given day.)

How can we represent this observation mathematically? Let's assume that on some day during the observation period (call it $\tau$), the parameter $\lambda$ suddenly jumps to a higher value. So we really have two $\lambda$ parameters: one for the period before $\tau$, and one for the rest of the observation period. In the literature, a sudden transition like this would be called a *switchpoint*:

$$
\lambda =
\begin{cases}
\lambda_1  & \text{if } t \lt \tau \cr
\lambda_2 & \text{if } t \ge \tau
\end{cases}
$$

If, in reality, no sudden change occurred and indeed $\lambda_1 = \lambda_2$, then the $\lambda$s posterior distributions should look about equal.

We are interested in inferring the unknown $\lambda$s. To use Bayesian inference, we need to assign prior probabilities to the different possible values of $\lambda$. What would be good prior probability distributions for $\lambda_1$ and $\lambda_2$? Recall that $\lambda$ can be any positive number. As we saw earlier, the *exponential* distribution provides a continuous density function for positive numbers, so it might be a good choice for modeling $\lambda_i$. But recall that the exponential distribution takes a parameter of its own, so we'll need to include that parameter in our model. Let's call that parameter $\alpha$.

\begin{align}
&\lambda_1 \sim \text{Exp}( \alpha ) \\\
&\lambda_2 \sim \text{Exp}( \alpha )
\end{align}

$\alpha$ is called a *hyper-parameter* or *parent variable*. In literal terms, it is a parameter that influences other parameters. Our initial guess at $\alpha$ does not influence the model too strongly, so we have some flexibility in our choice.  **A good rule of thumb is to set the exponential parameter equal to the inverse of the average of the count data**. Since we're modeling $\lambda$ using an exponential distribution, we can use the expected value identity shown earlier to get:

$$\frac{1}{N}\sum_{i=0}^N \;C_i \approx E[\; \lambda \; |\; \alpha ] = \frac{1}{\alpha}$$

An alternative, and something I encourage the reader to try, would be to have two priors: one for each $\lambda_i$. Creating two exponential distributions with different $\alpha$ values reflects our prior belief that the rate changed at some point during the observations.

What about $\tau$? Because of the noisiness of the data, it's difficult to pick out a priori when $\tau$ might have occurred. Instead, we can assign a *uniform prior belief* to every possible day. This is equivalent to saying

\begin{align}
& \tau \sim \text{DiscreteUniform(1,70) }\\\\
& \Rightarrow P( \tau = k ) = \frac{1}{70}
\end{align}

So after all this, what does our overall prior distribution for the unknown variables look like? Frankly, *it doesn't matter*. What we should understand is that it's an ugly, complicated mess involving symbols only a mathematician could love. And things will only get uglier the more complicated our models become. Regardless, all we really care about is the posterior distribution.

We next turn to PyMC3, a Python library for performing Bayesian analysis that is undaunted by the mathematical monster we have created.

Introducing our first hammer: PyMC3
-----

PyMC3 is a Python library for programming Bayesian analysis [3]. It is a fast, well-maintained library. The only unfortunate part is that its documentation is lacking in certain areas, especially those that bridge the gap between beginner and hacker. One of this book's main goals is to solve that problem, and also to demonstrate why PyMC3 is so cool.

We will model the problem above using PyMC3. This type of programming is called *probabilistic programming*, an unfortunate misnomer that invokes ideas of randomly-generated code and has likely confused and frightened users away from this field. The code is not random; it is probabilistic in the sense that we create probability models using programming variables as the model's components. Model components are first-class primitives within the PyMC3 framework.

B. Cronin [5] has a very motivating description of probabilistic programming:

>   Another way of thinking about this: unlike a traditional program, which only runs in the forward directions, a probabilistic program is run in both the forward and backward direction. It runs forward to compute the consequences of the assumptions it contains about the world (i.e., the model space it represents), but it also runs backward from the data to constrain the possible explanations. In practice, many probabilistic programming systems will cleverly interleave these forward and backward operations to efficiently home in on the best explanations.

Because of the confusion engendered by the term *probabilistic programming*, I'll refrain from using it. Instead, I'll simply say *programming*, since that's what it really is.

PyMC3 code is easy to read. The only novel thing should be the syntax. Simply remember that we are representing the model's components ($\tau, \lambda_1, \lambda_2$ ) as variables.

```python
import pymc3 as pm
import theano.tensor as tt

with pm.Model() as model:
    alpha = 1.0/count_data.mean()  # Recall count_data is the
                                   # variable that holds our txt counts
    lambda_1 = pm.Exponential("lambda_1", alpha)
    lambda_2 = pm.Exponential("lambda_2", alpha)

    tau = pm.DiscreteUniform("tau", lower=0, upper=n_count_data - 1)
```

In the code above, we create the PyMC3 variables corresponding to $\lambda_1$ and $\lambda_2$. We assign them to PyMC3's *stochastic variables*, so-called because they are treated by the back end as random number generators.

```python
with model:
    idx = np.arange(n_count_data) # Index
    lambda_ = pm.math.switch(tau > idx, lambda_1, lambda_2)
```

This code creates a new function `lambda_`, but really we can think of it as a random variable: the random variable $\lambda$ from above. The `switch()` function assigns `lambda_1` or `lambda_2` as the value of `lambda_`, depending on what side of `tau` we are on. The values of `lambda_` up until `tau` are `lambda_1` and the values afterwards are `lambda_2`.

Note that because `lambda_1`, `lambda_2` and `tau` are random, `lambda_` will be random. We are **not** fixing any variables yet.

```python
with model:
    observation = pm.Poisson("obs", lambda_, observed=count_data)
```

The variable `observation` combines our data, `count_data`, with our proposed data-generation scheme, given by the variable `lambda_`, through the `observed` keyword.

The code below will be explained in Chapter 3, but I show it here so you can see where our results come from. One can think of it as a *learning* step. The machinery being employed is called *Markov Chain Monte Carlo* (MCMC), which I also delay explaining until Chapter 3. This technique returns thousands of random variables from the posterior distributions of $\lambda_1, \lambda_2$ and $\tau$. We can plot a histogram of the random variables to see what the posterior distributions look like. Below, we collect the samples (called *traces* in the MCMC literature) into histograms.

```python
### Mysterious code to be explained in Chapter 3.
with model:
    step = pm.Metropolis()
    trace = pm.sample(10000, tune=5000,step=step)
```

    Multiprocess sampling (4 chains in 4 jobs)
    CompoundStep
    >Metropolis: [tau]
    >Metropolis: [lambda_2]
    >Metropolis: [lambda_1]
    Sampling 4 chains: 100%|██████████| 60000/60000 [00:13<00:00, 4577.12draws/s]
    The number of effective samples is smaller than 25% for some parameters.

```python
lambda_1_samples = trace['lambda_1']
lambda_2_samples = trace['lambda_2']
tau_samples = trace['tau']
```

```python
figsize(12.5, 10)
#histogram of the samples:

ax = plt.subplot(311)
ax.set_autoscaley_on(False)

plt.hist(lambda_1_samples, histtype='stepfilled', bins=30, alpha=0.85,
         label="posterior of $\lambda_1$", color="#A60628", density=True)
plt.legend(loc="upper left")
plt.title(r"""Posterior distributions of the variables
    $\lambda_1,\;\lambda_2,\;\tau$""")
plt.xlim([15, 30])
plt.xlabel("$\lambda_1$ value")

ax = plt.subplot(312)
ax.set_autoscaley_on(False)
plt.hist(lambda_2_samples, histtype='stepfilled', bins=30, alpha=0.85,
         label="posterior of $\lambda_2$", color="#7A68A6", density=True)
plt.legend(loc="upper left")
plt.xlim([15, 30])
plt.xlabel("$\lambda_2$ value")

plt.subplot(313)
w = 1.0 / tau_samples.shape[0] * np.ones_like(tau_samples)
plt.hist(tau_samples, bins=n_count_data, alpha=1,
         label=r"posterior of $\tau$",
         color="#467821", weights=w, rwidth=2.)
plt.xticks(np.arange(n_count_data))

plt.legend(loc="upper left")
plt.ylim([0, .75])
plt.xlim([35, len(count_data)-20])
plt.xlabel(r"$\tau$ (in days)")
plt.ylabel("probability");
```

### Interpretation

Recall that Bayesian methodology returns a *distribution*. Hence we now have distributions to describe the unknown $\lambda$s and $\tau$. What have we gained? Immediately, we can see the uncertainty in our estimates: the wider the distribution, the less certain our posterior belief should be. We can also see what the plausible values for the parameters are: $\lambda_1$ is around 18 and $\lambda_2$ is around 23. The posterior distributions of the two $\lambda$s are clearly distinct, indicating that it is indeed likely that there was a change in the user's text-message behaviour.

What other observations can you make? If you look at the original data again, do these results seem reasonable?

**Notice also that the posterior distributions for the $\lambda$s do not look like exponential distributions, even though our priors for these variables were exponential**. In fact, the posterior distributions are not really of any form that we recognize from the original model. But that's OK! This is one of the benefits of taking a computational point of view. If we had instead done this analysis using mathematical approaches, we would have been stuck with an analytically intractable (and messy) distribution. Our use of a computational approach makes us indifferent to mathematical tractability.

Our analysis also returned a distribution for $\tau$. Its posterior distribution looks a little different from the other two because it is a discrete random variable, so it doesn't assign probabilities to intervals. We can see that near day 45, there was a 50% chance that the user's behaviour changed. Had no change occurred, or had the change been gradual over time, the posterior distribution of $\tau$ would have been more spread out, reflecting that many days were plausible candidates for $\tau$. By contrast, in the actual results we see that only three or four days make any sense as potential transition points.

### Why would I want samples from the posterior, anyways?

We will deal with this question for the remainder of the book, and it is an understatement to say that it will lead us to some amazing results. For now, let's end this chapter with one more example.

We'll use the posterior samples to answer the following question: what is the expected number of texts at day $t, \; 0 \le t \le 70$ ? Recall that the expected value of a Poisson variable is equal to its parameter $\lambda$. Therefore, the question is equivalent to *what is the expected value of $\lambda$ at time $t$*?

In the code below, let $i$ index samples from the posterior distributions. Given a day $t$, we average over all possible $\lambda_i$ for that day $t$, using $\lambda_i = \lambda_{1,i}$ if $t \lt \tau_i$ (that is, if the behaviour change has not yet occurred), else we use $\lambda_i = \lambda_{2,i}$.

```python
figsize(12.5, 5)
# tau_samples, lambda_1_samples, lambda_2_samples contain
# N samples from the corresponding posterior distribution
N = tau_samples.shape[0]
expected_texts_per_day = np.zeros(n_count_data)
for day in range(0, n_count_data):
    # ix is a bool index of all tau samples corresponding to
    # the switchpoint occurring prior to value of 'day'
    ix = day < tau_samples
    # Each posterior sample corresponds to a value for tau.
    # for each day, that value of tau indicates whether we're "before"
    # (in the lambda1 "regime") or
    #  "after" (in the lambda2 "regime") the switchpoint.
    # by taking the posterior sample of lambda1/2 accordingly, we can average
    # over all samples to get an expected value for lambda on that day.
    # As explained, the "message count" random variable is Poisson distributed,
    # and therefore lambda (the poisson parameter) is the expected value of
    # "message count".
    expected_texts_per_day[day] = (lambda_1_samples[ix].sum()
                                   + lambda_2_samples[~ix].sum()) / N

plt.plot(range(n_count_data), expected_texts_per_day, lw=4, color="#E24A33",
         label="expected number of text-messages received")
plt.xlim(0, n_count_data)
plt.xlabel("Day")
plt.ylabel("Expected # text-messages")
plt.title("Expected number of text-messages received")
plt.ylim(0, 60)
plt.bar(np.arange(len(count_data)), count_data, color="#348ABD", alpha=0.65,
        label="observed texts per day")

plt.legend(loc="upper left");
```

Our analysis shows strong support for believing the user's behavior did change ($\lambda_1$ would have been close in value to $\lambda_2$ had this not been true), and that the change was sudden rather than gradual (as demonstrated by $\tau$'s strongly peaked posterior distribution). We can speculate what might have caused this: a cheaper text-message rate, a recent weather-to-text subscription, or perhaps a new relationship. (In fact, the 45th day corresponds to Christmas, and I moved away to Toronto the next month, leaving a girlfriend behind.)

##### Exercises

1\.  Using `lambda_1_samples` and `lambda_2_samples`, what is the mean of the posterior distributions of $\lambda_1$ and $\lambda_2$?

```python
#type your code here.
lambda_1_samples.mean(), lambda_2_samples.mean()
```

    (17.760179819690123, 22.710926545663497)

2\.  What is the expected percentage increase in text-message rates? `hint:` compute the mean of `lambda_1_samples/lambda_2_samples`. Note that this quantity is very different from `lambda_1_samples.mean()/lambda_2_samples.mean()`.

```python
#type your code here.
(lambda_1_samples/lambda_2_samples).mean()
```

    0.7832074809697906

```python
lambda_1_samples.mean()/lambda_2_samples.mean()
```

    0.7820103589336522

PS to myself: Note that the results returned are different !

3\. What is the mean of $\lambda_1$ **given** that we know $\tau$ is less than 45.  That is, suppose we have been given new information that the change in behaviour occurred prior to day 45. What is the expected value of $\lambda_1$ now? (You do not need to redo the PyMC3 part. Just consider all instances where `tau_samples < 45`.)

```python
#type your code here.
idx = tau_samples < 45
lambda_1_samples[idx].mean()
```

    17.757353124712516

### References

-  [1] Gelman, Andrew. N.p.. Web. 22 Jan 2013. [N is never large enough](http://andrewgelman.com/2005/07/31/n_is_never_larg).
-  [2] Norvig, Peter. 2009. [The Unreasonable Effectiveness of Data](http://static.googleusercontent.com/media/research.google.com/en//pubs/archive/35179.pdf).
- [3] Salvatier, J, Wiecki TV, and Fonnesbeck C. (2016) Probabilistic programming in Python using PyMC3. *PeerJ Computer Science* 2:e55 <https://doi.org/10.7717/peerj-cs.55>
- [4] Jimmy Lin and Alek Kolcz. Large-Scale Machine Learning at Twitter. Proceedings of the 2012 ACM SIGMOD International Conference on Management of Data (SIGMOD 2012), pages 793-804, May 2012, Scottsdale, Arizona.
- [5] Cronin, Beau. "Why Probabilistic Programming Matters." 24 Mar 2013. Google, Online Posting to Google . Web. 24 Mar. 2013. <https://plus.google.com/u/0/107971134877020469960/posts/KpeRdJKR6Z1>.

```python
from IPython.core.display import HTML
def css_styling():
    styles = open("../styles/custom.css", "r").read()
    return HTML(styles)
css_styling()
```

<style>
    @font-face {
        font-family: "Computer Modern";
        src: url('http://9dbb143991406a7c655e-aa5fcb0a5a4ec34cff238a2d56ca4144.r56.cf5.rackcdn.com/cmunss.otf');
    }
    @font-face {
        font-family: "Computer Modern";
        font-weight: bold;
        src: url('http://9dbb143991406a7c655e-aa5fcb0a5a4ec34cff238a2d56ca4144.r56.cf5.rackcdn.com/cmunsx.otf');
    }
    @font-face {
        font-family: "Computer Modern";
        font-style: oblique;
        src: url('http://9dbb143991406a7c655e-aa5fcb0a5a4ec34cff238a2d56ca4144.r56.cf5.rackcdn.com/cmunsi.otf');
    }
    @font-face {
        font-family: "Computer Modern";
        font-weight: bold;
        font-style: oblique;
        src: url('http://9dbb143991406a7c655e-aa5fcb0a5a4ec34cff238a2d56ca4144.r56.cf5.rackcdn.com/cmunso.otf');
    }
    div.cell{
        width:800px;
        margin-left:16% !important;
        margin-right:auto;
    }
    h1 {
        font-family: Helvetica, serif;
    }
    h4{
        margin-top:12px;
        margin-bottom: 3px;
       }
    div.text_cell_render{
        font-family: Computer Modern, "Helvetica Neue", Arial, Helvetica, Geneva, sans-serif;
        line-height: 145%;
        font-size: 130%;
        width:800px;
        margin-left:auto;
        margin-right:auto;
    }
    .CodeMirror{
            font-family: "Source Code Pro", source-code-pro,Consolas, monospace;
    }
    .prompt{
        display: None;
    }
    .text_cell_render h5 {
        font-weight: 300;
        font-size: 22pt;
        color: #4057A1;
        font-style: italic;
        margin-bottom: .5em;
        margin-top: 0.5em;
        display: block;
    }

    .warning{
        color: rgb( 240, 20, 20 )
        }
</style>


```python
%run common-imports.ipynb
```

<!-- Many of the styles here are inspired by:
    https://towardsdatascience.com/10-practical-tips-you-need-to-know-to-personalize-jupyter-notebook-fbd202777e20

    On the author's local machine, these exist in the custom.css file. However, in order to keep uniform look and feel,
    and at the request of participants, I have added it to this common import-file here.

    -->

<link href="https://fonts.googleapis.com/css?family=Lora:400,700|Montserrat:300" rel="stylesheet">

<link href="https://fonts.googleapis.com/css2?family=Crimson+Pro&family=Literata&display=swap" rel="stylesheet">
<style>

#ipython_notebook::before{
 content:"Workshop: Comprehensive Introduction to Data Science";
        color: white;
        font-weight: bold;
        text-transform: uppercase;
        font-family: 'Lora',serif;
        font-size:20pt;
        margin-bottom:15px;

}
body > #header {
    background: maroon;
        opacity: 0.7;
}

.navbar-default .navbar-nav > li > a, #kernel_indicator {
    color: white;
    transition: all 0.25s;
    font-size:12pt;
    font-family: sans;
    /*font-weight:bold;*/
}
.navbar-default {
    padding-left:100px;
    background: none;
    border: none;
}

body > menubar-container {
    background-color: wheat;
}
#ipython_notebook img{
    display:block;

    background: url("https://www.supportvectors.com/wp-content/uploads/2016/03/logo-poster-smaller.png") no-repeat;
    background-size: contain;

    padding-left: 600px;
    padding-right: 100px;

    -moz-box-sizing: border-box;
    box-sizing: border-box;
}

body {
 #font-family:  'Literata', serif;
    font-family:'Lora', san-serif;
    text-align: justify;
    font-weight: 400;
    font-size: 14pt;
}

iframe{
    width:100%;
    min-height:600px;
}

h1, h2, h3, h4, h5, h6 {
# font-family: 'Montserrat', sans-serif;
 font-family:'Lora', serif;
 font-weight: 300;
 text-transform: uppercase;
 color: #EC7063 ;
}

h2 {
    color: #000080;
}

.checkpoint_status, .autosave_status {
    color:wheat;
}

#notebook_name {
    font-weight: 1000;
    font-size:20pt;
    text-variant:uppercase;
    color: wheat;
    margin-right:20px;
}
#notebook_name:hover {
background-color: salmon;
}

.dataframe { /* dataframe atau table */
    background: white;
    box-shadow: 0px 1px 2px #bbb;
}
.dataframe thead th, .dataframe tbody td {
    text-align: center;
    padding: 1em;
}

.checkpoint_status, .autosave_status {
    color:wheat;
}

.output {
    align-items: center;
}

div.cell {
    transition: all 0.25s;
    border: none;
    position: relative;
    top: 0;
}
div.cell.selected, div.cell.selected.jupyter-soft-selected {
    border: none;
    background: transparent;
    box-shadow: 0 6px 18px #aaa;
    z-index: 10;
    top: -10px;
}
.CodeMirror pre, .CodeMirror-dialog, .CodeMirror-dialog .CodeMirror-search-field, .terminal-app .terminal {
    font-family: 'Source Code Pro Medium' , serif;
    font-weight: 500;
    font-size: 12pt;
}

</style>

#  Residuals variance ($\sigma_\epsilon$), RSS and $R^2$

## Q:
''*What is the the relationship between variance of the response and $R^2$?*''

## Answer

We assume that this question is asked in the context of (ordinary) linear regression models, which we are currently studying.

First, let us clarify our notation. We will treat $y_i$ to be the response value of a datum used to train a linear regression model. Once linear regression has learned an estimator $\hat y = f(\mathbf{x})$ from the training data, the estimation it provides for a given input $\mathbf x_i$ is $y_i$. For a given dataset on $n$ instances of datum, $\mathscr{D}$, one can then compute the residual error of each prediction as:

\begin{equation} \epsilon_i = y_i - \hat y_i \end{equation}

Then a measure of error as the residual errors summed and squared is:

$$ \begin{aligned}
RSS &= \sum_{i=1}^n \epsilon_i^2 \\
    &= \sum_{i=1}^n (y_i -\hat y_i)^2 \\
\end{aligned}
$$

There is a close relationship between the variance of the **residuals** $\sigma_\epsilon^2$, rather than the variance of the response variable itself. Let us explore this systematically.

Once a linear regession model has been fit to the data, in general, the mean of the residuals $\mu_\epsilon$ will be close to zero, i.e. $\mathbf{\mu_\epsilon \approx 0}$. With this in hand, let us consider the variance of the residual errors over the training dataset $\mathscr{D}$ of $n$ sample instances.Then:

$$
\begin{aligned}\sigma^2 &= \frac{1}{n-1}\sum_1^n (\epsilon_i - \mu_\epsilon)^2 \\
& = \frac{1}{n-1}\sum_1^n \epsilon_i^2 \text{  since } \mu_\epsilon \approx 0 \\
\end{aligned}
$$

Now consider the definition of TSS (total residual errors summed and squared) of the null-hypothesis $\phi$, where $\epsilon_{i,\phi} = (y_i -\hat y_i^\phi), \text{ here } y_i^\phi \text { is the prediction of the null hypothesis and thus always equal to } \bar y$. Thus $\epsilon_{i,\phi} = (y_i - \bar y)$.

$$\begin{aligned}
TSS &= \sum_{i=1}^n \epsilon_\phi^2 \\
&=(n-1)\sigma_\phi^2 \\
\end{aligned}
$$

Likewise, the RSS of a given hypothesis $h$, with errors $\epsilon_{i, h} = (y_i - \hat y_{i,h})$, is:

$$\begin{aligned}
RSS &= \sum_{i=1}^n \epsilon_{i,h}^2 \\
&=(n-1)\sigma_h^2 \\
\end{aligned}
$$

The coefficient of determination, $R^2$, is defined as:

\begin{equation}
R^2 = \frac{TSS - RSS}{TSS}
\end{equation}

Substituting the values of TSS and RSS from the prior equations in terms of their variance of residuals, we get, therefore:

\begin{equation}
R^2 = \frac{\sigma_\phi^2 - \sigma_h^2}{\sigma_\phi^2}
\end{equation}

Thus, we have arrived at the relationship between RSS, $R^2$ and the variance of the residual errors.

```python

```


```python
%run ../../common/import_all.py

from common.setup_notebook import set_css_style, setup_matplotlib, config_ipython
config_ipython()
setup_matplotlib()
set_css_style()
```

# The Maximum Likelihood, Maximum a Posteriori and Expectation-maximisation estimation methods

## The likelihood

Imagine you have a statistical model, that is, a mathematical description of your data which depends on some parameters $\theta$. The *likelihood function*, usually indicated as $\mathcal{L}$, is a function of these parameters and represents the probability of observing evidence (observed data) $E$ given said parameters:

$$
\mathcal{L} = P(E \ | \  \theta)
$$

Because it is a function of the parameters given the outcome, you write

$$
\mathcal{L}(\theta \  | \ E) = P(E \ | \  \theta)
$$

The difference between *probability* and *likelihood* is quite subtle in that in common language they are be casually swapped, but they represent different things. The probability mesaures the outcomes observed as a function of the parameters $\theta$ of the underlying model. But in reality $\theta$ are unknown and in fact, we go through the reverse process: estimating the parameters given the evidence we observe. For this, we use the likelihood, which is defined as above because we maximise it in such a way to respond to the equality above. This is exactly what the ML estimation does, as per below.

Bear in mind that the likelihood is a function of $\theta$.

## The MLE method

The Maximum Likelihood Estimation (MLE) is a procedure to find the parameters of a statistical model via the maximisation of the likelihood so as to maximise the agreement between the model and the observed data.

The maximisation of the likelihood is usually performed via the maximisation of its logarithm as it is much more convenient; the logarithm is a monotonic function so the procedure is legit.

### Example: a Bernoulli distribution

The likelihood function for a [Bernoulli distribution](../distributions-measures/famous-distributions.ipynb#Bernoulli)  ($x_i \in {0, 1}$) is, for parameter $p$:

\begin{align}
\mathcal{L}(x_1, x_2, \ldots, x_n \ | \ p) &= P(X_1 = x_1, X_2 = x_2, \ldots, X_n = x_n \ | \ p) \\
&= p^{x_1}(1-p)^{1-x_1} \cdot \ldots \cdot p^{x_n}(1-p)^{1-x_n} \\
&= p^{\sum_i x_i}(1-p)^{\sum_i(1-x_i)} \\
&=  p^{\sum_i x_i}(1-p)^{n -\sum_i x_i}
\end{align}

so that if we take the logarithm, we get

$$
\log \mathcal{L} = \sum_i x_i \log p + \Big(n - \sum_i x_i\Big) \log (1-p) \ .
$$

To maximise it, we compute and nullify the first derivative

$$
\frac{d \log \mathcal{L}}{d p} = \frac{\sum_i x_i}{p} - \frac{n - \sum_i x_i}{1-p} = 0
$$

which leads to

$$
\sum_i x_i - p \sum_i x_i = np - p \sum_i x_i
$$

and finally to

$$
p = \frac{\sum_i x_i}{n}
$$

### Example: estimating the best mean of some data

This example is reported from [[here]](#2). Let us assume we know the weights of women are normally distributed with a mean $\mu$ and standard deviation $\sigma$. A random sample of $10$ women is (in pounds):

$$
115, 122, 130, 124, 149, 160, 152, 138, 149, 180
$$

We want to estimate $\mu$. We know

$$
P(x_i ; \mu, \sigma) = \frac{1}{\sqrt{2 \pi \sigma^2}} e^{- \frac{(x_i - \mu)^2}{2 \sigma^2}}
$$

The likelihood is (note that the $X_i$ are independent)

\begin{align}
\mathcal{L}(x_i | \mu, \sigma) &= P(X_1=x_1, \ldots, X_n=x_n)  \\
&= \Pi_i p(x_i; \mu \sigma) \\
&= \sigma^n (2 \pi)^{-n/2} e^{- \frac{1}{2 \sigma^2} \sum_i (x_i - \mu)^2}
\end{align}

Now, again it is easier to work with the logarithm:

$$
\log \mathcal{L} = -n \log \sigma \frac{n}{2} \log 2 \pi - \frac{1}{2 \sigma^2} \sum_i (x_i - \mu)^2
$$

so that

$$
\frac{d \log \mathcal{L}}{d \mu} = -\frac{1}{2 \sigma^2} 2 \sum_i (x_i - \mu) = 0
$$

$$
\sum_i x_i - n \mu = 0
$$

$$
\mu = \frac{\sum_i x_i}{n}
$$

and so the maximum likelihood estimate for a given sample is 142.2 and we can could do the same to estimate $\sigma$, obtaining (can be proven through second derivative that it is a maximum)

$$
\sigma^2 = \frac{\sum_i (x_i - \mu)^2}{n}
$$

## The MAP method

This Maximim a Posteriori (MAP) estimation method uses the mode of the posterior to estimate the unknown population.

From Bayes' theorem, the posterior is expressed as

$$
P(\theta \ | \ x) = \frac{P(x \ | \ \theta) P(\theta)}{\int d \theta' P(x \ | \ \theta') P(\theta')} \ ,
$$

with $\theta$ being the parameters of the statistical model and $x$ the observed data. The MAP method estimates $\theta$ as the one which maximises the posterior; note that the denominator is just a normalisation factor:

$$
\hat{\theta}_{MAP}(x) = arg \max_\theta P(\theta \  | \  x) = arg \max_\theta P(x \ | \ \theta) P(\theta) \ .
$$

This means exactly taking the mode of the posterior distribution.

In the case of a uniform prior, the MAP estimation is equal to the ML estimation as we get to maximise the likelihood because the prior becomes just a factor. For the computation, conjugate priors are particularly handy.

As in the case of the MLE, what we really do is maximising the logarithm of the posterior rather than the posterior itself, so we do

$$
\hat \theta_{MAP}(x) = arg \max_{\theta} \log P(\theta \ | \ x) = arg \max_{\theta} [\log P(x \ | \ \theta) + \log P(\theta)] \ .
$$

## MAP and ML

In the last equation, if we only had the first term to maximise, we would be doing a ML estimation. The second term is the one accounting for the presence of a prior: this is why the MAP method is considered as a regularised ML as prior knowledge is factored in the computation.

While the ML method can be seen as responding to a frequentist approach, the MAP method responds to a Bayesian approach.

## The Expectation-Maximisation algorithm

The EM algorithm can be used to find the solution of MLE or MAP when some data is missing, meaning there are some latent variables not observed.

Let's say that for the random variable $x$ we have the $n$ observations

$$
x_1, \ldots, x_n \ ,
$$

which depend on parameters $\theta$, and that the goal is to find the parameter $\theta$ that maximises the likelihood which is of the form

$$
L = \sum_z P_\theta(x, z) \ ,
$$

meaning it is a sum over the latent variables $z$; this makes the problem difficult to solve analytically.

The EM algorithm updates the parameters in steps, which means it risks obtaining a local rather than a global maximum.

### The E step

In the E phase (time $t$), the expected value of $L$ is computed with respect to the conditional distribution of $z$ given $x$ under the current estimate of parameters $\theta$:

$$
\bar L(\theta | \theta^t) = \mathbb E_{z | x, \theta^t} [\log L (\theta, x)]
$$

This means that the log-likelihood is evaluated using the current state of the parameters.

### The M step

In the M phase (time $t+1$), we find the parameters which maximises the log-likelihood found in the E step:

$$
\theta^{t+1} = arg \max_{\theta} \bar L(\theta | \theta^t)
$$

## References

1.  [Cross Validated on the difference between Probability and Likelihood](https://stats.stackexchange.com/questions/2641/what-is-the-difference-between-likelihood-and-probability)
2.  Some examples in [this course](https://onlinecourses.science.psu.edu/stat414/node/191) from Penn State
3. [An assignement on the method from Carnegie Mellon](http://www.cs.cmu.edu/~aarti/Class/10601/homeworks/hw2Solutions.pdf)

```python

```


# [ATM 623: Climate Modeling](../index.ipynb)
[Brian E. J. Rose](http://www.atmos.albany.edu/facstaff/brose/index.html), University at Albany
# Lecture 6: Elementary greenhouse models

### About these notes:

This document uses the interactive [`IPython notebook`](http://ipython.org/notebook.html) format (now also called [`Jupyter`](https://jupyter.org)). The notes can be accessed in several different ways:

- The interactive notebooks are hosted on `github` at https://github.com/brian-rose/ClimateModeling_courseware
- The latest versions can be viewed as static web pages [rendered on nbviewer](http://nbviewer.ipython.org/github/brian-rose/ClimateModeling_courseware/blob/master/index.ipynb)
- A complete snapshot of the notes as of May 2015 (end of spring semester) are [available on Brian's website](http://www.atmos.albany.edu/facstaff/brose/classes/ATM623_Spring2015/Notes/index.html).

Many of these notes make use of the `climlab` package, available at https://github.com/brian-rose/climlab

## Contents

1. [A single layer atmosphere](#section1)
2. [Introducing the two-layer leaky greenhouse](#section2)
3. [Tuning the leaky greenhouse model to observations](#section3)
4. [Level of emission](#section4)
5. [Radiative forcing in the 2-layer leaky greenhouse](#section5)
6. [Radiative equilibrium in the 2-layer leaky greenhouse](#section6)

____________

## 1. A single layer atmosphere
____________

We will make our first attempt at quantifying the greenhouse effect in the simplest possible greenhouse model: a single layer of atmosphere that is able to absorb and emit longwave radiation.

```python
from IPython.display import Image
Image('../images/MarshallPlumbFig2.6.png')
```

*Figure reproduced from Marshall and Plumb (2008): Atmosphere, Ocean, and Climate Dynamics*

### Assumptions

- Atmosphere is a single layer of air at temperature $T_a$
- Atmosphere is **completely transparent to shortwave** solar radiation.
- Atmosphere is **completely opaque to infrared** radiation
- Both surface and atmosphere emit radiation as **blackbodies**
- Atmosphere radiates **equally up and down** ($A\uparrow = A\downarrow = \sigma T_a^4$)
- There are no other heat transfer mechanisms

We can now use the concept of energy balance to ask what the temperature need to be in order to balance the energy budgets at the surface and the atmosphere, i.e. the **radiative equilibrium temperatures**.

### Energy balance at the surface

\begin{align}
\text{energy in} &= \text{energy out} \\
(1-\alpha) Q + \sigma T_a^4 &= \sigma T_s^4 \\
\end{align}

The presence of the atmosphere above means there is an additional source term: downwelling infrared radiation from the atmosphere.

We call this the **back radiation**.

### Energy balance for the atmosphere

\begin{align}
\text{energy in} &= \text{energy out} \\
\sigma T_s^4 &= A\uparrow + A\downarrow = 2 \sigma T_a^4 \\
\end{align}

which means that
$$ T_s = 2^\frac{1}{4} T_a \approx 1.2 T_a $$

So we have just determined that, in order to have a purely **radiative equilibrium**, we must have $T_s > T_a$.

The surface must be warmer than the atmosphere.

### Solve for the radiative equilibrium surface temperature

Now plug this into the surface equation to find

$$ \frac{1}{2} \sigma T_s^4 = (1-\alpha) Q  $$

and use the definition of the emission temperature $T_e$ to write

$$ (1-\alpha) Q = \sigma T_e^4 $$

*In fact, in this model, $T_e$ is identical to the atmospheric temperature $T_a$, since all the OLR originates from this layer.*

Solve for the surface temperature:
$$ T_s = 2^\frac{1}{4} T_e $$

Putting in observed numbers, $T_e = 255$ K gives a surface temperature of
$$T_s = 303 ~\text{K}$$

This model is one small step closer to reality: surface is warmer than atmosphere, emissions to space generated in the atmosphere, atmosphere heated from below and helping to keep surface warm.

BUT our model now overpredicts the surface temperature by about 15ºC (or K).

Ideas about why?

Basically we just need to read our list of assumptions above and realize that none of them are very good approximations:

- Atmosphere absorbs some solar radiation.
- Atmosphere is NOT a perfect absorber of longwave radiation
- Absorption and emission varies strongly with wavelength (atmosphere does not behave like a blackbody).
- Emissions are not determined by a single temperature $T_a$ but by the detailed vertical profile of air temperture.
- Energy is redistributed in the vertical by a variety of dynamical transport mechanisms (e.g. convection and boundary layer turbulence).

____________

## 2. Introducing the two-layer leaky greenhouse
____________

Let's generalize the above model just a little bit to build a slighly more realistic model of longwave radiative transfer.

We will address two shortcomings of our single-layer model:
1. No vertical structure
2. 100% longwave opacity

Relaxing these two assumptions gives us what turns out to be a very useful prototype model for **understanding how the greenhouse effect works**.

### Assumptions

- The atmosphere is **transparent to shortwave radiation** (still)
- Divide the atmosphere up into **two layers of equal mass** (the dividing line is thus at 500 hPa pressure level)
- Each layer **absorbs only a fraction $\epsilon$ ** of whatever longwave radiation is incident upon it.
- We will call the fraction $\epsilon$ the **absorptivity** of the layer.
- Assume $\epsilon$ is the same in each layer

Note that this last assumption is appropriate is the absorption is actually carried out by a gas that is **well-mixed** in the atmosphere.

Out of our two most important absorbers:

- CO$_2$ is well mixed
- H$_2$O is not (mostly confined to lower troposphere due to strong temperature dependence of the saturation vapor pressure).

But we will ignore this aspect of reality for now.

In order to build our model, we need to introduce one additional piece of physics known as **Kirchoff's Law**:
$$ \text{absorptivity} = \text{emissivity} $$

So if a layer of atmosphere at temperature $T$ absorbs a fraction $\epsilon$ of incident longwave radiation, it must emit
$$ \epsilon ~\sigma ~T^4 $$
both up and down.

### A sketch of the radiative fluxes in the 2-layer atmosphere

```python
Image('../images/2layerAtm_sketch.png', retina=True)
```

- Surface temperature is $T_s$
- Atm. temperatures are $T_0, T_1$ where $T_0$ is closest to the surface.
- absorptivity of atm layers is $\epsilon$
- Surface emission is $\sigma T_s^4$
- Atm emission is $\epsilon_0 \sigma T_0^4, \epsilon_1 \sigma T_1^4$ (up and down)
- Absorptivity = emissivity for atmospheric layers
- a fraction $(1-\epsilon)$ of the longwave beam is **transmitted** through each layer

### Longwave emissions
Let's denote the emissions from each layer as
\begin{align}
E_s &= \sigma T_s^4 \\
E_0 &= \epsilon \sigma T_0^4 \\
E_1 &= \epsilon \sigma T_1^4
\end{align}
recognizing that $E_0$ and $E_1$ contribute to **both** the upwelling and downwelling beams.

### Shortwave radiation
Since we have assumed the atmosphere is transparent to shortwave, the incident beam $Q$ passes unchanged from the top to the surface, where a fraction $\alpha$ is reflected upward out to space.

### Upwelling beam

Let $U$ be the upwelling flux of longwave radiation.

The upward flux from the surface to layer 0 is
$$ U_0 = E_s $$
(just the emission from the suface).

Following this beam upward, we can write the upward flux from layer 0 to layer 1 as the sum of the transmitted component that originated below layer 0 and the new emissions from layer 0:
$$ U_1 = (1-\epsilon) U_0 + E_0 $$

Continuing to follow the same beam, the upwelling flux above layer 1 is
$$ U_2 = (1-\epsilon) U_1 + E_1 $$

Since there is no more atmosphere above layer 1, this upwelling flux is our Outgoing Longwave Radiation for this model:

\begin{align}
OLR &= U_2 \\
  &= (1-\epsilon) \bigg((1-\epsilon) \Big( \sigma T_s^4 \Big) + \epsilon \sigma T_0^4 \bigg) + \epsilon T_1^4 \\
  &= (1-\epsilon)^2 \sigma T_s^4 + \epsilon(1-\epsilon) \sigma T_0^4 + \epsilon T_1^4
\end{align}

Notice that the three terms in the OLR represent the **contributions to the total OLR that originate from each of the three levels**.

### Downwelling beam

Let $D$ be the downwelling longwave beam. Since there is no longwave radiation coming in from space, we begin with
$$ D_2 = 0$$

Between layer 1 and layer 0 the beam contains emissions from layer 1:
$$ D_1 = E_1  = \epsilon \sigma T_1^4$$
( in general we can write $D_1 = (1-\epsilon)D_2 + E_1$ if we are dealing with a non-zero $D_2$)

Finally between layer 0 and the surface the beam contains a transmitted component and the emissions from layer 0:
$$ D_0 = (1-\epsilon) D_1 + E_0 = \epsilon(1-\epsilon) \sigma T_1^4 + \epsilon \sigma T_0^4$$

This $D_0$ is what we call the **back radiation**, i.e. the longwave radiation from the atmosphere to the surface.

____________

## 3. Tuning the leaky greenhouse model to observations
____________

In building our new model we have introduced exactly one parameter, the absorptivity $\epsilon$. We need to choose a value for $\epsilon$.

We will tune our model so that it **reproduces the observed global mean OLR** given **observed global mean temperatures**.

To get appropriate temperatures for $T_s, T_0, T_1$, let's revisit the [global, annual mean lapse rate plot from NCEP Reanalysis data](Lecture05 -- Radiation.ipynb) from the previous lecture.

### Temperatures
First, we set
$$T_s = 288 \text{ K}  $$

From the lapse rate plot, an average temperature for the layer between 1000 and 500 hPa is

$$ T_0 = 275 \text{ K}$$

Defining an average temperature for the layer between 500 and 0 hPa is more ambiguous because of the lapse rate reversal at the tropopause. We will choose

$$ T_1 = 230 \text{ K}$$

From the graph, this is approximately the observed global mean temperature at 275 hPa or about 10 km.

### OLR

From the [observed global energy budget](Lecture01 -- Planetary energy budget.ipynb) we set

$$ OLR = 239 \text{ W m}^{-2} $$

### Solving for $\epsilon$

We wrote down the expression for OLR as a function of temperatures and absorptivity in our model above. We just need to equate this to the observed value and solve a quadratic equation for $\epsilon$.

We will use the symbolic math packaged called `sympy` to help us out here.

```python
import sympy
sympy.init_printing()
epsilon, T_s, T_0, T_1, sigma = sympy.symbols('epsilon, T_s, T_0, T_1, sigma')

#  Define the contributions to OLR originating from each level
OLR_s = (1-epsilon)**2 *sigma*T_s**4
OLR_0 = epsilon*(1-epsilon)*sigma*T_0**4
OLR_1 = epsilon*sigma*T_1**4

OLR = OLR_s + OLR_0 + OLR_1

print 'The expression for OLR is'
OLR
```

Subsitute in the numerical values we are interested in:

```python
OLR2 = OLR.subs([(sigma, 5.67E-8), (T_s, 288.), (T_0, 275.), (T_1, 230.)])
OLR2
```

Now use the `sympy.solve` function to solve the quadratic equation for $\epsilon$:

```python
sympy.solve(OLR2 - 239., epsilon)
```

There are two roots, but the second one is unphysical since we must have $0 < \epsilon < 1$.

We conclude that our tuned value is

$$ \epsilon = 0.58$$

This is the absorptivity that guarantees that our model reproduces the observed OLR given the observed tempertures.

____________

## 4. Level of emission
____________

Even in this very simple greenhouse model, there is **no single level** at which the OLR is generated.

The three terms in our formula for OLR tell us the contributions from each level.

Let's make a row vector of these three terms:

```python
OLRterms = sympy.transpose(sympy.Matrix([OLR_s, OLR_0, OLR_1]))
OLRterms
```

Now evaluate these expressions for our tuned temperature and absorptivity:

```python
tuned = [(T_s, 288), (T_0, 275), (T_1, 230), (epsilon, 0.58), (sigma, 5.67E-8)]
OLRtuned = OLRterms.subs(tuned)
OLRtuned
```

So we are getting about 69 W m$^{-2}$ from the surface, 79 W m$^{-2}$ from layer 0, and 92 W m$^{-2}$ from the top layer.

In terms of fractional contributions to the total OLR, we have (limiting the output to two decimal places):

```python
sympy.N(OLRtuned / 239., 2)
```

Notice that the largest single contribution is coming from the top layer. This is in spite of that the emissions from this layer are weak, because it is so cold.

Comparing to observations, the actual contribution to OLR from the surface is about 22 W m$^{-2}$ (or about 9% of the total), not 69 W m$^{-2}$. So we certainly don't have all the details worked out yet!

As we will see later, to really understand what sets that observed 22 W m$^{-2}$, we will need to start thinking about the spectral dependence of the longwave absorptivity.

____________

## 5. Radiative forcing in the 2-layer leaky greenhouse
____________

Adding some extra greenhouse absorbers will mean that a greater fraction of incident longwave radiation is absorbed in each layer.

Thus **$\epsilon$ must increase** as we add greenhouse gases.

Suppose we have $\epsilon$ initially, and the absorptivity increases to $\epsilon_2 = \epsilon + \delta_\epsilon$.

Suppose further that this increase happens abruptly so that there is no time for the temperatures to respond to this change. **We hold the temperatures fixed** in the column and ask how the radiative fluxes change.

**Do you expect the OLR to increase or decrease?**

Let's use our two-layer leaky greenhouse model to investigate the answer.

The components of the OLR before the perturbation are

```python
OLRterms
```

After the perturbation we have

```python
delta_epsilon = sympy.symbols('delta_epsilon')
OLRterms_pert = OLRterms.subs(epsilon, epsilon+delta_epsilon)
OLRterms_pert
```

Let's take the difference

```python
deltaOLR = OLRterms_pert - OLRterms
deltaOLR
```

To make things simpler, we will neglect the terms in $\delta_\epsilon^2$. This is perfectly reasonably because we are dealing with **small perturbations** where $\delta_\epsilon << \epsilon$.

Telling `sympy` to set the quadratic terms to zero gives us

```python
deltaOLR_linear = sympy.expand(deltaOLR).subs(delta_epsilon**2, 0)
deltaOLR_linear
```

Recall that the three terms are the contributions to the OLR from the three different levels. In this case, the **changes** in those contributions after adding more absorbers.

Now let's divide through by $\delta_\epsilon$ to get the normalized change in OLR per unit change in absorptivity:

```python
deltaOLR_per_deltaepsilon = sympy.simplify(deltaOLR_linear / delta_epsilon)
deltaOLR_per_deltaepsilon
```

Now look at the **sign** of each term. Recall that $0 < \epsilon < 1$. **Which terms in the OLR go up and which go down?**

**THIS IS VERY IMPORTANT, SO STOP AND THINK ABOUT IT.**

The contribution from the **surface** must **decrease**, while the contribution from the **top layer** must **increase**.

**When we add absorbers, the average level of emission goes up!**

### "Radiative forcing" is the change in radiative flux at TOA after adding absorbers

In this model, only the longwave flux can change, so we define the radiative forcing as

$$ R = - \delta OLR $$

(with the minus sign so that $R$ is positive when the climate system is gaining extra energy).

We just worked out that whenever we add some extra absorbers, the emissions to space (on average) will originate from higher levels in the atmosphere.

What does this mean for OLR? Will it increase or decrease?

To get the answer, we just have to sum up the three contributions we wrote above:

```python
R = -sum(deltaOLR_per_deltaepsilon)
R
```

Is this a positive or negative number? The key point is this:

**It depends on the temperatures, i.e. on the lapse rate.**

### Greenhouse effect for an isothermal atmosphere

Stop and think about this question:

If the **surface and atmosphere are all at the same temperature**, does the OLR go up or down when $\epsilon$ increases (i.e. we add more absorbers)?

Understanding this question is key to understanding how the greenhouse effect works.

#### Let's solve the isothermal case

We will just set $T_s = T_0 = T_1$ in the above expression for the radiative forcing.

```python
R.subs([(T_0, T_s), (T_1, T_s)])
```

which then simplifies to

```python
sympy.simplify(R.subs([(T_0, T_s), (T_1, T_s)]))
```

#### The answer is zero

For an isothermal atmosphere, there is **no change** in OLR when we add extra greenhouse absorbers. Hence, no radiative forcing and no greenhouse effect.

Why?

The level of emission still must go up. But since the temperature at the upper level is the **same** as everywhere else, the emissions are exactly the same.

### The radiative forcing (change in OLR) depends on the lapse rate!

For a more realistic example of radiative forcing due to an increase in greenhouse absorbers, we can substitute in our tuned values for temperature and $\epsilon$.

We'll express the answer in W m$^{-2}$ for a 1% increase in $\epsilon$.

The three components of the OLR change are

```python
deltaOLR_per_deltaepsilon.subs(tuned) * 0.01
```

And the net radiative forcing is

```python
R.subs(tuned) * 0.01
```

So in our example, **the OLR decreases by 2.2 W m$^{-2}$**, or equivalently, the radiative forcing is +2.2 W m$^{-2}$.

What we have just calculated is this:

Given the observed lapse rates, a small increase in absorbers will cause a small decrease in OLR.

The greenhouse effect thus gets stronger, and energy will begin to accumulate in the system -- which will eventually cause temperatures to increase as the system adjusts to a new equilibrium.

____________

## 6. Radiative equilibrium in the 2-layer leaky greenhouse
____________

In the previous section we made no assumptions about the processes that actually set the temperatures. We used the model to calculate radiative fluxes, **given observed temperatures**. We stressed the importance of knowing the lapse rates in order to know how an increase in emission level would affect the OLR, and thus determine the radiative forcing.

A key question in climate dynamics is therefore this:

**What sets the lapse rate?**

It turns out that lots of different physical processes contribute to setting the lapse rate. Understanding how these processes acts together and how they change as the climate changes is one of the key reasons for which we need more complex climate models.

For now, we will use our prototype greenhouse model to do the most basic lapse rate calculation: the **radiative equilibrium temperature**.

We assume that

- the only exchange of energy between layers is longwave radiation
- equilibrium is achieved when the net radiative flux convergence in each layer is zero.

```python
E_s = sigma*T_s**4
E_0 = epsilon*sigma*T_0**4
E_1 = epsilon*sigma*T_1**4
E = sympy.Matrix([E_s, E_0, E_1])
E
```

#### The upwelling beam

```python
U = sympy.Matrix([E_s, (1-epsilon)*E_s + E_0, (1-epsilon)*((1-epsilon)*E_s + E_0) + E_1])
U
```

#### The downwelling beam

```python
fromspace = 0
D = sympy.Matrix([(1-epsilon)*((1-epsilon)*fromspace + E_1) + E_0, (1-epsilon)*fromspace + E_1, fromspace])
D
```

```python
# Net flux, positive up
F = sympy.simplify(U - D)
F
```

```python
# The absorption is then simply the flux convergence in each layer

# define a vector of absorbed radiation -- same size as emissions
A = E.copy()

#  absorbed radiation at surface
A[0] = F[0]
# Get the convergence
for n in range(2):
    A[n+1] = -(F[n+1]-F[n])

A = sympy.simplify(A)
A
```

```python
# Solve for radiative equilibrium by setting this equal to zero
T_e = sympy.symbols('T_e')
sympy.solve(A - sympy.Matrix([sigma*T_e**4, 0, 0]),[T_s**4, T_1**4, T_0**4])
```

The radiative equilibrium solution is thus

\begin{align}
T_s &= T_e \left( \frac{2+\epsilon}{2-\epsilon} \right)^{1/4} \\
T_1 &= T_e \left( \frac{1+\epsilon}{2-\epsilon} \right)^{1/4} \\
T_2 &= T_e \left( \frac{ 1}{2 - \epsilon} \right)^{1/4}
\end{align}

Plugging in $\epsilon = 0.58$ gives

\begin{align}
T_s &= 296 \text{ K} \\
T_0 &= 262 \text{ K} \\
T_1 &= 234 \text{ K} \\
\end{align}

Compare these to the values we derived from the observed lapse rates:
\begin{align}
T_s &= 288 \text{ K} \\
T_0 &= 275 \text{ K} \\
T_1 &= 230 \text{ K} \\
\end{align}

The **radiative equilibrium** solution is substantially **warmer at the surface** and **colder in the lower troposphere** than reality.

This is a very general feature of radiative equilibrium, and we will see it again very soon in this course.

### A follow-up assignment

You are now ready to tackle [Assignment 5](../Assignments/Assignment05 -- Radiative forcing in a grey radiation atmosphere.ipynb), where you are asked to extend this grey-gas analysis to many layers.

For more than a few layers, the analytical approach we used here is no longer very useful. You will code up a numerical solution to calculate OLR given temperatures and absorptivity, and look at how the lapse rate determines radiative forcing for a given increase in absorptivity.

[Back to ATM 623 notebook home](../index.ipynb)

____________
## Version information
____________

```python
%install_ext http://raw.github.com/jrjohansson/version_information/master/version_information.py
%load_ext version_information
%version_information numpy, sympy, climlab
```

    Installed version_information.py. To use it, type:
      %load_ext version_information

Software	Version

Python	2.7.9 64bit [GCC 4.2.1 (Apple Inc. build 5577)]

IPython	3.1.0

OS	Darwin 14.3.0 x86_64 i386 64bit

numpy	1.9.2

sympy	0.7.6

climlab	0.2.11

Thu May 14 16:02:40 2015 EDT

____________
## Credits

The author of this notebook is [Brian E. J. Rose](http://www.atmos.albany.edu/facstaff/brose/index.html), University at Albany.

It was developed in support of [ATM 623: Climate Modeling](http://www.atmos.albany.edu/facstaff/brose/classes/ATM623_Spring2015/), a graduate-level course in the [Department of Atmospheric and Envionmental Sciences](http://www.albany.edu/atmos/index.php), offered in Spring 2015.
____________

Lectures/Lecture06 -- Elementary greenhouse models.ipynb


# Chapter 8: Tree-Based Methods
- **Chapter 8 from the book [An Introduction to Statistical Learning](https://www.statlearning.com/)**
- **By Gareth James, Daniela Witten, Trevor Hastie and Rob Tibshirani**
- **Pages from $332$ to $333$**
- **By [Mosta Ashour](https://www.linkedin.com/in/mosta-ashour/)**

**Exercises:**
- **[1.](#1)**
- **[2.](#2)**
- **[3.](#3)**
- **[4.](#4) [(a)](#4a) [(b)](#4b)**
- **[5.](#5)**
- **[6.](#6)**

#  8.4 Exercises
##  Conceptual

### $1.$ Draw an example (of your own invention) of a partition of two-dimensional feature space that could result from recursive binary splitting. Your example should contain at least six regions. Draw a decision tree corresponding to this partition. Be sure to label all aspects of your figures, including the regions $R_1, R_2, \dots,$ the cutpoints $t_1, t_2, \dots,$ and so forth.
### *Hint: Your result should look something like Figures $8.1$ and $8.2$.*

                     [x1 > t1]
                    |         |
            [x2 > t3]         [x2 < t5]
            |       |         |       |
      [x2>t2]       R4     [x1<t6]    [x2<t7]
      |     |              |     |    |     |
[x1>t4]     R3             R5    R6   R7    [x1<t8]
|     |                                     |     |
R1    R2                                    R8    R9

### $2.$ It is mentioned in Section $8.2.3$ that boosting using depth-one trees (or *stumps*) leads to an *additive* model: that is, a model of the form

$$f(X) = \sum_{j=1}^{p}{f_j(X_j)}$$

### Explain why this is the case. You can begin with $(8.12)$ in Algorithm $8.2$.

**Answer:**
>- Boosting has three tuning parameters which the third one is: the number $d$ of splits in each tree, which controls the complexity of the boosted ensemble.
>- When $d = 1$, each tree is a *stump*, consisting of a *single split*.
>- In this case, the boosted ensemble is fitting an additive model, since each term involves only a single variable.
>- With this setting the Boosting for Regression Trees algorithm becomes:
>  1. Set $\hat{f}(x) = 0$ and $r_i = y_i$ for all $i$ in training set.
>  2. For $b = 1, 2, ..., B,$ repeat:

>  $(a)$ Fit a tree $\hat{f^b}$ with **1** split to the training data $(X, r)$.

>  $(b)$ Update $\hat{f}$ by adding the new tree:

>  $$\hat{f}(x) \leftarrow \hat{f}(x) + \hat{f}^b(X_j)$$
 **Note:** it can't be shrunken because it is already as small as it can possibly be, also note that it is a function of a single variable.

>  $(c)$ Update the residuals,
>  $$r_i \leftarrow r_i - \hat{f}^b(X_j)$$
>  3. Output the boosted model,
$$\hat{f}(x) = \sum_{b=1}^{B}{\hat{f}^b(X_j)}$$
>- Because the model is using depth-one trees, each $\hat{f}^b$ generated in step $2$ is a function of a single feature $\hat{f}^b(X_j)$ and so the model output in step $3$ is a sum of functions of a given single variable which is presented as the additive model.

### 3. Consider the Gini index, classification error, and cross-entropy in a simple classification setting with two classes. Create a single plot that displays each of these quantities as a function of $\hat{p}_{m1}$. The $x$-axis should display $\hat{p}_{m1}$, ranging from $0$ to $1$, and the $y$-axis should display the value of the Gini index, classification error, and entropy.

### *Hint: In a setting with two classes, $\hat{p}_{m1} = 1 - \hat{p}_{m2}$. You could make this plot by hand, but it will be much easier to make in R.*

**Answer:**
- Recall these settings from the book page $312$. For $k= \{1,2\}$:

>- $\text{Classification Error}:$
>  $$ \begin{align}
E &= 1 - \mathop{max}_k(\hat{p}_{mk}) \tag{8.5}\\
  &= 1 - \mathop{max}_k\{\hat{p}_{m1}, \hat{p}_{m2}\}
\end{align}$$

>- $\text{Gini Index}:$
>  $$ \begin{align}
G &= \sum_{k=1}^K \hat{p}_{mk}(1 - \hat{p}_{mk}), \tag{8.6}\\
  &= \hat{p}_{m1}(1 - \hat{p}_{m1}) + \hat{p}_{m2}(1 - \hat{p}_{m2})
\end{align}$$

>- $\text{Entropy}:$
>  $$ \begin{align}
D &= - \sum_{k=1}^K \hat{p}_{mk} log \hat{p}_{mk}. \tag{8.7}\\
  &= - \hat{p}_{m1} log \hat{p}_{m1} - \hat{p}_{m2} log \hat{p}_{m2}
\end{align}$$

```python
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

pm1 = np.arange(0.01, 1, 0.01)
pm2 = 1 - pm1

# Classification Error:
clf_err = 1 - np.maximum(pm1, pm2)

# Gini:
gini = (pm1 * (1 - pm1)) + (pm2 * (1 - pm2))

# Entropy:
entropy = - (pm1 * np.log(pm1)) - (pm2 * np.log(pm2))

# dataframe
df = pd.DataFrame({'Classification_Error': clf_err,
                   'Gini': gini,
                   'Entropy': entropy}).set_index(pm1)

plt.figure(figsize=(10, 8))
sns.lineplot(data=df);
```

### $4.$ This question relates to the plots in Figure $8.12$.

**$(a)$ Sketch the tree corresponding to the partition of the predictor space illustrated in the left-hand panel of Figure $8.12$. The numbers inside the boxes indicate the mean of $Y$ within each region.**
                 [X1 < 1]
                |        |
         [X2 < 1]        5
         |      |
[ X1 < 0 ]      15
|        |
3        [X2 < 0]
         |      |
         10     0

**$(b)$ Create a diagram similar to the left-hand panel of Figure $8.12$, using the tree illustrated in the right-hand panel of the same figure. You should divide up the predictor space into the correct regions, and indicate the mean for each region.**

```python
# dividing up the predictor space into regions
plt.figure(figsize=(8, 8))
plt.xlim([-1, 3])
plt.ylim([-1, 3])

plt.axhline(1)
plt.axhline(2)
plt.axvline(1, ymax=.5)
plt.axvline(0, ymin=.5, ymax=.75)

# plot the mean for each region
means = {'-1.08': (0, 0),
         '0.63': (2, 0),
         '-1.06': (-0.5, 1.5),
         '0.21': (1.5, 1.5),
         '2.49': (1, 2.5)}

for k, (p1, p2) in means.items():
    plt.text(p1, p2, k, fontsize='xx-large', ha='center');
```

### $5.$ Suppose we produce ten bootstrapped samples from a data set containing red and green classes. We then apply a classification tree to each bootstrapped sample and, for a specific value of $X$, produce $10$ estimates of $\text{P(Class is Red|X)}$:
 $$0.1, 0.15, 0.2, 0.2, 0.55, 0.6, 0.6, 0.65, 0.7, \text{and } 0.75.$$
 There are two common ways to combine these results together into a single class prediction. One is the majority vote approach discussed in this chapter. The second approach is to classify based on the average probability. In this example, what is the final classification under each of these two approaches?

```python
res = np.array([0.1, 0.15, 0.2, 0.2, 0.55, 0.6, 0.6, 0.65, 0.7, 0.75])

# Majority vote approach
majority = (0.5 < res).sum()

# The average Probability
average = np.mean(res)

print(f'Is red (by Majority): {majority > .5}, majority = {majority}')
print(f'Is red (by Average) : {average  > .5}, avg = {average}')
```

    Is red (by Majority): True, majority = 6
    Is red (by Average) : False, avg = 0.45

### $6.$ Provide a detailed explanation of the algorithm that is used to fit a regression tree.
**Answer:**
> The Regression Tree algorithm is shown as Algorithm  $8.1$  in the book. The algorithm is all about building a single tree for predictors, therefore we tend to select the best individual tree by applying **cost complexity pruning** and **K-fold cross-validation** to choose the best $\alpha$.
 It requires $4$ steps to build a Regression Tree, as shown in Algorithm $8.1$:

> $\text{Step 1:}$ Use **recursive binary splitting** to grow a large tree on the training data, stopping only when each terminal node has fewer than some minimum number of observations.
>- We take a top-down, greedy approach that is known as **recursive binary splitting**. The approach is *top-down* because it begins at the top of the tree (at which point all observations belong to a single region) and then successively splits the predictor space; each split is indicated via two new branches further down on the tree. It is *greedy* because at each step of the tree-building process, the best split is made at that particular step, rather than looking ahead and picking a split that will lead to a better tree in some future step.
>- We first select the predictor $X_j$ and the cutpoint $s$ such that splitting the predictor space into the regions $\{X|X_j < s\}$ and $\{X|X_j \geq s\}$ leads to the greatest possible
reduction in $\text{RSS}$, and then choose the predictor and cutpoint such that the resulting tree has the lowest $\text{RSS}$.
>- Next, we repeat the process, looking for the best predictor and best cutpoint in order to split the data further so as to minimize the $\text{RSS}$ within each of the resulting regions.
>- However, this time, instead of splitting the entire predictor space, we split one of the two previously identified regions.
>- We now have three regions. Again, we look to split one of these three regions further, so as to minimize the $\text{RSS}$. The process continues until a stopping criterion is reached; for instance, we may continue until no region contains more than five observations "as in Figure $8.3$".
>- Once the regions $R_1, \dots , R_J$ have been created, we predict the response for a given test observation using the mean of the training observations in the region to which that test observation belongs.
>- And finally, this process may produce good predictions on the training set, but is likely to **overfit** the data, leading to poor test set performance.

> $\text{Step 2:}$ Apply **cost complexity pruning** to the large tree in order to obtain a sequence of best subtrees, as a function of $\alpha$.
>- One possible strategy is that we can build the tree only so long as the decrease in the $\text{RSS}$ due to each split exceeds some (high) threshold.
>- Therefore, a better strategy is to grow a very large tree $T_0$, and then $\text{prune}$ it back in order to obtain a $\text{subtree}$.
>- To select a subtree with the lowest test error rate, we can estimate its test error using cross-validation or the validation set approach. But estimating the CV error for every possible subtree would be computationally expensive "Cumbersome".
>- So, Cost $\text{complexity pruning}$ also known as $\text{weakest link pruning}$ gives us a way to do just this. Rather than considering every possible subtree, we consider a sequence of trees indexed by a nonnegative tuning parameter $\alpha$.
>- For each value of α there corresponds a subtree $T \subset T_0$ is as small as possible such that
$$
\sum_{m=1}^{|T|} \sum_{i: x_i \in R_m} (y_i - \hat{y}_{Rm})^2 + \alpha |T|  \tag{8.4}
$$
>- Here the tuning parameter $\alpha$ controls a trade-off between the subtree's complexity and its fit to the training data.
>  - **Quick reminder:** (Equation $(8.4)$ is a similar $\text{cost + penalty}$ format as regularization methods as the **lasso** $(6.7)$ from Chapter $6$, in which a similar formulation was used in order to control the complexity of a linear model.)
>- When $\alpha$ = 0, then the subtree $T$ will simply equal to $T_0$, because then $(8.4)$ just measures the training error "cost".
>- As we start to increase $\alpha$, branches get pruned from the tree in a nested and predictable fashion, so obtaining the whole sequence of subtrees as a function of $\alpha$ is easy.

> $\text{Step 3:}$ We can select a value of $\alpha$ using a **validation set** or using **cross-validation**. We then return to the full data set and obtain the subtree corresponding to $\alpha$ as following.
>- Use **K-fold cross-validation** to choose $\alpha$. That is, divide the training observations into $K$ folds. For each $k = 1, \dots ,K$:
>  - $(a)$ Repeat Steps $1$ and $2$ on all but the $k$th fold of the training data.
>  - $(b)$ Evaluate the mean squared prediction error on the data in the left-out $k$th fold, as a function of $\alpha$. Average the results for each value of $\alpha$, and pick $\alpha$ to minimize the average error.

> $\text{Step 4:}$ Return the subtree from Step $2$ that corresponds to the chosen value of $\alpha$, re-fit a regression tree on the full dataset to obtain the subtree, and use this final form for prediction.

# Done!