```python
%pylab inline
```

    Populating the interactive namespace from numpy and matplotlib

    c:\users\isomorphism\appdata\local\programs\python\python36\lib\site-packages\IPython\core\magics\pylab.py:160: UserWarning: pylab import has clobbered these variables: ['f']
    `%matplotlib` prevents importing * from pylab and numpy
      "\n`%matplotlib` prevents importing * from pylab and numpy"

# Probability Theory

## Week 1

**Topics:** Random sampling, permutations, combinations.

**Problems:** 3.1, 3.3, 3.4, 3.5, 3.6, additional problem set 1.

### Random sampling

Taking a random sample can be done in different ways:

||With replacement|Without replacement|
|---|---|---|
|**With order**|$n^k$|$n!, \quad k \leq n$|
|**Without order**|$\begin{pmatrix}n+k-1\\k\end{pmatrix}$|$\begin{pmatrix}n\\k\end{pmatrix}$|

This leads to four different cases which will be addressed seperately.

### Permutations

The general formula for permutations is:

$$ _nP_k = \dfrac{n!}{(n-k)!}$$

#### With replacement

#### Without replacement

### Combinations

The general formula for combinations is (which can also be written as the binomial coefficient):

$$ _nC_k=\dfrac{n!}{k!(n-k)!} = \begin{pmatrix}n\\k\end{pmatrix}$$

#### With replacement

#### Without replacement

## Week 2

**Topics**: Probability definitions, probability rules, grid chart.

**Problems:** 3.7, 3.8, 3.10, additional problem set 2.

### Probability definitions

#### Probability experiment

There are different examples of probability experiments:

1. One throw with a dice
2. Two throws with a dice
3. One throw with a coin
4. Pulling two cards from a deck of cards

#### Sample space

The sample space $\Omega$ are all the possible outcomes for a probability experiment. For the above given examples this gives:

1. $\Omega=\{1,2,3,4,5,6\}$
2. $\Omega=\{(a,b):a,b \in \{1,2,3,4,5,6\}\}$
3. $\Omega=\{H,T\}$
4. $\Omega=\{(a,b):a,b \in \{1,\ldots,52\}\}$

#### Event

An event is a subset of $\Omega$.

1. Throwing an even number, $A=\{2,4,6\}$
2. Throwing the same dices, $A=\{(1,1),(2,2),\ldots,(6,6)\}$
3. Throwing heads, $A=\{H\}$
4. Picking a heart A ($HA$) and club 2 ($C2$), $A=\{(HA,C2),(C2,HA)\}$

#### Elementary event

An elementary event $\omega$ (also called atomic event) is exactly one element from $\Omega$.

For example (1) all the elementary events are: $\{1\}$, $\{2\}$, $\{3\}$, $\{4\}$, $\{5\}$, $\{6\}$.

#### Probability (axiomatic definition)

For the probability function $P$ the following three axioms hold:

1. $P(S)=1$
2. $0\leq P(A) \leq 1$
3. $P(A \cap B) = P(A)+P(B)-P(A \cap B)$, where $P(A \cap B) = 0$ if and only if $A \cap B = \emptyset$.

#### Probability space

A probability space consists of the following:

1. A sample space $\Omega$ which are all the possible outcomes for the experiment.
2. A set of events, where each event is a set containing zero or more outcomes.
3. The assigment of probabilities to the events; that is, a function $P$ from events to probabilities.

With this we can determine the probability for an event $A$:

$$P(A) = \dfrac{n_a}{n_\Omega}$$

Where $n_a$ are the favorable outcomes and $n_\Omega$ are all the possible outcomes.

### Probability rules

Now we establish a few rules for probabilites. Later on when we cover conditional probilities we will add a few more rules to the list.

#### Sum rule

$P(A \cup B) = P(A)+P(B)-P(A \cap B)$ where $P(A \cap B) = 0$ if and only if $A \cap B = \emptyset$.

#### Complement rule

We can define this as $P(A) + P(\bar{A}) = P(S) = 1$. We use this to infer that $P(A) = 1-P(\bar{A})$. This is particularly useful because most of the times it is a lot easier to figure out what $P(\bar{A})$ is, and then use the complement rule to determine the probability of $P(A)$.

### Grid chart

A grid chart is useful to visualize conditions.

For example, suppose we want to throw $2$ dices, and we multiply the outcome. What is the probability that the result is greater than $20$?

Using the grid chart it is easy to see that:

$$P(x>20) = \frac{6}{6^2} = \frac{1}{6}$$

## Week 3

**Topics:** Conditional probability, independence, product rule, sampling with replacement, sampling without replacement, probability tree.

**Problems:** 3.2, 3.12, 3.13, 3.14, 3.15, 3.16, additional problem set 3.

### Conditional probability

A conditional probability gives more information about the possible outcome space, which is defined as:

$$P(A|B) = \dfrac{P(A \cap B)}{P(B)} ,\quad P(B)>0$$

### Indepence

If $P(A|B)=P(A)$ then the events $A$ and $B$ are independent.

### Product rule

The general product rule is:

$$ P(A \cap B) = P(A|B) \cdot P(B) = P(B|A) \cdot P(A) $$

In the case that $A$ and $B$ are independent, we use:

$$ P(A \cap B) = P(A) \cdot P(B)$$

### Probability rules

With the addition of above rules, we now have the following rules for probabilities:

1. $P(\Omega)=1$, sample space has a probability of 1.
2. $P(\emptyset)=0$, an empty set has a probability of 0.
3. $P(A)=1-P(\bar{A})$, complement rule.
4. $P(A\cup B)=P(A)+P(B)-P(A\cap B)$, sum rule.
5. $P(A\cup B)=P(A)+P(B)$, if and only if $A\cap B=\emptyset$.
6. $P(A\cap B)=P(A|B)\cdot P(B)$, product rule.
7. $P(A\cap B)=P(A)\cdot P(B)$, if and only if $A$ and $B$ are independent.

### Sampling with replacement

### Sampling without replacement

### Probability tree

## Week 4

**Topics:** Bayes' rule.

**Problems:** 3.9, 3.11, 3.17 till 3.31.

### Bayes' rule

#### Inference

When we have a cause and effect model where $A_i\xrightarrow[P(B|A_i)]{\text{cause-effect}}B$ is known, we can use Bayes' rule to infer $B\xleftarrow[P(A_i|B)]{\text{inference}}A_i$.

**General case**

The general use case for Bayes rule is:

$$ P(A|B) = \dfrac{P(A) \cdot P(B|A)}{P(A)\cdot P(B|A) + P(\bar{A})\cdot P(B|\bar{A})} $$

We can use this to infer $P(A|B)$ if only $P(B|A)$ is known.

**Definition**

Bayes' rule is defined as:

$$ P(A_i|B) = \dfrac{P(A_i)\cdot P(B|A_i)}{\sum\left[P(A_j)\cdot P(B|A_j)\right]}$$

such that the following holds:

1. $A_i \cap A_j = \emptyset $, the intersection must be disjunct.
2. $\cup A_i = \Omega$, the union of $A$ is $\Omega$.

## Week 5

**Topics:** Discrete stochastic variable, discrete probability function, cumulative distribution function, expected value, variance for a discrete stochastic variable.

**Problems:** 4.1, 4.3, 4.4, 4.5, 4.6, 4.7, 4.8

### Stochastic variables

**Definition**

Given a sample space $\Omega$ and a function $\underline{k}$ such that $\underline{k}:\Omega\rightarrow\mathbb{R}$. Where the range is $\underline{R}_k \subset \mathbb{R}$. In this context $\underline{k}$ is the stochastic variable (or probability variable). With the following two rules:

1. $f(k) \geq 0$ for $\forall k$
2. $\sum f(k) = 1, \quad \left(\sum\limits_{k\in\underline{k}}(\underline{k}=k)=1\right)$

**Example**

Given a probability experiment: throwing with a dice.

We define $\underline{k}$ as the squared outcome of the throw. The mapping of the function $\underline{k}$ is defined as:

$$\Omega=\underbrace{\{1,2,3,4,5,6\}}_{\text{domain}} \xrightarrow{\underline{k}} \underbrace{\{1,4,9,16,25,36\}}_{\text{range}} = \underline{R}_k \subset \mathbb{R}$$

### Discrete probability distribution

The probability distribution is defined as $f(k)=P(\underline{k}=k)$ such that $k\in\underline{R}_k$. For the above given example this yields:

$$ f(1)=P(\underline{k}=1)=\dfrac{1}{6} \\ f(4)=P(\underline{k}=4)=\dfrac{1}{6} \\ \vdots \\ f(36)=P(\underline{k}=36)=\dfrac{1}{6}$$

**Code**

```python
def f(k):
    r = [1,4,9,16,25,36]
    if k in r: return 1/len(r)
    return 0
```

Plotting this function gives a pin bar graph with a vertical line at the value when there is a probability defined. All the other values are not defined because this is a discrete function.

```python
points = np.arange(0,37,0.01)
plot(points, [f(x) for x in points], c='b', lw=2);
```

### Cumulative distribution function

The cumulative distribution function $F(k)=P(\underline{k}\leq k)$ for the example above yields:

$$ F(1)=P(\underline{k}\leq1)=\dfrac{1}{6} \\ F(2)=P(\underline{k}\leq2)=\dfrac{1}{6} \\ F(4)=P(\underline{k}\leq4)=\dfrac{2}{6} \\ \vdots \\ F(36)=P(\underline{k}\leq36)=\dfrac{6}{6}=1$$

**Code**

```python
def F(k):
    x = 0
    r = [1,4,9,16,25,36]
    for i in r:
        if i <= k: x = i
    try: return (r.index(x)+1)/len(r)
    except: return 0
```

Plotting this function gives a staircase graph.

```python
points = np.arange(-10,50,0.01)
plot(points, [F(x) for x in points], c='b', lw=2);
```

### Expected value of a discrete stochastic variable

The expected value $E(\underline{k})$ is the weighted arithmetic mean from descriptive statistics but altered for a stochastic variable.

**Definition**

The formula for the expected value is:

$$ E(\underline{k})=\sum\limits_{k\in\underline{k}} k\cdot P(\underline{k}=k) $$

Notice that we do not divide by the sum of values since that sum would equal 1, hence it's omitted.

**Example**

The expected value for the example with a dice is:

$$E(\underline{k})=1 \cdot \dfrac{1}{6} + 4 \cdot \dfrac{1}{6} + 9 \cdot \dfrac{1}{6} + 16  \cdot \dfrac{1}{6} + 25 \cdot \dfrac{1}{6} + 36 \cdot \dfrac{1}{6} = \dfrac{91}{6} = 15 \dfrac{1}{6}$$

**Code**

```python
def E(r):
    return sum([x * 1/len(r) for x in r])
```

We can use this to easily determine the expected value for any range of $\underline{k}$ when there is a uniform distribution.

```python
expected = E([1,4,9,16,25,36])
expected
```

    15.166666666666668

Plotting $f(k)$, $F(k)$, and $E(\underline{k})$ yields:

```python
points = np.arange(0,50,0.01)
plot(points, [f(x) for x in points], c='lightblue', lw=2);
plot(points, [F(x) for x in points], c='b', lw=2);
scatter(expected, F(expected), c='r', lw=4);
```

### Variance of a discrete stochastic variable

The variance $\text{Var}(\underline{k})$ is the variance from descriptive statistics but is altered for a discrete stochastic variable.

**Definition**

The formula for the variance of a discrete stochastic variable is defined as:

$$ \text{Var}(\underline{k}) = \sum\limits_{k\in\underline{k}}\left(k-E(\underline{k})\right)^2\cdot P(\underline{k}=k_i) $$

Notice that we do not divide by the sum of probabilities since that sum would equal 1, hence it's omitted.

**Example**

An example for throwing a dice gives:

$$\text{Var}(\underline{k})= \left(1-\dfrac{91}{6}\right)^2 + \left(4-\dfrac{91}{6}\right)^2 + \ldots + \left(36-\dfrac{91}{6}\right)^2 = 149.14$$

**Standard deviation**

With the example given above, it is easy to determine the standard deviation, which is:

$$\sigma_{\underline{k}} = \sqrt{149.14} = 12.21$$

**Alternative method**

An alternative (preferred) method to calculate the variance of a discrete stochastic variable is:

$$\text{Var}(\underline{k})= E(\underline{k}^2)-\left[E(\underline{k})\right]^2$$

Proof:

We start with the definition:

$$\begin{align} &\sum\limits_{k\in\underline{k}}\left(k-E(\underline{k})\right)^2 \cdot P(\underline{k}=k) \\ = &\sum\limits_{k\in\underline{k}}\left(k^2 - 2k\cdot E(\underline{k})+\left[E(\underline{k})\right]^2 \right) \cdot P(\underline{k}=k) \\ = & \underbrace{\sum\limits_{k\in\underline{k}}k^2\cdot P(\underline{k}=k)}_{\text{def.}\ E(\underline{k}^2)}-2\sum\limits_{k\in\underline{k}}k\cdot E(\underline{k})\cdot P(\underline{k}=k)+\sum\limits_{k\in\underline{k}}\left[E(\underline{k})\right]^2\cdot P(\underline{k}=k) \\ = &E(\underline{k}^2) - 2\cdot E(\underline{k}) \cdot \underbrace{\sum\limits_{k\in\underline{k}} k \cdot P(\underline{k}=k)}_{\text{def.}\ E(\underline{k})} + \left[E(\underline{k})\right]^2 \cdot \underbrace{\sum\limits_{k\in\underline{k}} P(\underline{k}=k)}_{1} \\ = &E(\underline{k}^2)-2\cdot E(\underline{k})\cdot E(\underline{k})+\left[E(\underline{k})\right]^2 \\ = & E(\underline{k}^2) - \left[E(\underline{k})\right]^2 \end{align}$$

Which proves our formula.

**Code**

```python
def var(r):
    expected = E(r)
    return sum([(x-expected)**2*f(x) for x in r])
```

```python
var([1,4,9,16,25,36])
```

    149.13888888888886

```python
def var2(r):
    return E([x**2 for x in r]-E(r)**2)
```

```python
var2([1,4,9,16,25,36])
```

    149.13888888888886

```python
def stdev(r):
    return math.sqrt(var2(r))
```

```python
stdev([1,4,9,16,25,36])
```

    12.212243401148244

## Week 6

**Topics:** Rules for expected value and variance, summing stochastic variables, multi-dimensional stochastic variables.

**Problems:** 4.9 till 4.13, 4.20, 4.21, additional problem set 4.

### Applying linear transformations

When we apply a linear transformation of the form $\underline{m}=\alpha \underline{k} + A$ to our values, there are a few rules that apply to the expected value and variance that will help us. Instead of recalculating the expected value and/or variance we can use the observations below.

#### Multiplying with a scalar $\alpha$

When we multiply with a scalar $\alpha$, the expected value will also change by that factor:

$$E(\underline{m}) = E(\alpha\underline{k})=\alpha E(\underline{k})$$

If we apply a scalar $\alpha$ the variance will change with a factor $\alpha^2$:

$$\text{Var}(\underline{m})=\text{Var}(\alpha\underline{k})=\alpha^2\text{Var}(\underline{k})$$

Even if a negative value for $\alpha$ will be used, the standard deviation will remain positive, thus:

$$\sigma_{\underline{m}}=\sigma_{\alpha\underline{k}}=\sqrt{\alpha^2 \text{Var}(\underline{k})}=\ |\ \alpha \ |\ \sigma_\underline{k}$$

#### Adding a constant $A$

When we add a constant to the outcomes the expected value will also change by that constant value:

$$E(\underline{y}) = E(\underline{k} + A) = E(\underline{x})+A$$

If we shift all the values over a constant value $A$, the variance will not be affected because the spread between the variables will remain the same.

### Rules for expected value and variance

#### Sum rule for expected values

The expected value of the sum is the sum of the expected values:

$$ E(\underline{k}_{sum}) = E(\underline{k}_1) + E(\underline{k}_2) $$

#### Sum rule for variance

The variance of the sum is the sum of the variances:

$$ \text{Var}(\underline{k}_{sum}) = \text{Var}(\underline{k}_1) + \text{Var}(\underline{k}_2) $$

### Multidimensional stochastic variables

A multidimensional stochastic variable is defined as:

$$\begin{align}  f(k)&=P(\underline{k}=k),\ \text{any}\ m) \\&=\sum\limits_j P(\underline{k}=k, \underline{m}=m_j)\sum\limits_jp_{kj} \end{align}$$

## Week 7

### Populatiecorrelatiecoëfficiënt $\rho$

Om de populatiecorrelatiecoefficient van twee stochasten te berekenen volgen we de onderstaande stappen:

1. Bereken $E(\underline{x})$ en $E(\underline{y})$.
2. Bereken $E(\underline{x}^2)$ en $E(\underline{y}^2)$.
3. Bereken $\text{Var}(\underline{x})$ and $\text{Var}(\underline{y})$.
4. Bereken $E(\underline{x}\cdot\underline{y})$.
5. Bereken $\text{Cov}(\underline{x}, \underline{y})$.
6. Bereken $\rho(\underline{x}, \underline{y})$.

Waarbij $\text{Cov}(\underline{x},\underline{y})$ is gedefinieerd als:

$$ \text{Cov}(\underline{x},\underline{y}) = \sum\limits_{i=1}^r\sum\limits_{j=1}^k(x_i-\mu_\underline{x})(y_j-\mu_\underline{y})\cdot P(\underline{x}=x_i \land \underline{y} = y_j)$$

En net als bij Beschrijvende Statistiek, is $\rho$ gedefinieerd als:

$$ \rho = \frac{ \text{Cov}(\underline{x}, \underline{y}) }{\sigma_\underline{x} \cdot \sigma_\underline{y}} $$

### Simultane kansverdeling

### Onderling onafhankelijk

Twee stochasten zijn onderling onafhankelijk als:

$$ P(\underline{x}=x_i \land \underline{y}=y_i)=P(\underline{x}=x_i)\cdot P(\underline{y}=y_i)$$

voor alle getallen paren $(x_i,y_i)$.

```python

```


```python
# Erasmus+ ICCT project (2018-1-SI01-KA203-047081)

# Toggle cell visibility

from IPython.display import HTML
tag = HTML('''
Toggle cell visibility <a href="javascript:code_toggle()">here</a>.''')
display(tag)

# Hide the code completely

# from IPython.display import HTML
# tag = HTML('''<style>
# div.input {
#     display:none;
# }
# </style>''')
# display(tag)
```

Toggle cell visibility here.

```python
%matplotlib notebook
import numpy as np
import control as control
import matplotlib.pyplot as plt
import ipywidgets as widgets
import scipy.signal as signal
continuous_update=False
```

## Risposta dei sistemi del primo ordine

La funzione di trasferimento del sistema del primo ordine selezionato è definita come

\begin{equation}
    \frac{K_p}{\tau_p s+1},
\end{equation}

dove $K_p$ e $\tau_p$ sono parametri del sistema.

La risposta del sistema dipende dal segnale di ingresso. In questo esempio vengono utilizzati la funzione gradino (trasformata di Laplace $\frac{1}{s}$), la funzione impulso unitario (trasformata di Laplace $1$), la funzione rampa unitaria (trasformata di Laplace $\frac{1}{s^2}$) e un segnale sinusoidale (trasformata di Laplace $\frac{1}{s^2+1}$) come segnali di ingresso.

Il grafico sottostante mostra il segnale di ingresso e il segnale di uscita corrispondente per i valori scelti dei parametri $K_p$ e $\tau_p$.

### Come usare questo notebook?

Testa le diverse funzioni di ingresso (gradino, impulso, rampa e sinusoide). Sposta gli sliders per modificare i valori di $K_p$ e $\tau_p$.

```python
# sinus, step, ramp, x^2, sqrt(x)
functionSelect = widgets.ToggleButtons(
    options=[('gradino unitario', 0), ('impulso unitario', 1), ('rampa unitaria', 2), ('sinusoide', 3)],
    description='Seleziona: ')

fig = plt.figure(num='Risposta del sistema del primo ordine')
fig.set_size_inches((9.8, 3))
fig.set_tight_layout(True)
f1 = fig.add_subplot(1, 1, 1)

f1.grid(which='both', axis='both', color='lightgray')

f1.set_xlabel('$t$ [s]')
f1.set_ylabel('input, output')

f1.axhline(0,Color='black',linewidth=0.5)
f1.axvline(0,Color='black',linewidth=0.5)

inputf, = f1.plot([],[])
responsef, = f1.plot([],[])
arrowf, = f1.plot([],[])

num_samples=2041

def create_draw_functions(Kp,taup,index):
    t=np.linspace(-0.1,5,num_samples)

    num=[Kp]
    den=[taup,1]
    Wsys=control.tf(num,den)

    global inputf,responsef, arrowf

    if index==0:
        yin=np.zeros(2041)
        yin[40:num_samples]=1
        tnew=np.linspace(0,5,2001)
        tout,yout=control.step_response(Wsys, T=tnew)
    elif index==1:
        yin=signal.unit_impulse(2001, 0)
        tnew=np.linspace(0,5,2001)
        t=tnew
        tout,yout=control.impulse_response(Wsys,tnew,X0=0)
    elif index==2:
        yin=np.zeros(num_samples)
        yin[40:num_samples]=np.linspace(0,5,2001)
        tnew=np.linspace(0,5,2001)
        tout,yout,xx=control.forced_response(Wsys,tnew,yin[40:])
    elif index==3:
        yin=np.sin(np.linspace(0,30,2001))
        tnew=np.linspace(0,30,2001)
        t=tnew
        tout,yout,xx=control.forced_response(Wsys,tnew,yin)

    f1.lines.remove(inputf)
    f1.lines.remove(responsef)
    f1.lines.remove(arrowf)

    inputf, = f1.plot(t,yin,color='C0',label='input')
    responsef, = f1.plot(tout,yout,color='C1',label='output')

    if index == 1:
        arrowf, = f1.plot([-0.1,0,0.1],[0.95,1,0.95],color='C0')
    else:
        arrowf, = f1.plot([],[])

    f1.legend()

    f1.relim()
    f1.relim()
    f1.autoscale_view()
    f1.autoscale_view()

Kp_slider = widgets.FloatSlider(value=1, min=0, max=2, step=0.1, description='$K_p$',
                          continuous_update=True, layout=widgets.Layout(width='auto', flex='5 5 auto'),readout_format='.1f')

taup_slider = widgets.FloatSlider(value=1, min=0, max=2, step=0.1, description='$\\tau_p$',
                          continuous_update=True, layout=widgets.Layout(width='auto', flex='5 5 auto'),readout_format='.1f')

input_data = widgets.interactive_output(create_draw_functions, {'Kp':Kp_slider,
                                                                'taup':taup_slider,
                                                                'index':functionSelect})

def update_sliders(index):
    global x0_slider

    Kpval = [1,1,1,1]
    Kp_slider.value = Kpval[index]
    taupval = [1,1,1,1]
    taup_slider.value = taupval[index]

input_data2 = widgets.interactive_output(update_sliders, {'index':functionSelect})

display(functionSelect)

display(Kp_slider,taup_slider,input_data)

# display(Markdown("The system can be represented as $f(x)=5$ for small excursions of x about x0."))
```

    <IPython.core.display.Javascript object>

    ToggleButtons(description='Seleziona: ', options=(('gradino unitario', 0), ('impulso unitario', 1), ('rampa un…

    FloatSlider(value=1.0, description='$K_p$', layout=Layout(flex='5 5 auto', width='auto'), max=2.0, readout_for…

    FloatSlider(value=1.0, description='$\\tau_p$', layout=Layout(flex='5 5 auto', width='auto'), max=2.0, readout…

    Output()


# Computação Científica com Julia

 * Computação científica
 * Julia
## Como são as coisas no IPT hoje?

 * Planilha eletrônica
 * Excel
 * Deus nos acuda!

# Será que isso é o suficiente???

 * Como detectar erros em planilhas?
 * Como reutilizar cálculos já feitos?
 * Como fazer algo mais complexo?
 * Como fazer **gráficos decentes**?

## O problema do Excel é mais um problema de cultura do que qualquer outra coisa!

# O que eu quero dizer com cultura:

 * Modelagem matemática de problemas se resume ao que se fazia 100 anos atrás.
 * Pouco reaproveitamento
 * Pouco compartilhamento de trabalho já realizado
 * Endeusamento do que já funciona
 * Pouca discussão e ossificação do conhecimento

## Exemplo do que já ocorreu no passado

 * Túnel de vento (tuninho) circa 1990 -  Mestrado do Nilson
 * Algor - escoamento potencial
 * STAN5 (ou 7 de acordo com o Nilson)
 * Modelo em escala reduzida
 * Medição com fio quente

# Minha opinião

 * Compreender o que ocorre numa bancada ou em um trabalho externo
 * Dar ao computador o seu lugar de direito: um escravo muito burro mas muito forte!
 * Muito do que nós fazemaos é software. **Engenharia de Software**
 * Conhecer o que se faz aqui dentro e fora
 * Compartilhar este conhecimento

# Ideal

 * Todas as bancadas modeladas
   * Modelos hidráulicos **e**
   * Modelos CFD
   * Conhecimento profundo do que ocorre de fato nas bancadas.
 * Um modelo das plantas em campo **antes** de medir.
 * Cada área ter um conjunto de ferramentas bem conhecidas
 * Ser "piloto" de software não vai nos levar longe

# Computação Científica

 * Métodos básicos
   * Sistemas lineares
   * Aproximação
   * Equações diferenciais
   * Sistemas não lineares
 * Mais importante do que o básico, é saber como usar isso.

 * Métodos numérico rápidos e eficientes
   * Muito trabalhoso
   * Muito difícil
   * Coisa de profissa.

# Porque Julia

1. Ambiente interativo
   * Muito melhor que FORTRAN ou C/C++/Pascal/Java
   * Parecido com R/Python/Matlab/Mathematica/Scilab
2. Desempenho
   * Consegue desempenho de C/FORTRAN
   * Mas não precisa vetorizar
   * Praticamente tudo é implementado em Julia!
3. Biblioteca extensa
   * Visualização, Álgebra Linear, equações diferenciais, etc
   * Ainda não tem tudo o que Matlab tem
   * Muito fácil chamar Python (que hoje tem um ecossistema impressionante)
4. A linguagem é muito boa!

# Engenharia de Software

 * Controle de versões e repositórios de software (git e github)
 * Documentação no código (*literate programming*)
 * Testes unitários
 * Pensar no ambiente de trabalho:
   * Editor
   * Interface Notebook - Jupyter
   * REPL (terminal)
   * *Muito do que nós fazemos é programação exploratória*

# Estrutura do curso

Objetivo final:
$$
\nabla\cdot\pmb{u} = 0\\
\frac{\partial \pmb{u}}{\partial t} + \pmb{u}\cdot\nabla\pmb{u} =
-\nabla p + \frac{1}{Re}\nabla^2\pmb{u}
$$

Precisamos saber resolver este tipo de equação:
$$
\nabla^2 \phi = f\\
\frac{\partial \phi}{\partial t} = \alpha\nabla^2
\phi\\
\pmb{u}\cdot\phi = \alpha\nabla^2\phi
$$

Vamos focar no problema 1D para depois tentar avançar em outras direções

# Em paralelo: use algum software para resolver um problema concreto:

 * [OpenFoam](https://www.openfoam.com/)
 * [SU2](https://su2code.github.io/)
 * [CalculiX](http://www.calculix.de/)
 * [Code Saturne](https://www.code-saturne.org/)
 * [Code Aster](https://www.code-aster.org/)
 * [Fenics](https://fenicsproject.org/)
 * [FreeFem++](https://freefem.org/)
 * [Dedalus Project](http://dedalus-project.org/)
 * [Nektar](https://www.nektar.info/)
 * [MEEP](https://meep.readthedocs.io/en/latest/)

# Roteiro para $\nabla^2 u = f$

 * $u^\delta(x) = \sum \hat{u}_k \phi_k(x) \approx u(x)$ - Aproximação e interpolação
 * $\nabla^2 u^\delta - f = \varepsilon(x)$ - Erro da equação diferencial
 * Resíduos ponderados $\int_\Omega w_i(x) \nabla^2 u^\delta \:dx = \int_\Omega f w_i(x)\:dx$
 * Formulação fraca: $ -\int_\Omega \nabla w_i(x) \cdot \nabla u^\delta \:dx + \int_{\partial\Omega} w_i\frac{\partial u}{\partial n} = \int_\Omega f w_i(x)\:dx$
 * Galerkin: $w_i(x) = \phi_i(x)$
 * Sistema linear: $\left[A\right]\cdot\left\{\hat{u}\right\} = \left\{f\right\}$,
 $$A_{i,k} = -\int_\Omega\nabla\phi_i\cdot\nabla\phi_k\:dx + \int_{\partial\Omega}\phi_i\frac{\partial u}{\partial n}\\
 f_i = \int_\Omega f\phi_i\:dx
 $$

# Problema 1D

 1. Interpolação e aproximação: polinomial, senos e cossenos
 2. Derivadas: simbólica, numérica, diferenciação automática
 3. Quadratura, Simbólica, Numérica
 4. Sistemas lineares: Métodos diretos, métodos iterativos, *Explorar a estrutura da matriz*
 5. Equações diferenciais ordinárias: problemas de valor inicial
 6. Sistemas de equações não lineares: Newton-Raphson e outros

# Introduzindo Julia

 * http://julialang.org
 * Lista de discussão https://discourse.julialang.org/
 * Canal do youtube https://www.youtube.com/user/JuliaLanguage
 * Documentação: https://docs.julialang.org

```julia
# Caluladora
1+1

```

```julia
function soma(a,b)
    return a+b
end
soma2(a,b) = a+2b

x = 1
y = 10
soma2(x,y)
```

# Porque Julia consegue ser rápido como C?

```julia
code_native(soma2, (Int,Int))
```

```julia
code_native(soma2, (Float64,Float64))
```

```julia
using ForwardDiff

function f1(x)

    res = one(x)
    for i = 1:5
        res = x + res*x
    end
    return res
end
df1 = x -> ForwardDiff.derivative(f1, x)
f1b(x) = x + x*(one(x) + x*(one(x) + x * (one(x)+ 2x)))
df1b(x) = one(x) + 2x + 3x^2 + 4x^3 + 10x^4

```

```julia
df1(0.5) - df1b(0.5)
```

```julia
using BenchmarkTools

@btime f1(0.5)
@btime f1b(0.6)
@btime df1(0.7)
@btime df1b(0.7)
```

# Sistema de equações diferenciais ordinárias:

Equações de Lorenz
$$
\begin{align}
\frac{dx}{dt} &= σ(y-x) \\
\frac{dy}{dt} &= x(ρ-z) - y \\
\frac{dz}{dt} &= xy - βz \\
\end{align}
$$

```julia
using DifferentialEquations
using Plots
gr()
```

```julia
function lorenz(du,u,p,t)
 du[1] = 10.0*(u[2]-u[1])
 du[2] = u[1]*(28.0-u[3]) - u[2]
 du[3] = u[1]*u[2] - (8/3)*u[3]
end
```

```julia
u0 = [1.0;0.0;0.0]
tspan = (0.0,100.0)
prob = ODEProblem(lorenz,u0,tspan)
sol = solve(prob);
```

```julia
gr()
plot(sol,vars=(1,2,3))
```

```julia
using Psychro
using Unitful
```

```julia
println(volume(MoistAir, 20.0u"°C", DewPoint, 60.0u"°F", 1.0u"atm", u"cm^3/lb"))
println(wetbulb(MoistAir, 20.0u"°C", DewPoint, 60.0u"°F", 1.0u"atm"))
println(relhum(MoistAir, 20.0u"°C", DewPoint, 60.0u"°F", 1.0u"atm"))

```

```julia
using ACME

circ = @circuit begin
    j_in = voltagesource(), [-] ⟷ gnd
    r1 = resistor(1e3), [1] ⟷ j_in[+]
    c1 = capacitor(47e-9), [1] ⟷ r1[2], [2] ⟷ gnd
    d1 = diode(is=1e-15), [+] ⟷ r1[2], [-] ⟷ gnd
    d2 = diode(is=1.8e-15), [+] ⟷ gnd, [-] ⟷ r1[2]
    j_out = voltageprobe(), [+] ⟷ r1[2], [-] ⟷ gnd
end
model = DiscreteModel(circ, 1/4410)
t = 1/44100 * (0:44099)'
y = run!(model, sin.(2π*100 .* t));
plot(t[1,:],y[1,:])
```

```julia
using PyCall
```

```julia
math = pyimport("math")
println(math.sin(1))

np = pyimport("numpy")
nprand = pyimport("numpy.random")
nprand.randn(3,4)

```

```julia
using LinearAlgebra, SpecialFunctions, Plots, ApproxFun
x = Fun(identity,0..10)
f = sin(x^2)
g = cos(x)
```

```julia
h = f + g^2
r = roots(h)
rp = roots(h')

using Plots
plot(h)
scatter!(r,h.(r))
scatter!(rp,h.(rp))
```


# Recording of class
Background: people join from Americas/East Asia

Solution: recording of class
- Will be put on secret youtube links - not searchable
- Delete mid June

Consent: via poll

# Session 5:
## Growing Causal Trees
### - *Causal Forests and Generalized Random Forests*

*Andreas Bjerre-Nielsen*

## Agenda

1. [Causality](#Causality)
1. [Potential outcomes](#Potential-outcomes)
2. [Experiments](#Experiments)
3. [Matching](#Matching)
    - [Covariate based matching](#Covariate-matching)
    - [Propensity score matching](#Propensity-score-matching)

4. [Heterogeneous treatment effects with causal trees](#Causal-trees)
    - [Causal Forest](#Causal-Forest)
    - [Generalized Random Forest](#Generalized-Random-Forest)
    - [Application of causal forest](#Application-of-causal-forest)

# Buckle up...

```python
import matplotlib.pyplot as plt
import networkx as nx
import numpy as np
import pandas as pd
import seaborn as sns
%matplotlib inline
```

# Causality

## Correlation does not imply causation

Spurious or causal?

Figure below is adapted from chapter 5 in Judea Pearl's book titled "Book of Why"

```python
import networkx as nx

plt.rcParams.update({'axes.titlesize': 21})

f_lung_cancer, ax = plt.subplots(1,2,figsize=(17,6.7))
ax[0].set_title('Richard Doll and Austin Bradford Hill')
ax[1].set_title('Ronald A. Fisher')

s,l,g= 'Smoking', 'Lung\ncancer', 'Unobserved factors'
for i in range(2):
    G = nx.DiGraph()
    G.graph['dpi'] = 120
    G.add_nodes_from([s,l,g])

    G1 = G.copy()
    G1.add_edges_from([(s,l)])
    nx.draw_networkx_edges(G1,arrowsize=30,ax=ax[i],edge_color='blue',
                           pos = {g: [1,1], s: [0.2,0], l: [1.8,0]})

    if i>0:
        G2 = G.copy()
        G2.add_edges_from([(g,l),(g,s)])
        nx.draw_networkx_edges(G2,arrowsize=30,ax=ax[i],edge_color='red',
                               pos = {g: [1,1], s: [0.2,0], l: [1.8,0]})

    nx.draw_networkx_nodes(G,node_color='white',node_size=1000,alpha=0,ax=ax[i],
                           pos = {g: [1,1], s: [0,0], l: [2,0]})
    nx.draw_networkx_labels(G,ax=ax[i],font_size=16,
                            pos = {g: [1,1.1], s: [-.05,0], l: [2,0]})
    ax[i].axis('off')
    ax[i].set_xlim([-.3,2.3])
    ax[i].set_ylim([-.2,1.4])

```

## What is causality?

Relationship between two or more variables such that whereby a change in one or more variable(s) ***affect(s)*** the distribution of one or more other variable(s).

We can draw these relationships (from The Book of Why, Judea Pearl), e.g. smoking example.
- Ronald Fisher argued that unobserved confounders could cause smoking and lung cancer

```python
f_lung_cancer
```

## Establishing causality

Currently there are two broad approaches for establishing causal relationships:

- Experiment and quasi-experiments
    - Corresponds to what is taught in *Mostly Harmless Econometrics*
- Structural equation models
    - Used for structural econometric choice models etc.
    - Also used estimating causal graphs, e.g. as by Judea Pearl

# Potential outcomes

## The aim

We are interested in the effect of some treatment, e.g.
- getting admitted to a certain educaton on wages, life-expectancy
- access to paternity leave on wages (husband and wife)

## The Rubin Causal Model

Denote the treatment variable as $T_i$ where $T_i=1$ corresponds to unit $i$ being treated, while $T_i=0$ is not treated. Define the potential outcomes:

$Y_i=\begin{cases}
Y_i(1), & T_i=1;\\
Y_i(0), & T_i=0.
\end{cases}$

The observed outcome $Y_i$ can be written in terms of potential outcomes as
$$ Y_i = Y_{i}(0) + [Y_{i}(1)-Y_{i}(0)]\cdot T_i$$

$Y_{i}(1)-Y_{i}(0)$ is the *causal* effect of $T_i$ on $Y_i$.

But we never observe the same individual $i$ in both states. This is the **fundamental problem of causal inference**.

## Selection Bias

We need some way of estimating the state we do not observe (the ***counterfactual***)

Usually, our sample contains individuals from both states - treated and untreated.

So why not do a naive comparison of averages by treatment status? i.e. $E[Y_i|T_i = 1] - E[Y_i|T_i = 0]$

## Selection Bias II
We can rewrite into:
\begin{align}
\nonumber E[Y_i|T_i = 1] - E[Y_i|T_i = 0] = &E[Y_i(1)|T_i = 1] - E[Y_i(0)|T_i = 1] + \\
 \nonumber  &E[Y_i(0)|T_i = 1] - E[Y_i(0)|T_i = 0]
\end{align}

The decomposition:

 - $E[Y_i(1)|T_i = 1] - E[Y_i(0)|T_i = 1] = E[Y_i(1) - Y_i(0)|T_i = 1]$: the average *causal* effect of $T_i$ on $Y$.

- $E[Y_i(0)|T_i = 1] - E[Y_i(0)|T_i = 0]$: difference in average $Y_i(0)$ between the two groups. Likely to be different from 0 when individuals are allowed to self-select into treatment. Often referred to as ***selection bias***.

# Experiments

## Random assignment solves the problem

Random assignment implies $T_i$ is independent of potential outcomes

- Selection bias term is zero: $E[Y_{i}(0)|T_i = 1] = E[Y_{i}(0)|T_i = 0]$

- Intuition: non-treated individuals can be used as counterfactuals for treated (*what would have happened to individual $i$ had he not received the treatment*?)

- Overcome the fundamental problem of causal inference

## Randomization

Holland and Rubin (1986)

> no causation without manipulation

As mentioned, we need to worry when individuals are allowed to self-select

- A lot of thought has to go into the *randomization phase*.

- Randomization into treatment groups has to be manipulated by someone.

## Randomized Controlled Trials

*Randomized controlled trials (RCT)*: randomization done by researcher

- Survey experiments
- Field experiments

Note: difficult to say one is strictly better than the other. Randomization can be impractical and/or unethical.

## An alternative to experiments

*Quasi-experiments*: randomization happens by "accident"

- Matching (*today*)
- Differences in Differences
- Regression Discontinuity Design
- Instrument variables

# Matching

## The what and why of matching

**What** - we construct counterfactual potential treated and control units.
- We *match* observations across treatment and control based on similarity.

**Why** -  matching control for used covariates
- excludes (observable) confounders
- may improve precision of treatment estimate of experiments (less variance)

Note: An alternative to matching is to using regression - basically same idea.

Problem:
- matching does not unconfound generally!!
- unobserved factors may still confound

## The how of matching

We use a set of covariates $X$ for matching.

Two core ideas:
- We match on covariates
    - We require sufficient similarity by some metric over covarities
- We match on propensity
    - We require sufficient similar probability of treatment (prediction)

# Covariate based matching

## Exact matching

 We match a treatment $i$ obs. with control obs. $j$ if
 - $X_i=X_j$, i.e. they are exactly identical,
 - $||X_i-X_j||_2=0$, i.e. zero Euclidian distance

## Treatment effects

We can compute the Average Treatment Effect (ATE)

- For treatment obs. $i$ the counterfactual outcomes $Y_i(0)$ are the average of control $j$ where $X_j=X_i$.
- For control obs. $i$ the counterfactual outcomes $Y_i(1)$ are the average of treatment $j$ where $X_j=X_i$.

We can also compute treatment effects only for treament observations, known as Average Treatment Effect on the Treated (**ATT** or **ATET**).

## Balance of match

What happens if some observations are not matched?
- We get biased estimates!
- We not to check whether the match is balanced
    - Problem, exact matching usually leads to very few matches.

## Example of exact matching

Aim: understand whether traning program affects wages.

We have covariates and outcomes treatment and controls. (synthetic data from Scott Cunninghams's "Causal Inference - The Mixtape" book)

```python
scuse = 'https://storage.googleapis.com/causal-inference-mixtape.appspot.com/{0}.dta'
df = pd.read_stata(scuse.format('training_example')).replace('',np.nan)
arr = df.values[:20].astype('float')
X_cntrl, y_cntrl = arr[:20,4:5], arr[:20,5]
X_treat, y_treat = arr[:10,1:2], arr[:10,2]
```

```python
f,ax = plt.subplots()
sns.distplot(X_treat, bins=10, label='Treatment', ax=ax)
sns.distplot(X_cntrl, bins=10, label='Control', ax=ax)
ax.legend()
ax.set_xlim(15,55)
sns.despine(f)
f.savefig('fig/balance_labor.png',dpi=300)
```

## Example of exact matching (2)

We have only one dimension of covariate so we can easily check the balance.
- Problem no counterfactuals for control!!

## Example of exact matching (3)

We can match exactly using `RadiusNeighborsRegressor` with zero radius.
- OBS: in econometrics this radius is often known as a caliper

```python
from sklearn.neighbors import RadiusNeighborsRegressor as RNR

impute_t_exact = RNR(radius=0).fit(X_cntrl, y_cntrl).predict(X_treat)
impute_c_exact = RNR(radius=0).fit(X_treat, y_treat).predict(X_cntrl)
impute_c_exact
```

    One or more samples have no neighbors within specified radius; predicting NaN.

    array([10000., 11750., 10250.,    nan,    nan, 12250.,    nan, 13250.,
           11000., 12500., 13250.,    nan, 10500.,  9500.,    nan,    nan,
            9750., 12500.,    nan,    nan])

## Exact matching (4)

We can compute unbiased estimate of ATT:

```python
print(y_treat.mean(), y_cntrl.mean() )
diff = y_treat - impute_t_exact
print(f'ATT: {round(diff.mean(),1)} ± {round(diff.std()*1.96,1)}')
```

    11075.0 11101.25
    ATT: 1695.0 ± 646.7

## Other covariate based matching

We can extend exact matching in several ways

- Coarsened Exact Matching:
    - where continuous variables are split into blocks
    - very popular for experiments
- Radius / Caliper matching
- Nearest neighbor matching

We can also have different metrics:
- Euclidian
- Mahalanobis distance ($=(X-\bar{X})^TCOVAR(X)(X-\bar{X}) $)

Note that approximate matching on covariates may introduce other biases, see [Abadie and Imbenes (2011)](https://doi.org/10.1198/jbes.2009.07333).

# Propensity score matching

## Predicting treatment status

Alternative way of match on likelihood of treatment.

Procedure:
1. estimate a model that predicts treatment
2. match with observations of similar treatment likelihood
    - (use match function, e.g. nearest neighbor, caliper)
3. compute counterfactual outcomes for treatment and control
4. (possibly adjust for differences in observed covariates)
5. compute ATE

## Uncoundedness property

[Rosenbaum and Rubin (1983)](https://doi.org/10.1093/biomet/70.1.41) show that propensity score matching will unconfounded:
- can serve as an unbiased estimator of the average treatment effect
- endows non-experimental data with experimantal qualities

Critical requirement - conditional independence assumption (**CIA**):
- same as no unobserved confounders
- often CIA is violated
  - e.g. causal effect of taking education with registry data - many unobserved factors

## Summary - matching

Useful tool, but requires that we know all relevant factors
- can be useful to minimize variance of experimental estimates
- problem in observational studies - often there are unobserved confounders and selection

If we think there is selection effects or endogeneity:
- Use quasi-experimental methods which can handle this, e.g. diff-in-diff or regression discontinuity

# Causal trees

## Average Joe
Suppose, we have credible measures of average treatment effect, $\tau$.

Can we get personalized estimates?

- Measure whether certain groups are affected differently by our new school policy
    - e.g. boys vs. girls, natives vs. immigrants
- Some react positively to one kind of information, others to another

## Beyond average Joe

Conditional Average Treatment Effects (CATE)

- Treatment effect for given characteristics $x$
    - $\tau(x) = \mathbb{E}[Y_i(1)-Y_i(0)|X=x]$

Methods exist, e.g. use regression analysis.

But.. **True model is unknown**..!

- May need to test model on data.
- Can lead to conclusions based on data mining (dangerous!!)

## Being dishonest with you

An adaptive, data driven approach

- use all data for training decision tree
    - partitions X into categories based outcome similarity
    - enough treatment and control in each leaf
- then estimating treatment effects in partitions
    - measure treatment effects in each partition group (=leaf in tree model)

**Quiz**: is this different from propensity scores?

- Propensity scores has treatment assignment $T_i$ as target.

- The adaptive approach uses outcome $y_i$ as target.

## Getting honest with you

Could we use out-of-sample intuition?

[Athey and Imbens (2016)](https://doi.org/10.1073/pnas.1510489113) suggest to let data speak **honestly**:
- half of sample ($\mathcal{S}^{tr}$) for training decision tree
    - partitions X into categories based outcome similarity
    - enough treatment and control in each leaf
- other half ($\mathcal{S}^{est}$) for estimating treatment effects
    - measure treatment effects in each partition group (=leaf in tree model)

This is similar to splitting into train and test
- prevents data-leakage
- allows honest evaluation of model performance!

## Core assumption

Potential outcomes and treatment assignment are unconfounded given covariates

\begin{equation*}
T_i \,\perp\!\!\perp\, (Y_i(1), \,Y_i(0))\,\, | \,\, X
\end{equation*}

- where $\perp\!\!\perp$ is a symbol  for conditional independence (strong assumption!!)
- recall from earlier
    - always holds for experiments
    - or propensity scores (note: assumption cannot be tested)

## Modified splitting procedure

The usual way of training decision trees is Classification And Regression Trees (CART).

- Splits leaves repeatedly based on criteria (e.g. entropy, MSE)
- We can put in restriction, e.g. depth of trees (hyperparameters)

Causal trees
- new criteria:
    - expected MSE (in hypothetical test set): $\mathbb{E}[\underset{=MSE}{\underbrace{(Y_i-\bar{Y}_i)^2}} - Y_i^2]$
    - idea: new term $Y_i^2$ penalizes small leaves
- note: same ranking as MSE, matters for properties

## Modified splitting procedure

The usual way of training decision trees is Classification And Regression Trees (CART).

- Splits leaves repeatedly based on criteria (e.g. entropy, MSE)
- We can put in restriction, e.g. depth of trees (hyperparameters)

Causal trees
- criteria: $\mathbb{E}[(Y_i-\bar{Y_i})^2 - Y_i^2]$
- note: same ranking as MSE, matters for properties

## Inference

Partioning of the covariate data works like coarsened matching!

- Estimate average treatment effects locally for each group/leaf
- Corresponds to local matching!

## Inference - validation

[Athey and Imbens (2016)](https://doi.org/10.1073/pnas.1510489113) performs a simulation study under various scenarios.

Main take-away: **honest** outperforms **adapative** (convential CART).

## Summary - causal trees

Leverage machine learning idea:
- Heterogeneity is estimated separate from treatment effects.
- New scoring function makes smaller leafs.
- Outperforms adapative procedure

Main advantage
- Structure of heterogeneity from data.
- Can be part of pre-analysis plan - only one solution (given split of data!).

# Recap on Random Forest

## The  forest full of trees

What is the difference between a Decision Tree and a Random Forest?

- Decision tree iteratively splits data into subsets (partitions) and calculates mean outcome in leaves (end of splits)
- Minimize on some criteria, often entropy or similar loss function
- Collection/ensemble of decision trees
  - Subset of data by bootstrap (sampling with replacement)
  - Subset of features

## A special tree

So what distinguishes a Causal Tree from a Decision Tree?

- Causal tree estimates partition of data where treatment effects can be computed locally
- In order to have valid estimates we need **honesty** of trees by estimating partitions and treatment effects on different subsets of data
  - Analogy to train / test split

## A tradeoff in structure of heterogeneity

Two approaches?

- Data driven heterogeneity
  - Based on causal trees etc.
- A priori sensible heterogeneity
  - e.g. gender, socioeconomic, ethnicity
  - we use regression model and have interaction with desired variable

When to choose which?
- Choose data driven heterogeneity for policy where you want to maximize impact given data (no theory)
- If we want to test whether certain subgroups are adversely affected

## Limitations of Decision Trees

Random forests are nice but no asymptotic normality of prediction.

- Crucial for inference! (corresponds to MLR6 in Econometrics 1)

- Also holds for causal trees

# Random forest for inference and treatment effecs

## Causal  Trees

The goal of causal trees is to establish unbiased, consistent estimates of heterogeneous treatment effects
- also known as conditional average treatment effects (**CATE**)
- the effect size is denoted $\hat{\tau}(x)$;
- standard tools for inference, e.g. using statistical tests locally

## Causal Forest

What is the output from the decisions trees? Each tree produces a partitioning of the feature space $X$. Example of three trees:

(from [Athey, Wager, Tibshirani, 2019](https://doi.org/10.1214/18-aos1709))

## Double Sample Trees

For Causal Trees

- first half ($\mathcal{J}$, $|\mathcal{J}|=\lceil s / 2\rceil$)
  - training Decision Tree
    - minimize adjusted MSE
    - require at least $k$ observations for both treatment and control in all leaves of $\mathcal{I}$-sample
- other half  ($\mathcal{I}$, $|\mathcal{I}|=\lfloor s / 2\rfloor$)
  - estimating treatment effects, $\hat{\tau}(x)$

## Double Sample Trees (2)

For Regression Trees

- first half ($\mathcal{J}$, $|\mathcal{J}|=\lceil s / 2\rceil$)
  - training Decision Tree
    - minimize MSE / Gini etc.
    - require at least $k$ observations in all leaves of $\mathcal{I}$-sample
- other half  ($\mathcal{I}$, $|\mathcal{I}|=\lfloor s / 2\rfloor$)
  - estimating outcome, $\hat{\mu}(x)$

**Quiz:** How is this different from normal Decision Trees for regression problems?

- Unlike normal decision trees outcomes are estimated honestly.

## Main results: econometric properties (1)

[Wager and Athey (2017)](https://doi.org/10.1080/01621459.2017.1319839) show
 - We can estimate the variance of CATE
 - $\hat{V}_{IJ}(x)=\frac{n-1}{n}\left(\frac{n}{n-s}\right)^{2} \sum_{i=1}^{n} \operatorname{Cov}_{*}\left[\hat{\tau}_{b}^{*}(x), N_{i b}^{*}\right]^{2}$

## Main results: econometric properties (2)

From Theorem 4.1 in [Wager and Athey (2017)](https://doi.org/10.1080/01621459.2017.1319839)

- The conditional average treatment estimates are unbiased and consistent
  - unbiased: no systematic error of measurement
  - consistency: with more data our estimate approaches true value

- Moreover, we can do inference:
  - The variance estimator $\hat{V}_{IJ}(x)$ is consistent.
  - Treatment effect estimates are asymptotic normal and unbiased
    - $(\hat{\tau}(x)-\tau(x)) / \sqrt{\operatorname{Var}[\hat{\tau}(x)]} \Rightarrow \mathcal{N}(0,1)$

Caveat: only works for evaluating treatment effects in one point $x$! Do not perform multiple tests.

## Useful forests

Two more procedures

1. Double Sampled Trees
  - using Regression trees for predicting outcome (=$\hat{\mu}(x)$)
1. Propensity Trees
  -  using propensity trees for propensity score matching

What is the shared procedure?
- Each tree is estimated using repeated subsampling (**no replacement**)
  - Constrast to bootstrap aggregation in random forest (sample **with replacment**)
- Random subsample of features

## More results

[Wager and Athey (2017)](https://doi.org/10.1080/01621459.2017.1319839) show that the same properties of Double Sample Trees using causal trees also hold analogously for regression trees.
- Random forests  have the property of being asymptotic normal and can thus be used for inference
- Similar intuition as idea of nested CV where we could do inference

## Simulation experiment

[Wager,  and Athey (2017)](https://doi.org/10.1214/18-aos1709) compare causal forest to nearest neighbor methods

- random forest is kind of local nearest neighbor estimate
- based on work by Lin and Jeon (2006).

## Simulation (1)

- simulation setup: no treatment effect, only confounding factors
- method: propensity trees
- comparison of estimated treatment effects
    - lower MSE and better coverage
    - coverage falls for increasing number of variables $d$

(from [Athey, Wager, Tibshirani, 2019](https://doi.org/10.1214/18-aos1709))

## Simulation (2)

- setup: heterogeneous treatment effect, **no** confounding factors

- comparison of estimated treatment effects
    - lower MSE and better coverage
    - coverage falls for increasing number of variables $d$

(from [Athey, Wager, Tibshirani, 2019](https://doi.org/10.1214/18-aos1709))

## Alternatives to compute heterogeneous treatment effects

There are some existing and new frameworks for estimating heterogeneous treatment effects. For instance BART is already quite established and often outperforms GRF.

- [Chipman, George and McCulloch (2010)](https://doi.org/10.1214/09-aoas285) develops Bayesian Additive Regression Trees (BART)

- [Künzel et al. (2019)](https://doi.org/10.1073/pnas.1804597116) investigates more general class of prediction tools for partitioning data using

  - Lower EMSE in many cases relative to CF/GRF and BART

- [Nie and Wager (2017)](https://arxiv.org/pdf/1712.04912.pdf) investigates another class of methods called R-learners that leverages a smart representation of CATE.

## Round-up causal forest

Summary of [Wager and Athey (2017)](https://doi.org/10.1080/01621459.2017.1319839)
- builds on Causal Trees method
- strong econometric properties
  - unbiased and consistent
  - asymptotic normality given $x$
      - causal and regression forest allows inference!
- problem:
  - must choose focus
    - unconfounding (propensity) or
    - estimate CATE
  - coverage was not good, especially for higher $d$!

# Generalized Random Forest

## A higher aim

Causal forests are pretty cool. Can we use our honest procedure more generally?

- Estimate any quantity $\theta(x)$ identified via local moment conditions, e.g.
  - simultaneously unconfound and find heterogeneity?
  - find heterogeneous treatment effects from IV estimation?

## Estimating equations
The general estimating equation
  - $\mathbb{E}\left[\psi_{\theta(x), \nu(x)}\left(O_{i}\right) | X_{i}=x\right]=0, \quad \forall x.$

Where $\psi$  estimating function, maps parameters and data into moment equations
  - Parameters
    - $\theta$ parameter we want estimate
    - $\nu$ is nuisance we want to "partial out" (optional)
  - Data
    - $O_i$ main objects we are interested in modelling, e.g. $Y_i, T_i$
    - $X_i$ covariates

## Estimating equations

What is a moment condition?

- Similiar to solution to first order condition
- More general - can incorporate extra restrictions (e.g. unconfounding)

## Estimating equations

Suppose we want to estimate conditional average treatment effects

Functional form: $\psi_{\beta(x),c(x)}=Y_i-\beta(x)W_i-c(x) \left(1 \quad W_{i}\right)$ where
  - $\beta$ is treatment effect
  - $c$ is nuisance parameter

## Using a kernel

Kernel methods can be used to unconfound and compute heterogeneous effects simulateneously

- Problem how to decide weights?

(from [Athey, Wager, Tibshirani, 2019](https://doi.org/10.1214/18-aos1709))

## The Generalized Random Forest

[Athey, Wager, Tibshirani (2019)](https://doi.org/10.1214/18-aos1709) show that kernel weights can be estimated using forest methods

- can be adapted for different purposes
  - quantile regression
  - heterogeneous treatment effects
  - instrumental variables

## The Generalized Random Forest (2)

[Athey, Wager, Tibshirani (2019)](https://doi.org/10.1214/18-aos1709) use a procedure as follows:

1. Use estimating equation, $\psi$ to estimate tree splits iteratively on subsample.
2. View forests as a weights of similar neighbors
    - Amount of partitions where observations
  \begin{equation}\alpha_i(x)=\frac{1}{B}\sum_{b=1}\frac{\mathbb{1}(X_i\in L_b(x))}{|L_b(x)|}\end{equation}
3. Re-estimate $\psi$ using weights on entire sample.

Difference from Causal Forest - trees are used for constructing weights!

## Computing weights $\alpha$

Given a subsample $\mathcal{I}$ of data.

1. Split subsample $\mathcal{I}$ into $\mathcal{J}_1,\mathcal{J}_2$
1. Estimate trees on $\mathcal{J}_1$
  1. Compute estimating equations on subsample at each parent node (before split)
  1. Evaluate different splits - use approximate solutions with gradients
1. Estimate weights using forests on $\mathcal{J}_2$.
  \begin{equation}\alpha_i(x)=\frac{1}{B}\sum_{b=1}\frac{\mathbb{1}(X_i\in L_b(x))}{|L_b(x)|}\end{equation}
    - where $L_b(x)$ training examples falling in the same leaf as x

## Overall procedure

1. Repeatedly estimate trees where splits are based on estimating equation, $\psi$ to obtain weights $\alpha$.
1. Re-estimate $\psi$ using weights on entire sample where forests splits are weights.

## Estimating equations

Given $x$ compute the ***local*** estimating equations using weights $\alpha_i(x)$ on entire sample:

\begin{equation}
(\hat{\theta}(x), \hat{\nu}(x)) \in \operatorname{argmin}_{\theta, \nu}\left\{\left\|\sum_{i=1}^{n} \alpha_{i}(x) \psi_{\theta, \nu}\left(O_{i}\right)\right\|_{2}\right\}
\end{equation}

Example of applications in Athey et al. (2019)
- Conditional Average Treatment Effects
- Instrumental Variables
- Quantile Regressions

## Estimation equations for CATE

What does the local estimating equation look like under CATE?

- the estimating equations $\psi$ with possibly multi-dimensional treatments

\begin{align}\psi_{\beta(x), c(x)}\left(Y_{i}, W_{i}\right)=\left(Y_{i}-\beta(x) \cdot W_{i}-c(x)\right)\left(1 \quad W_{i}\right)\end{align}

- Note: $\left(1 \quad W_{i}\right)$ which implies there is a vector of equations

## Estimation equations for CATE (2)

What is the solution?
- Run local regression of $y_i$ on $W_i$ with weights $\alpha$

\begin{align}\hat{\theta}(x)=\xi^{\top}\left(\sum_{i=1}^{n} \alpha_{i}(x)\left(W_{i}-\bar{W}_{\alpha}\right)^{\otimes 2}\right)^{-1} \sum_{i=1}^{n} \alpha_{i}(x)\left(W_{i}-\bar{W}_{\alpha}\right)\left(Y_{i}-\bar{Y}_{\alpha}\right)\end{align}

## Main results

Athey et al (2019) show that Generalized Random Forests have the following propeties:

- Estimates, $\hat{\theta}(x)$, are consistent (Theorem 3)
- Asymptotic normality of estimates (Theorems 5,6)

# Application of causal forest

## Note about software

Use `econml` implementation

- Not implementation of generalized random forest
- Uses double machine learning to unconfound data
- Caution:
  - If you want to use in research - use GRF package by Athey et al.

## Make synthetic data

```python
import numpy as np

n, p = 1000, 10
X = np.random.RandomState(0).randn(n,p)
P = 0.4 + 0.2 * (X[:, 0] > 0)
T = (np.random.rand(n) > P).astype('float')
Y = np.max([X[:, 0] * T, np.zeros(n)],0) + \
    X[:, 1] + np.max([X[:, 2] * T, np.zeros(n)],0)
```

## Apply  causal forest from `econml`

Note - we use double maching learning here to unconfound treatment assignment and control for confounders of outcome

```python
t0,t1 = 0,1
from econml.dml import CausalForestDML
from sklearn.ensemble import GradientBoostingRegressor
est = CausalForestDML(model_y=GradientBoostingRegressor(),
                      model_t=GradientBoostingRegressor())
est.fit(Y, T, X=X, W=X)
```

    <econml.dml.causal_forest.CausalForestDML at 0x7fef1c314a30>

## Getting predicted treatment effects for x range

```python
n_plot = 1000
X_range = np.zeros([n_plot,p])
X_range[:,0] = np.linspace(-2,2,n_plot)
Y_dgp = np.max([X_range[:, 0] * np.ones(n_plot), np.zeros(n_plot)],0) + X_range[:, 1] + np.max([X_range[:, 2] * np.ones(n_plot), np.zeros(n_plot)],0)

tau_hat = est.effect(X_range, T0=t0, T1=t1)
tau_lb, tau_ub = est.effect_interval(X_range, T0=t0, T1=t1, alpha=0.05)
```

## Plotting treatment effects

```python
from matplotlib import pyplot as plt
%matplotlib inline
f,ax = plt.subplots(figsize=(8,6))
ax.plot(X_range[:,0], Y_dgp, color='red', label='Data Generating Process')
# plt.plot(X_range[:,0], tau_hat)
ax.fill_between(X_range[:,0], tau_lb, tau_ub, label="Causal Forest 95% CI")
ax.legend()
```

# Comparison
### Causal Forests and Generalized Random Forests

## Causal Trees and Forests
Strong econometric properties
- unbiased and consistent (trees and forests)
- asymptotic normality given $x$ (forests)

- weaknesses:
  - either unconfounding or heterogeneity
  - we "use" data to buy honesty at the price of statistical power

## Generalized Random Forest
Difference from Causal Forest - trees are used for constructing weights!
- strengths:
  - unconfounding (propensity) *AND* heterogeneity
  - additional uses
      - quantile regression
      - instrumental variables
      - clustered standard errors
      - and more
- weakness:
  - we "waste" data on honesty