# Conditional Probability and Conditional Expectation

## Intro

- given some partial information
- or just first "condition" on some appropriate $r.v.
\DeclareMathOperator*{\argmin}{argmin}
\newcommand{\ffrac}{\displaystyle \frac}
\newcommand{\Tran}[1]{{#1}^{\mathrm{T}}}
\newcommand{\d}[1]{\displaystyle{#1}}
\newcommand{\EE}[2][\,\!]{\mathbb{E}_{#1}\left[#2\right]}
\newcommand{\dd}{\mathrm{d}}
\newcommand{\Var}[2][\,\!]{\mathrm{Var}_{#1}\left[#2\right]}
\newcommand{\Cov}[2][\,\!]{\mathrm{Cov}_{#1}\left(#2\right)}
\newcommand{\Corr}[2][\,\!]{\mathrm{Corr}_{#1}\left(#2\right)}
\newcommand{\using}[1]{\stackrel{\mathrm{#1}}{=}}
\newcommand{\I}[1]{\mathrm{I}\left( #1 \right)}
\newcommand{\N}[1]{\mathrm{N} \left( #1 \right)}
\newcommand{\space}{\text{ }}
\newcommand{\bspace}{\;\;\;\;}
\newcommand{\QQQ}{\boxed{?\:}}
\newcommand{\CB}[1]{\left\{ #1 \right\}}
\newcommand{\SB}[1]{\left[ #1 \right]}
\newcommand{\P}[1]{\left( #1 \right)}
\newcommand{\ow}{\text{otherwise}}$

## The Discrete Case

$\forall$ events $E$ and $F$, the **conditional probability** of $E$ *given* $F$ is defined, as long as $P(F) > 0$, by

$$P(E\mid F) = \ffrac{P(EF)} {P(F)}$$

Hence, if $X$ and $Y$ are discrete $r.v.$, then it's natural to define the **conditional probability mass function** of $X$ *given* that $Y=y$ and $P\CB{Y=y}>0$, by:

$$\begin{align}
p_{X\mid Y}(X\mid Y) &= P\CB{X=x\mid Y=y} \\[0.5em]
&= \ffrac{P\CB{X=x\mid Y=y}} {P\CB{Y=y}} \\
&= \ffrac{p(x,y)} {p_{Y}(y)}
\end{align}$$

and the conditional pdf of $X$ *given* that $Y=y$ and $P\CB{Y=y}>0$ is

$$\begin{align}
F_{X\mid Y}(X\mid Y) &= P\CB{X \leq x\mid Y=y} \\[0.5em]
&= \sum_{a \leq x} p_{X\mid Y}(a\mid Y)
\end{align}$$

and finally, the conditional expectation of $X$ *given* that $Y=y$ and $P\CB{Y=y}>0$ is defined by

$$\begin{align}
\EE{X\mid Y=y} &= \sum_{x} x \cdot P\CB{X = x\mid Y=y} \\[0.5em]
&= \sum_{x} x \cdot p_{X\mid Y}(x\mid Y)
\end{align}$$

$Remark$

If $X$ is independent of $Y$, then all the aforementioned definitions are identical to what we have learned before.

**e.g.**

If $X_1$ and $X_2$ are independent **binomial** $r.v.$ with respective parameters $(n_1,p)$ and $(n_2,p)$. Find the conditional pmf of $X_1$ given that $X_1 + X_2 = m$.

>$\begin{align}
P\CB{X_1 = k \mid X_1 + X_2 = m} &= \ffrac{P\CB{X_1 = k , X_1 + X_2 = m}} {P\CB{X_1 + X_2 = m}} \\[0.6em]
&= \ffrac{P\CB{X_1 = k}\cdot P\CB{ X_2 = m-k}} {P\CB{X_1 + X_2 = m}}\\
&= \ffrac{\d{\binom{n_1} {k} p^k q^{\d{n_1 - k}} \cdot \binom{n_2} {m-k} p^{m-k} q^{\d{n_2 - m + k}}}} {\d{\binom{n_1 + n_2}{m} p^m q^{\d{n_1 + n_2 -m}}}}
\end{align}$
>
>Here $q = 1-p$ and $X_1 + X_2$ is a **binomial** $r.v.$ as well with parameters $(n_1 + n_2 , p)$. Simplify that, we obtain:
>
> $$P\CB{X_1 = k \mid X_1 + X_2 = m} = \ffrac{\d{\binom{n_1} {k}\binom{n_2} {m-k}}} {\d{\binom{n_1 + n_2}{m}}}$$
>
>$Remark$
>
>This is a hypergeometric distribution.

***

**e.g.**

If $X$ and $Y$ are independent **poisson** $r.v.$ with respective parameters $\lambda_1$ and $\lambda_2$. Find the conditional pmf of $X$ given that $X+Y = n$.

>We follow the same fashion and can easily get that
>
>$\begin{align}
P\CB{X = k \mid X + Y = n} &= \ffrac{e^{-\lambda_1} \; \lambda_1^k} {k!}\cdot\ffrac{e^{-\lambda_2}\; \lambda_2^{n-k}} {(n-k)!} \left[\ffrac{e^{-\lambda_1 - \lambda_2} \left(\lambda_1 + \lambda_2\right)^{n}} {n!}\right]^{-1} \\
&= \ffrac{n!} {k!(n-k)!}\left(\ffrac{\lambda_1} {\lambda_1 + \lambda_2}\right)^k\left(\ffrac{\lambda_2} {\lambda_1 + \lambda_2}\right)^{n-k}
\end{align}$
>
>Given this, we can say that the conditional distribution of $X$ given that $X+Y=n$ is the binomial distribution with parameters $n$ and $\lambda_1/\left(\lambda_1 + \lambda_2\right)$. Hence, we also have
>
>$$\EE{X \mid X+Y = n} = n\ffrac{\lambda_1}{\lambda_1 + \lambda_2}$$

***

## Continuous Case

If $X$ and $Y$ have a joint probability density function $f\P{x,y}$, the the ***conditional pdf*** of $X$, given that $Y=y$, is defined for all values of $y$ such that $f_Y\P{y} < 0$, by

$$f_{X \mid Y} \P{x \mid y} = \ffrac{f\P{x,y}} {f_Y\P{y}}$$

Then the expectation: $\EE{X \mid Y = y} = \d{\int_{-\infty}^{\infty}} x \cdot f_{X \mid Y} \P{x \mid y} \;\dd{x}$

**e.g.**

Joint density of $X$ and $Y$: $f\P{x,y} = \begin{cases}
6xy(2-x-y), & 0 < x < 1, 0 < y < 1\\
0, & \text{otherwise}
\end{cases}$

Compute the conditional expectation of $X$ given that $Y=y$, where $0 <y <1$.

>We first need to compute the conditional density:
>
>$f_{X \mid Y} \P{x\mid y} = \ffrac{f\P{x,y}} {f_Y\P{y}} = \ffrac{6xy(2-x-y)} {\d{\int_{0}^{1} 6xy(2-x-y) \;\dd{x}}} = \ffrac{6x(2-x-y)} {4-3y}$
>
>Hence we can find the expectation
>
>$\EE{X \mid Y=y} = \d{\int_{0}^{1}} x \cdot \ffrac{6x(2-x-y)} {4-3y} \;\dd{x} = \ffrac{5-4y} {8-6y}$

***

**e.g.** The ***t-Distribution***

If $Z$ and $Y$ are independent, with $Z$ having a **standard normal** distribution and $Y$ having a **chi-squared** distribution with $n$ degrees of freedom, then the random variable $T$ defined by

$$T = \ffrac{Z} {\sqrt{Y/n}} = \sqrt{n} \ffrac{Z} {\sqrt{Y}}$$

is said to be a $\texttt{t-}r.v.$ with $n$ degrees of freedom. We now compute its density function:

>Here the strategy is to use one conditional density function and multiply that with the one just on condition, then integrate them.
>
>$$f_T(t) = \int_{0}^{\infty} f_{T,Y}\P{t,y} \;\dd{y} = \int_{0}^{\infty} f_{T\mid Y}\P{t \mid y} \cdot f_{Y}(y) \;\dd{y}$$
>
>Since we have already get the pdf for **chi-squared** $r.v.$,so $f_Y(y) = \ffrac{e^{-y/2} y^{n/2-1}} {2^{n/2} \Gamma\P{n/2}}$, for $y > 0$. Then $T$ conditioned on $Y$, which is a **normal distribution** with mean $0$ and variance $\P{\sqrt{\ffrac{n} {y}}}^2 = \ffrac{n} {y}$, so
>
>$f_{T \mid Y} \P{t \mid y} = \ffrac{1} {\sqrt{2\pi n /y}} \exp\CB{-\ffrac{t^2 y} {2n}} = \ffrac{y^{1/2}} {\sqrt{2 \pi n}} \exp\CB{-\ffrac{t^2 y} {2n}}$ for $-\infty < t < \infty $. Then:
>
>$$\begin{align}
f_{T}(t) &= \int_{0}^{\infty} \ffrac{y^{1/2}} {\sqrt{2 \pi n}} \exp\CB{-\ffrac{t^2 y} {2n}} \cdot \ffrac{e^{-y/2} y^{n/2-1}} {2^{n/2} \Gamma\P{n/2}} \;\dd{y} \\[0.6em]
&= \ffrac{1} {\sqrt{\pi n} \; 2^{\P{n+1}/2} \; \Gamma\P{n/2}} \int_{0}^{\infty} \exp\CB{-\ffrac{1} {2} \P{1 + \ffrac{t^2} {n}}y} \cdot y^{\P{n-1}/2} \; \dd{y} \\[0.8em]
& \;\;\;\;\text{then we let } \ffrac{1} {2} \P{1 + \ffrac{t^2} {n}}y = x \\[0.8em]
&= \ffrac{\P{1 + \frac{t^2} {n}}^{-\P{n+1}/2} \P{1/2}^{-\P{n+1}/2}} {\sqrt{\pi n} \; 2^{\P{n+1}/2} \; \Gamma\P{n/2}} \int_{0}^{\infty} e^{-x} x^{\P{n-1}/2} \;\dd{x} \\[0.6em]
&= \ffrac{\P{1 + \frac{t^2} {n}}^{-\P{n+1}/2}} {\sqrt{\pi n} \; \Gamma\P{n/2}} \Gamma\P{\ffrac{n-1} {2} + 1} = \ffrac{\Gamma\P{\frac{n+1} {2}}} {\sqrt{\pi n} \; \Gamma\P{\frac{n} {2}}} \P{1 + \ffrac{t^2} {n}}^{-\P{n+1}/2}
\end{align}$$

$Remark$

It will be much easier if given the joint distribution of $T$ and $Y$. $\texttt{FXXX}$

***

**e.g.**

Joint density of $X$ and $Y$: $f\P{x,y} = \begin{cases}
\ffrac{1} {2} y e^{-xy}, & 0 < x < \infty, 0 < y < 2 \\[0.7em]
0, &\ow
\end{cases}$. So what is $\EE{e^{X/2} \mid Y=1}$?

> This time will be much easier, we first obtain the conditional density of $X$ given that $Y=1$:
>
>$$\begin{align}
f_{X\mid Y} \P{x \mid 1} &= \ffrac{f\P{x,1}} {f_Y\P{1}} \\
&= \ffrac{\ffrac{1} {2} e^{-x}} {\d{\int_{0}^{\infty}\frac{1} {2} e^{-x} \; \dd{x}}} = e^{-x}
\end{align}$$
>
>Hence, we have $\EE{e^{X/2} \mid Y=1} = \d{\int_{0}^{\infty}} e^{x/2} \cdot f_{X\mid Y} \P{x \mid 1} \; \dd{x} =  2$

***

**(V)e.g.**

Let $X_1$ and $X_2$ be independent **exponential** $r.v.$ with rates $\mu_1$ and $\mu_2$. Find the conditional density of $X_1$ given that $X_1 + X_2 = t$.

>We'll be using the formula: $f_{\d{X_1 \mid X_1 + X_2}}\P{x \mid t} = \ffrac{f_{\d{X_1, X_1 + X_2}}\P{x, t}} {f_{\d{ X_1 + X_2}}\P{t}}$. The denominator is a *constant at this time*, not given. And as for the numerator, using Jacobian determinant we can find that
>
>$$J = \begin{vmatrix}
\ffrac{\partial x} {\partial x} & \ffrac{\partial x} {\partial y}\\
\ffrac{\partial x+y} {\partial x} & \ffrac{\partial x+y} {\partial y}
\end{vmatrix} = 1$$
>
>So our conclusion is: $f_{\d{X_1, X_1 + X_2}}\P{x, t} = f_{\d{X_1, X_2}}\P{x_1,x_2} \cdot J^{-1} = f_{\d{X_1, X_2}}\P{x,t-x}$. Plug in, we have
>
>$$\begin{align}
f_{\d{X_1 \mid X_1 + X_2}}\P{x \mid t} &= \ffrac{f_{\d{X_1, X_2}}\P{x,t-x}} {f_{\d{X_1 + X_2}}\P{t}} \\
&= \ffrac{1} {f_{\d{X_1 + X_2}}\P{t}} \cdot \P{\:\mu_1 e^{\d{-\mu_1 x}}}\P{\mu_2 e^{\d{-\mu_2 \P{t-x}}}}, \;\;\;\; 0 \leq x \leq t \\[0.7em]
&= C \cdot \exp\CB{-\P{\mu_1 - \mu_2}t}, \;\;\;\; 0 \leq x \leq t
\end{align}$$
>
>Here $C = \ffrac{\mu_1 \mu_2 e^{\d{-\mu_2t}}} {f_{\d{X_1 + X_2}}\P{t}} $. The easier situation is when $\mu_1 = \mu_2 = \mu$, then $f_{\d{X_1 \mid X_1 + X_2}}\P{x \mid t} = C, 0 \leq x \leq t$, which is a uniform distribution, yielding that $C = 1/t$.
>
>And when they're not equal, we need to use:
>
>$$1 = \int_{0}^{t} f_{\d{X_1 \mid X_1 + X_2}}\P{x \mid t} \;\dd{x} = \ffrac{C} {\mu_1 - \mu_2} \P{1 - \exp\CB{-\P{\mu_1 - \mu_2}t}}\\[1.6em]
\Longrightarrow C = \ffrac{\mu_1 - \mu_2} {1 - \exp\CB{-\P{\mu_1 - \mu_2}t}}$$

>Now we can see the final answer and the byproduct:
>
>$f_{\d{X_1 \mid X_1 + X_2}}\P{x \mid t} = \begin{cases}
1/t, & \text{if }\mu_1 = \mu_2 = \mu\\[0.6em]
\ffrac{\P{\mu_1 - \mu_2}\exp\CB{-\P{\mu_1 - \mu_2}t}} {1 - \exp\CB{-\P{\mu_1 - \mu_2}t}}, &\text{if } \mu_1 \neq \mu_2
\end{cases}$
>
>$
f_{\d{X_1 + X_2}}\P{t} = \begin{cases}
\mu^2 t e^{-\mu t}, & \text{if }\mu_1 = \mu_2 = \mu\\[0.6em]
\ffrac{\mu_1\mu_2\P{\exp\CB{-\mu_2t} - \exp\CB{-\mu_1t}}} {\mu_1 - \mu_2}, &\text{if } \mu_1 \neq \mu_2
\end{cases}$

$Remark$

>When calculate $f_{\d{ X_1 + X_2}}\P{t}$, $t$ is no longer a given constant.

***

## Computing Expectation by Conditioning

Denote $\EE{X \mid Y}$ as the *function* of the $r.v.$ $Y$ whose value at $Y = y$ is $\EE{X \mid Y = y}$. An extremely important property of conditional expectation is that for all $r.v.$ $X$ and $Y$,

$$\EE{X} = \EE{\EE{X \mid Y}} = \begin{cases}
\d{\sum_y} \EE{X \mid Y = y} \cdot P\CB{Y = y}, & \text{if } Y \text{ discrete}\\[0.5em]
\d{\int_{-\infty}^{\infty}} \EE{X \mid Y = y} \cdot f_Y\P{y}\;\dd{y}, & \text{if } Y \text{ continuous}
\end{cases}$$

We can interpret this as that when we calculate $\EE{X}$ we may take a *weighted average* of the conditional expected value of $X$ given $Y = y$, each of the terms $\EE{X \mid Y = y}$ being weighted by the probability of the event on which it is conditioned.

**e.g.** The Expectation of the Sum of a Random Number of Random Variables

The expected number of unqualified cookie products per week at an industrial plant is $n$, and the number of broken cookies in each product are independent $r.v.$ with a common mean $m$. Also assume that the number of broken cookies in each product is independent of the number of unqualified products. Then, what's the expected number of broken cookies during a week?

>Letting $N$ denote the number of unqualified cookie products and $X_i$ the number of broken cookies inside the $i\texttt{-th}$ product. Then the total broken cookies is $\sum X_i$. Now to bring the sum operation out of the expecation (even linear operation, however $N$ is a $r.v.$), we need to condition that on $N$
>
>$\bspace \d{\EE{\sum\nolimits_{i=1}^{N} X_i} = \EE{\EE{\sum\nolimits_{i=1}^{N} X_i \mid N}}}$
>
>But by the independence of $X_i$ and $N$ , we can derive that:
>
>$\bspace \d{\EE{\sum_{i=1}^{N}X_i \mid N =n} = \EE{\sum_{i=1}^{n} X_i} = n\cdot\EE{X}}$,
>
>which yields the function for $N$: $\d{\EE{\sum\nolimits_{i=1}^{N}X_i\mid N} = N \cdot \EE{X}}$ and thus:
>
>$\bspace \d{\EE{\sum_{i=1}^{N}X_i} = \EE{N\cdot \EE{X}} = \EE{N} \cdot \EE{X} = mn}$

$Remark$

>***Compound Random Variable***, as those the sum of a random number (like the preceding $N$) of $i.i.d.$ $r.v.$ (like the preceding $X_i$) that are also independent of $N$.

**e.g.** The Mean of a Geometric Distribution

Before, we use derivatives or 错位相减 to obtain that $\EE{X} = \d{\sum_{n=1}^{\infty} n \cdot p(1-p)^{n-1}} = \ffrac{1} {p}$. Now a new method can be applied here:

>Let $N$ be the number of trials required and define $Y$ as
>
>$Y = \bspace \begin{cases}
1, &\text{if the first trial is a success} \\[0.6em]
0, &\text{if the first trial is a failure} \\
\end{cases}$
>
>Then $\EE{N} = \EE{\EE{N\mid Y}} = \EE{N\mid Y = 1}\cdot P\CB{Y = 1} + \EE{N \mid Y = 0}\cdot P\CB{Y=0}$. However, $\EE{N \mid Y = 1} = 1$ (no more trials needed) and $\EE{N \mid Y = 0} = 1 + \EE{N}$. Substitute these back we have the equation:
>
>$\bspace \EE{N} = 1\cdot p + \P{1 + \EE{N}}\cdot \P{1-p} \space \Longrightarrow \space \EE{N} = 1/p$

**e.g.** Multinomial Covariances

Consider $n$ independent trials, each of which results in one of the outcomes $1, 2, \dots, r$, with respective probabilities $p_1, p_2, \dots, p_r$, and $\sum p_i = 1$. If we let $N_i$ denote the number of trials that result in outcome $i$, then $\P{N_1, N_2, \dots, N_r}$ is said to have a ***multinomial distribution***. For $i \neq j$, let us compute $\Cov{N_i, N_j} = \EE{N_i N_j} - \EE{N_i} \EE{N_j}$

> Each trial independently results in outcome $i$ with probability $p_i$, and it follows that $N_i$ is binomial with parameters $\P{n, p_i}$, given that $\EE{N_i}\EE{N_j} = n^2 p_i p_j$. Then to compute $\EE{N_i N_j}$, condition on $N_i$ we can obtain:
>
>$\bspace \begin{align}
\EE{N_i N_j}&=\sum_{k=0}^{n} \EE{N_i N_j \mid N_i = k}\cdot P\CB{N_i = k} \\
&= \sum\nolimits_{k=0}^{n} k\EE{N_j \mid N_i = k} \cdot P\CB{N_i = k}
\end{align}$
>
>Now given that only $k$ of the $n$ trials result in outcome $i$, each of the other $n-k$ trials independently results in outcome $j$ with probability: $P\P{j \mid \text{not }i} = \frac{p_j} {1-p_i}$, thus showing that the conditional distribution of $N_j$, given that $N_i = k$, is binomial with parameters $\P{n-k,\ffrac{p_j} {1-p_i}}$ and its expectation is: $\EE{N_j \mid N_i = k} = \P{n-k} \ffrac{p_j} {1-p_i}$.
>
>Using this yields:
>$\bspace \begin{align}
\EE{N_i N_j} &= \sum_{k=0}^{n} k\P{n-k} \ffrac{p_j} {1-p_i} P\CB{N_i = k} \\
&= \ffrac{p_j} {1-p_i} \P{n \sum_{k=0}^{n} kP\CB{N_i = k} - \sum_{k=0}^{n} k^2 P \CB{N_i = k}} \\
&= \ffrac{p_j} {1-p_i}\P{n\EE{N_i} - \EE{N_i^2}}
\end{align}$

>And $N_i$ is a binomial $r.v.$ with parameters $\P{n,p_i}$, thus, $\EE{N_i^2} = \Var{N_i} + \EE{N_i}^2 = np_i\P{1-p_i} + \P{np_i}^2$. Hence, $\EE{N_iN_j} = \ffrac{p_j} {1-p_i} \SB{n^2 p_i - np_i\P{1-p_i} - n^2p_i^2} = n\P{n-1}p_ip_j$, which yields the result:
>
>$\bspace \Cov{N_i N_j} = n\P{n-1}p_ip_j - n^2p_ip_j = -np_ip_j$

**(V)e.g.** The Matching Rounds Problem from the example of last chapter, the one of randomly choosing hat. Now for those who have already chosen their own hats, leave the room. And then mix the wrongly-matched hats again and reselect. This process continues until each individual has his own hats.

$\P{1}$ Let $R_n$ be the number of rounds that are necessary when $n$ individuals are initially present. Find $\EE{R_n}$.

>Follow the results from last example. For each round, on average there'll be only *one* match, no matter how many candidates remain. So intuitively, $\EE{R_n} = n$. Here's an induction proof. Firstly, it's obvious $\EE{R_1} = $, then we assume that $\EE{R_k} = k$ for $k = 1,2,\dots,n-1$, then we find $\EE{R_n}$ by conditioning on $X_n$, which is the number of matches that occur in the first round:
>
>$\bspace \EE{R_n} = \d{\sum_{i=0}^{n} \EE{R_n \mid X_n = i}} \cdot P\CB{X_n = i}$. Now given that there're totally $i$ matches in the first round, then we have $\EE{R_n \mid X_n = i} = 1 + \EE{R_{n-i}}$.
>
>$\bspace \begin{align}
\EE{R_n} &= \sum_{i=0}^{n} \P{1 + \EE{R_{n-i}}} \cdot P\CB{X_n = i} \\
&= 1 + \EE{R_n} P\CB{X_n = 0} + \sum_{i=1}^{n} \EE{R_{n-i}} \cdot P\CB{X_n = i} \\
&= 1 + \EE{R_n} P\CB{X_n = 0} + \sum_{i=1}^{n} \P{n-i} \cdot P\CB{X_n = i}, \; \text{as the induction hypothesis}\\
&= 1 + \EE{R_n} P\CB{X_n = 0} + n\P{1-P\CB{X_n = 0}} - \EE{X_n}, \; \P{\EE{X_n} = 1 \text{ as the result before}}\\[0.6em]
&= \EE{R_n} P\CB{X_n = 0} + n\P{1-P\CB{X_n = 0}}\\[0.7em]
& \bspace \text{then we solve the equation,}\\[0.7em]
\Longrightarrow& \space\EE{R_n} = n
\end{align}$

$Remark$

>Assuming something happened in the first trial...

$\P{2}$ Let $S_n$ be the total number of selections made by the $n$ individuals for $n \geq 2$. Find $\EE{S_n}$

>Still we condition on $X_n$, which gives:
>
>$\bspace \begin{align}
\EE{S_n} &= \sum_{i=0}^{n} \EE{S_n \mid X_n = i} \cdot P\CB{X_n = i} \\
&= \sum_{i=0}^{n} \P{n + \EE{S_{n-i}}} \cdot P\CB{X_n = i} = n + \sum_{i=0}^{n} \EE{S_{n-i}} \cdot P\CB{X_n = i}
\end{align}$
>
>And since $\EE{S_0} = 0$ we rewrite it as $\EE{S_n} = n + \EE{S_{n-X_n}}$. To solve this, we first make a guess. What if there were exactly one matach in each round?.

>Thus, totally there'll be $n+\cdots+1  = n\P{n+1}/2$ selections. So, for $n \geq 2$, we assume that
>
>$\bspace an + bn^2 = n + \EE{a\P{n-X_n} + b\P{n-X_n}^2} = n + a\P{n-\EE{X_n}} + b\P{n^2 - 2n\EE{X_n} + \EE{X_n^2}}$
>
>Following the results before that $\EE{X_n} = \Var{X_n} = 1$, we can solve this that $a=1$ and $b = 1/2$. So that maybe, $\EE{S_n} = n + n^2/2$. What a guess (2333)! Now we prove it by induction on $n$.
>
> For $n=2$, the number of rounds is a geometric random variable with parameter $p = 1/2$. And it's obvious that the number of selections is twice the number of rounds. Thus, $\EE{S_n} = 4$. Right!
>
>Hence, upon assuming $\EE{S_0} = \EE{S_1} = 0$ and $\EE{S_k} = k+k^2/2$ for $k=2,3,\dots,n-1$:
>
>$\bspace \begin{align}
\EE{S_n} &= n + \EE{S_n} \cdot P\CB{X_n = 0} + \sum_{i=1}^{n} \SB{n-i+\P{n-i}^2}\cdot P\CB{X_n=i}\\
&= n + \EE{S_n}\cdot P\CB{X_n = 0} + \P{n+n^2/2}\P{1-P\CB{X_n = 0}} - \P{n+1}\EE{X_n} + \ffrac{\EE{X_n^2}} {2}\\[0.7em]
& \bspace \text{then we solve the equation by using }\EE{X_n} = 1 \text{ and } \EE{X_n^2} = 2\\[0.7em]
\Longrightarrow& \space\EE{S_n} = n+n^2/2
\end{align}$

$\P{c}$ Find the expected number of false selections made by one of the $n$ people for $n \geq 2$.

>Let $C_j$ denote the number of hats chosen by person $j$, then we have $\sum C_j = S_n$. Then taking the expectation, using the fact that each $C_j$ takes the same mean, $\EE{C_j} = \EE{S_n}/ n = 1 + n/2$. Thus, the result is $\EE{C_j - 1} = n/2$.

$\P{d}$ Suppose the first round, what if the first person cannot meet a match? Given that, find the conditional expected number of matches.

>Let $Y$ equal $1$ if the first person has a match and $0$ otherwise. Let $X$ denote the number of matches. Then, with the result before we have
>
>$\bspace \begin{align}
1 = \EE{X} &= \EE{X\mid Y=1} P\CB{Y=1} + \EE{X\mid Y=0} P\CB{Y=0} \\
&= \ffrac{\EE{X\mid Y=1}} {n} + \ffrac{n-1} {n}\EE{X \mid Y = 0}
\end{align}$
>
>Now given that $Y=1$, then the rest $n-1$ people will choose $n-1$ hats, thus $\EE{X\mid Y=1} = 1+1=2$, thus, the result is $\EE{X \mid Y=0} = \P{n-2}/\P{n-1}$

**(V)e.g.** Consecutive success

Independent trials, each of which is a success with probability $p$, are performed until there are $k$ consecutive success. What's the mean of necesssary trials?

>Let $N_k$ denote the number of necessary trials to obtain $k$ consecutive success and $M_k = \EE{N_k}$. Then we might write: $N_k = N_{k-1} + \texttt{something}$... And actually that something is the number of additional trials needed to go from have $k-1$ successes in a row to having $k$ in a row. We denote that by $A_{k-1,k}$. Then we take the expectation and obtain:

>$\bspace M_k = M_{k-1} + \EE{A_{k-1,k}}$. Wanna know what's $\EE{A_{k-1,k}}$? There're only two possible result, the $k\texttt{-th}$ trial is a success, or not, then:
>
>$\bspace \EE{A_{k-1,k}} = 1 \cdot p + \P{1+M_k}\cdot\P{1-p} = 1+\P{1-p}M_k \Rightarrow M_k = \ffrac{1} {p} + \ffrac{M_{k-1}} {p}$
>
>Well, what's $M_1$? Obviously $N_1$ is a geometric with parameter $p$, thus $M_1 = 1/p$ and recursively we have:
>
>$\bspace M_k = \ffrac{1} {p} + \ffrac{1} {p^2} + \cdots \ffrac{1} {p^k}$

**(V)e.g.** Quick-Sort Algorithm

Given a set of $n$ distinct values, sort them increasingly. The quick-sort algorithm is defined reursively as: When $n=2$, compare the two values and puts them in appropriate order. When $n>2$, randomly shoose one of the $n$ values, say, $x_i$ and then compares each of the other $n-1$ values with $x_i$, noting which are smaller and which are larger than $x_i$. Letting $S_i$ denote the set of elements smaller than $X_i$ and $\bar{S}_i$ the set of elements greater than $X_i$. Then sort the set $S_i$ and $\bar{S}_i$. That's it.

One measure of the effectiveness of this algorithm is the expected number of comparison that it makes. Denote by $M_n$ the expected number of comparisons needed by the q-s algorithm to sort a set of $n$ distinct values. To obtain a recursion for $M_n$ we condition on the rank of the initial value selected to obtain

$$M_n = \sum_{j=1}^{n} \EE{\text{number of comparisons}\mid \text{value selected is actually j}\texttt{-th}\text{ smallest}}\cdot \ffrac{1} {n}$$

So $M_n = \d{\sum_{j=1}^{n} \P{n-1 + M_{j-1} + M_{n-j}}\cdot\frac{1} {n}} = n-1 + \ffrac{2} {n} \sum_{k=1}^{n-1} M_k$ ($M_0 = 0$), or equivalently, $nM_n = n\P{n-1} + 2\d{\sum_{k=1}^{n-1}}M_k$.

我们用一点数列知识。。。

$\bspace \P{n+1}M_{n+1} - mM_n = 2n+2M_n \Longrightarrow M_n = 2\P{n+2}\sum\limits_{k=0}^{n-1}\ffrac{n-k} {\P{n+1-k}\P{n+2-k}} = 2\P{n+2}\sum\limits_{i=1}^{n} \ffrac{i} {\P{i+1}\P{i+2}}$ for $n \geq 1$.

$$\begin{align}
M_{n+1} &= 2\P{n+2}\SB{\sum_{i=1}^{n}\ffrac{2} {i+2} - \sum_{i=1}^{n}\ffrac{1} {i+1}} \\
&\approx 2\P{n+2}\SB{\int_{3}^{n+2} \ffrac{2} {x} \;\dd{x} - \int_{2}^{n+1} \ffrac{1} {x} \;\dd{x}}\\
&= 2\P{n+2}\SB{\log\P{n+2} + \log\P{\ffrac{n+1} {n+2}} + \log 2 -2\log 3}\\
&\approx 2\P{n+2}\log\P{n+2}
\end{align}$$

### Computing Variances by Conditioning

Here we first gonna use $\Var{X} = \EE{X^2} - \EE{X}^2$ while using the condioning to obtain both the $\EE{X^2}$ and $\EE{X}$.

**e.g.** Variance of the Geometric $r.v.$

Independent trials, each resulting in a success with probability $p$ and are performed in sequence. Let $N$ be the trial number of the first success. Find $\Var{N}$.

>Still we condition on the first trial: let $Y=1$ if the first trial is a success, and $0$ otherwise.
>
>$\bspace\begin{align}
\EE{N^2} &= \EE{\EE{N^2\mid Y}} \\
&= \EE{N^2 \mid Y=1}\cdot P\P{Y=1} + \EE{N^2 \mid Y=0}\cdot P\P{Y=0}\\
&= 1 \cdot p + \EE{\P{1+N}^2} \cdot \P{1-p}\\
&= 1 + \EE{2N+N^2} \cdot \P{1-p} \\
&= 1 + 2 \P{1-p}\EE{N} + \EE{N^2}\cdot \P{1-p}
\end{align}$
>
>And from **e.g.** The Mean of a Geometric Distribution, we've acquired that $\EE{N} = 1/p$, thus we can substitute this back and then solve the equation and get: $\EE{N^2} = \ffrac{2-p} {p^2}$. Then
>
>$\bspace \Var{N} = \ffrac{2-p} {p^2} - \P{\ffrac{1} {p}}^2 = \ffrac{1-p} {p^2}$

***

Then how about (a drink?) $\Var{X \mid Y}$? Here's the proposition.

$Proposition$ ***conditional variance formula***

$\bspace \Var{X} = \EE{\Var{X \mid Y}} + \Var{\EE{X \mid Y}}$

$Proof$

>$\bspace \begin{align}\EE{\Var{X \mid Y}} &= \EE{\EE{X^2 \mid Y} - \P{\EE{X\mid Y}}^2} \\
&= \EE{\EE{X^2 \mid Y}} - \EE{\P{\EE{X\mid Y}}^2} \\
&= \EE{X^2} - \EE{\P{\EE{X\mid Y}}^2}
\end{align}$
>
>$\bspace \begin{align}
\Var{\EE{X \mid Y}} &= \EE{\P{\EE{X \mid Y}}^2} - \P{\EE{\EE{X \mid Y}}}^2\\
&= \EE{\P{\EE{X \mid Y}}^2} - \P{\EE{X}}^2
\end{align}$
>
>Therefore, $\EE{\Var{X \mid Y}} + \Var{\EE{X \mid Y}} = \EE{X^2} - \P{\EE{X}}^2 = \Var{X}$

**e.g.** The Variance of a Compound $r.v.$

Let $X_1,X_2,\dots$ be $i.i.d.$ $r.v.$ with distribution $F$ having mean $\mu$ and variance $\sigma^2$, and assume that they are independent of the nononegative integer valued $r.v.$ $N$. Here the **compound $r.v.$** is $S = \sum_{i=1}^{N}X_i$. Find its variance.

>You can directly condition on $N$ to obtain $\EE{S^2}$, well.
>
>$\bspace \Var{S\mid N=n} = \Var{\sum\limits_{i=1}^{n} X_i} = n\sigma^2$
>
>and with the similar reasoning, we have $\EE{S \mid N=n} = n\mu$. Thus, $\Var{S\mid N} = N\sigma^2$ and $\EE{S \mid N} = N \mu$ and the conditional variance formula gives
>
>$\bspace \Var{S} = \EE{N\sigma^2} + \Var{N\mu} = \sigma^2 \EE{N} + \mu^2 \Var{N}$

$Remark$

>CANNOT directly write $\Var{S\mid N} = \EE{\P{S\mid N}^2}-\cdots$, because it's not well defined. So the correct way is to first find $\Var{S\mid N = n}$. Then convert this to the variable $\Var{S\mid N} = f\P{N}$
***

**e.g.** The Variance in the Matching Rounds Problem

Following the previous definition, $R_n$ is the number of rounds that are necesssary when $n$ individuals are initially present. Let $V_n$ be its variance. Show that $V_n = n$ for $n \geq 2$.

> Actually when you try $n=2$, as shown before, it's a geometric with parameter $p = 1/2$ thus $V_2 = \ffrac{1-p} {p^2} = 2$. So the assumption here is $V_j = j$ for $2\leq j \leq n-1$. And when there're $n$ individuals. still we condition that on the first round, or more specificly, let $X$ be the number of matches in the first round. Thus $\EE{R_n \mid X} = 1 + \EE{R_{n-X}}$ and by the previous result, we have $\EE{R_n \mid X} = 1 + n - X$. Also with $V_0 = 0$, we have $\Var{R_n \mid X} = \Var{R_{n-X}} = V_{n-X}$. Hence by the **conditional variance formula**,
>
>$\bspace \begin{align}
V_n &= \EE{\Var{R_n \mid X}} + \Var{\EE{R_n \mid X}} \\[0.6em]
&= \EE{V_{n-X}} + \Var{1+n-X} \\[0.5em]
&= \sum_{j=0}^{n} V_{n-j}\cdot P\CB{X=j} + \Var{X} \\
&= V_n \cdot P \CB{X=0} + \sum_{j=1}^{n} \P{n-j}\cdot P\CB{X=j} + \Var{X} \\
&= V_n \cdot P \CB{X=0} + n \P{1-P\CB{X=0}} - \EE{X} + \Var{X}
\end{align}$
>
>Here $\EE{X}$ is given in a example from Chapter 2 and as for $\Var{X}$, with the similar method, we have
>
>$\bspace \begin{align}
\Var{X} &= \sum\Var{X_i} + 2\sum_{i=1}^{N}\sum_{j>i} \Cov{X_i,X_j} \\
&= N\P{\EE{X_i^2} - \P{\EE{X_i}}^2} + 2\sum_{i=1}^{N}\sum_{j>i} \P{\EE{X_iX_j} - \EE{X_i} \EE{X_j}} \\
&= N\P{\ffrac{1} {N} - \ffrac{1} {N^2}} + 2 \ffrac{N\P{N-1}} {2} \P{\ffrac{1} {N\P{N-1}} - \ffrac{1} {N^2}} =1
\end{align}$
>
>Thus we substitute the value into the last equation and solve is. Then it's proved.

## Computing Probabilities by Conditioning

We've already know that by conditioning we can easily calculate the expectation and variance. Now we present how to use this method to find certain probabilities. First we define the ***indicator $r.v.$***:

$\bspace X = \begin{cases}
1, &\text{if } E \text{ occurs}\\
0, &\ow
\end{cases}$

Then $\EE{X} = P\P{E}$ and $\EE{X\mid Y=y} = \P{E\mid Y=y}$ for any $r.v.$ $Y$. Therefore,

$\bspace P\P{E} = \begin{cases}
\d{\sum_y P\CB{E \mid Y=y} \cdot P\CB{Y=y}}, & \text{if }Y\text{ is discrete}\\
\d{\int_{-\infty}^{\infty} P\CB{E \mid Y=y} \cdot f_Y\P{y}\;\dd{y}}, & \text{if }Y\text{ is continuous}\\
\end{cases}$

**e.g.** The probability of a $r.v.$ is less than another one

Let $X$ and $Y$ be two independent continuous $r.v.$ with densities $f_X$ and $f_Y$ respectively. Compute $P\CB{X < Y}$

> $\bspace \begin{align}
P\CB{X < Y} &= \int_{-\infty}^{\infty} P\CB{X<Y \mid Y=y} \cdot f_Y\P{y} \;\dd{y} \\
&= \int_{-\infty}^{\infty} P\CB{X<y} \cdot f_Y\P{y} \;\dd{y} \\
&= \int_{-\infty}^{\infty} F_X\P{y} \cdot f_Y\P{y} \;\dd{y} \\
\end{align}$

**e.g.**

A possion $r.v.$ with parameter $\lambda$, and each time it happens the result split into two, success and failure, with probability $p$ and $1-p$. Find the joint probability of exactly $n$ successes and $m$ failures.

>Let $T$ be the total times it happens. We can directly write that
>
>$\bspace \begin{align}
P\CB{S=n,F=m} &= P\CB{S=n,F=m,T=n+m}\\
&= P\CB{S=n,F=m \mid T = n+m} \cdot P\CB{T = n+m}
\end{align}$
>
>or by
>
>$\bspace \begin{align}
P\CB{S=n,F=m} &= \sum\limits_{t=0}^{\infty}P\CB{S=n,F=m \mid T = t} \cdot P\CB{T =t} \\
&= 0 + \CB{S=n,F=m \mid T = n+m} \cdot P\CB{T = n+m}
\end{align}$
>
>Thus, since it's just a binomial probability of $n$ successes in a $n+m$ trials, we have
>
>$\bspace \begin{align}
P\CB{S=n,F=m} &= \binom{n+m} {n} p^n \P{1-p}^{m} e^{-\lambda} \ffrac{\lambda^{n+m}} {\P{n+m}!}\\
&= \ffrac{\P{n+m}!} {n!m!} p^n \P{1-p}^{m} \lambda^n \lambda^m \ffrac{e^{-\lambda p}e^{-\lambda\P{1-p}}} {\P{n+m}!}\\
&= e^{-\lambda p} \ffrac{\P{\lambda p}^{n}} {n!}\cdot e^{-\lambda \P{1-p}} \ffrac{\P{\lambda \P{1-p}}^{m}} {m!}
\end{align}$
>
>That's it.

$Remark$

>It's also can be regarded as the product of two independent terms who are only rely on $n$ and $m$ respectively. It follows that $S$ and $F$ are independent. Moreover,
>
>$\bspace P\CB{S=n} = \sum\limits_{m=0}^{\infty}P\CB{S=n,F=m} = e^{-\lambda p} \ffrac{\P{\lambda p}^{n}} {n!}$
>
>And similar for $P\CB{F=m} = e^{-\lambda \P{1-p}} \ffrac{\P{\lambda \P{1-p}}^{m}} {m!}$

$Remark$

>We can also generalize the result to the case where each of a Poisson distributed number of events, $N$, with mean $\lambda$ is independently classified as being one of $k$ types with the probability that it is type $i$ being $p_i$. And $N_i$ is the number that are classified as type $i$, then:
>
> $N_i$ for $i = 1,2,\dots,k$ are independent Poisson $r.v.$ with respective means $\lambda p_1,\lambda p_2,\dots, \lambda p_k$, and this follows that:
>
>$\bspace \begin{align}
&P\CB{N_1 = n_1, N_2 = n_2,\dots, N_k = n_k} \\
=\;& P\CB{N_1 = n_1, N_2 = n_2,\dots, N_k = n_k \mid N = n} \cdot P\CB{N = n} \\[0.5em]
=\;& \binom{n} {n_1,n_2,\dots,n_k} \cdot e^{-\lambda} \ffrac{\lambda^n} {n!} \\
=\;& \prod_{i=1}^{k} e^{-\lambda p_i} \ffrac{\P{\lambda p_i}^{n_i}}{n_i!}
\end{align}$

**e.g.** The Distribution of the Sum of Independent Bernoulli $r.v.$

Let $X_i$ be independent Bernoulli $r.v.$ with $P\CB{X_i = 1} = p_i$. What's the pmf of $\sum X_i$? Here's a recursive way to obtain all these.

> First let $P_k\P{j} = P\CB{X_1 + X_2 + \cdots + X_k = j}$ and note that $P_k\P{0} = \prod\limits_{i=1}^{k} q_i$, and $P_k\P{k} = \prod\limits_{i=1}^{k} p_i$. Then we condition $P_k\P{j}$ on $X_k$. (We don't contion that on the first event this time, cause we are doing a recursion! RECURSION! F\*\*\*)
>
> $\bspace \begin{align}
P_k\P{j} &= P\CB{X_1 + X_2 + \cdots + X_k = j \mid X_k = 1} \cdot p_k + P\CB{X_1 + X_2 + \cdots + X_k = j \mid X_k = 0} \cdot q_k \\
&= P_{k-1}\P{j-1}  \cdot p_k + P_{k-1}\P{j} \cdot q_k
\end{align}$

**e.g.** The Best Prize Problem

$n$ girls appeared in my life in sequence. I have to confess once if I met her or never would I got another chance. The only information I have when deciding whether to confess is the relative rank of that girl compared to ones alreay met. My wish is to maximize the probability of obtaining the best lover. Assuming all $n!$ ordering of the girls are equally likely, how do we do?

> Strategy: fix a value $k$ and only confess to the first girl after that is better than all first $k$ girls. Using this, we denote $P_k\P{\text{best}}$ as the probability that the best girl is confessed by me.
>And to find that we condition that on $X$, the position of best girl. This gives:
>
>$\bspace \begin{align}
P_k\P{\text{best}} &= \sum_{x=1}^{n} P_k\P{\text{best}\mid X =x}\cdot P\CB{X = x} \\
&= \ffrac{1} {n}\P{\sum_{x=1}^{k} P_k\P{\text{best}\mid X =x} +\sum_{x=k+1}^{n} P_k\P{\text{best}\mid X =x}}\\
&= \ffrac{1} {n}\P{0 + \sum_{x=k+1}^{n}\ffrac{k} {i-1}}\\
&\approx \ffrac{k} {n}\int_{k}^{n-1} \ffrac{1} {x} \;\dd{x} \approx \ffrac{k} {n} \log\P{\ffrac{n} {k}}
\end{align}$

>To find its maximum, we differentiate $g\P{x} = \ffrac{x} {n} \log\P{\ffrac{n} {x}}$ and get $x_{\min} = \ffrac{n} {e}$

**e.g.** Hat match game

$n$ men with their hats mixed up and then each man randomly select one. What's the probability of no matches? or exactly $k$ matches?

>We are finding this by conditioning on whether or not the first man selects his own hat, $M$ or $M^c$. Let $E$ denote the event that no matches occur. Then
>
>$\bspace P_n = P\P{E} = P\P{E\mid M} \cdot P\P{M} + P\P{E\mid M^c} \cdot P\P{M^c}=P\P{E\mid M^c}\ffrac{n-1} {n}$
>
>Following that, since it's given that the first man doesn't get a hat, then there're only $n-1$ men left and thus
>
>$\bspace P\P{E\mid M^c} = P_{n-1} + \ffrac{1} {n-1} P_{n-2}$
>
>saying that it's equal to the condition when the second person find his hat, or not.
>
>Then since $P_1 = 0$, $P_2 = 0.5$ we can find all of them recursively.

**e.g.** The Ballot Problem

In an election, candidate $A$ have received $n$ votes, and $B$, $m$ votes with $n>m$. Assuming that all orderings are equally likely, show that the probability that $A$ is always ahead in the count of votes is $\P{n-m}/\P{n+m}$.

>Let $P_{n,m}$ denote the desired probability then:
>
>$\begin{align}
P_{n,m} &= P\CB{A \text{ always ahead}\mid A \text{ receive last vote}} \cdot \ffrac{n} {n+m} \\
& \bspace + P\CB{A \text{ always ahead}\mid B \text{ receive last vote}} \cdot \ffrac{m} {n+m} \\
&= \ffrac{n} {n+m} \cdot P_{n-1,m} + \ffrac{m} {n+m} \cdot P_{n,m-1}
\end{align}$
>
>Then by induction, done! $P_{n,m} = \ffrac{n} {n+m} \cdot \ffrac{n-1-m} {n-1+m} + \ffrac{m} {n+m} \cdot \ffrac{n-m+1} {n+m-1} = \ffrac{n-m} {n+m}$

***

**e.g.**

Let $U_1,U_2,\dots$ be a sequence of independent uniform $\P{0,1}$ $r.v.$, and let $N = \min\CB{n \geq 2: U_n > U_{n-1}}$ and $M = \min\CB{n \geq 1: U_1 + U_2 + \dots + U_n > 1}$. Surprisingly, $N$ and $M$ have the same probability distribution, and their common mean is $e$! Prove it!

> For $N$ since all the possible ordering or $U_1,\dots,U_n$ are equally likely, we have:
>
>$\bspace P\CB{U_1 > U_2 > \cdots > U_n} = \ffrac{1} {n!} = P\CB{N > n}$.
>
>For $M$, to use induction method, we intend to prove $P\CB{M\P{x} > n} = x^n/n!$ where $M\P{x} = \min\CB{n\geq 1: U_1 + U_2 + \cdots + U_n > x}$, for $0 < x \leq 1$. When $n=1$,

> $\bspace P\CB{M\P{x} > 1} = P\CB{U_1 \leq x} = x$
>
>Then assume that holds true for all $n$ to determine $P\CB{M\P{x} > n+1}$ we condition that on $U_1$ to obtain:
>
>$\bspace \begin{align}
P\CB{M\P{x} > n+1} &= \int_{0}^{1} P\CB{M\P{x} > n+1\mid U_1 = y}\;\dd{y} \\
& \bspace\text{since }y \text{ cannot exceed }x\text{ , we change the upper limit of integral} \\
&= \int_{0}^{x} P\CB{M\P{x} > n+1\mid U_1 = y}\;\dd{y} \\
&= \int_{0}^{x} P\CB{M\P{x-y} > n}\;\dd{y} \\
&\bspace \text{induction hypothesis}\\
&= \int_{0}^{x} \ffrac{\P{x-y}^n} {n!}\;\dd{y} \\
&\using{u=x-y} \int_{0}^{x} \ffrac{u^n} {n!} \;\dd{u} \\
&= \ffrac{x^{n+1}} {\P{n+1}!}
\end{align}$
>
>Thus $P\CB{M\P{x} > n} = x^n/n!$ and let $x=1$ and we can draw the final conclusion that $P\CB{M > 1} = 1/n!$, so that $N$ and $M$ have the same distribution. Finally, we have:
>
>$$\EE{M} = \EE{N} = \QQQ \sum_{n=0}^{\infty} 1/n! = e$$

**e.g.**

Let $X_1, X_2,\dots,X_n$ be independent continuous $r.v.$ with a common distribution function $F$ and density $f = F'$, suppose that they are to be observed one at a time in sequence. Let $N = \min\CB{n\geq 2: X_n = \text{second largest of }X_1,X_2,\dots,X_n}$ and $M = \min\CB{n\geq 2: X_n = \text{second smallest of }X_1,X_2,\dots,X_n}$. Which one tends to be larger?

> To find $X_N$ it's natural to condition on the value of $N$. We first let $A_i = \CB{X_i \neq \text{second largest of }X_1,X_2,\dots,X_i}, i\geq 2$. Thus
>
> $$\newcommand{\void}{\left.\right.}P\CB{N=n} = P\P{A_2A_3\cdots A_{n-1}A^c} = \ffrac{1} {2}\ffrac{2} {3}\cdots\ffrac{n-2} {n-1} \ffrac{1} {n} = \ffrac{1} {n\P{n-1}}$$
>
>Here $A_i$ being independent is because $X_i$ are $i.i.d.$. Then we condition that on $N$ and obtain:
>
>$$\begin{align}
f_{X_{\void_N}}\P{x} &= \sum_{n=2}^{\infty} \ffrac{1} {n\P{n-1}} f_{X_{\void_N}\mid N} \P{x\mid n} \\
&= \sum_{n=2}^{\infty} \ffrac{1} {n\P{n-1}} \ffrac{n!} {1!\P{n-2}!} \P{F\P{x}}^{n-2}\cdot f\P{x} \cdot \P{1-F\P{x}} \\
&= f\P{x} \cdot \P{1-F\P{x}} \sum_{n=0}^{\infty} \P{F\P{x}}^i \\
&= f\P{x}
\end{align}$$
>
>We are almost there. Now we think their opposite number. Let $W_i = -X_i$, then $M = \min\CB{n\geq 2: X_n = \text{second largest of }W_1,W_2,\dots,W_n}$. So similiarly, $W_M$ has the same distribution with $W_1$ just like $X_N$ and $X_1$. Then we drop the minus sign so that $X_M$ has the same distribution with $X_1$. So they all are of the same distribution.

$Remark$

>This is a special case for ***Ignatov's Theorem***, where second could be $k\texttt{th}$ largest/smallest. Still the distribution is $F$, for all $k$!

***

**(V)e.g.**

A population consists of $m$ families. Let $X_j$ denote the size of family $j$ and suppose that $X_1,X_2,\dots,X_m$ are independent $r.v.$ having the common pmf: $p_k = P\CB{X_j = k}, \sum_{i=1}^{\infty}p_k = 1$ with mean $\mu = \sum\limits_k k\cdot p_k$. Suppose a member of whatever family is chosen randomly, and let $S_i$ be the event that the selected individual is from a family of size $i$. Prove that $P\P{S_i} \to \ffrac{ip_i} {\mu}$ as $m \to \infty$.

> This time we need to condition on a vector of $r.v.$. Let $N_i$ be the number of familier that are of size $i$: $N_i = \#\CB{k:k=1,2,\dots,m:X_k = i}$; and then condition on $\mathbf{X} = \P{X_1,X_2,\dots,X_m}$:
>
>$$P\P{S_i\mid \mathbf{X}} = \ffrac{iN_i} {\sum_{i=1}^{m} X_k}$$
>
>$$P\P{S_i} = \EE{P\P{S_i\mid X}}
= \EE{\ffrac{iN_i} {\sum_{i=1}^{m} X_k}}
= \EE{\ffrac{iN_i/m} {\sum_{i=1}^{m} X_k/m}}$$
>
>$\bspace$This follows by the **strong law of large numbers** that $N_i/m$ would converges to $p_i$ as $m \to \infty$, and $\sum_{i=1}^{m} X_k/m \to \EE{X} = \mu$. Thus: $P\P{S_i} \to \EE{\ffrac{ip_i} {\mu}} = \ffrac{ip_i} {\mu}$ as $m \to \infty$

***

**e.g.**

Consider $n$ independent trials in which each trials results in one of the outcomes $1,2,\dots,k$ with respective probabilities $p_i$ and $\sum p_i = 1$. Suppose further that $n > k$, and that we are interested in determining the probability that each outcome occurs at least once. If we let $A_i$ denote the event that outcome $i$ dose not occur in any of the $n$ trials, then the desired probability is $1 - P\P{\bigcup_{i=1}^{k} A_i}$ and by the inclusion-exclusion theorem, we have

$$\begin{align}
p\P{\bigcup_{i=1}^{k} A_i} =& \sum_{i=1}^{k} P\P{A_i} - \sum_i\sum_{j>i} P\P{A_iA_j} \\
&\bspace + \sum_i\sum_{j>i}\sum_{k>j} P\P{A_iA_jA_k} - \cdots + \P{-1}^{k+1} P\P{A_1 \cdots A_k}
\end{align}$$

where

$$\begin{align}
P\P{A_i} &= \P{1-p_i}^{n} \\
P\P{A_iA_j} &= \P{1-p_i - p_j}^{n}, \bspace i < j\\
P\P{A_iA_jA_k} &= \P{1-p_i - p_j - p_k}^{n}, \bspace i < j < k\\
\end{align}$$

How to solve this by conditioning on whatever something?

> Note that if we start by conditioning on $N_k$, the number of times that outcome $k$ occurs, then when $N_k>0$ the resulting conditional probability will equal the probability that all of the outcomes $1,2,\dots,k-1$  occur at least once when $n-N_k$ trails are performed, and each results in outcome $i$ with probability $p_i/\sum_{j=1}^{k-1} p_j$, for $i = 1, 2,\dots,k-1$. Then we could use a similar conditioning step on these terms.
>
>Follow this idea, we let $A_{m,r}$, for $m\leq n, r\leq k$, denote the event that each of the outcomes $1,2,\dots,r$ occurs at least once when $m$ independent trails are performed, where each trial results in one of the outcomes $1,2,\dots,r$ with respective probabilities $p_1/P_r, \dots,p_r/P_r$, where $P_r = \sum_{j=1}^{r}p_j$. Then let $P\P{m,r} = P\P{A_{m,r}}$ and note that $P_{n,k}$ is the desired probability. To obtain the expression of $P\P{m,r}$, condition on the number of times that outcome $r$ occurs. This gives rise to:
>
>$\bspace\begin{align}
P_{m,r} &= \sum_{j=0}^{m} P\CB{A_{m,r} \mid r \text{ occurs }j \text{ times}} \binom{m}{j} \P{\ffrac{p_r} {P_r}}^j \P{1 - \ffrac{p_r} {P_r}}^{m-j} \\
&= \sum_{j=1}^{m-r+1} P_{m-j,r-1}\binom{m}{j} \P{\ffrac{p_r} {P_r}}^j \P{1 - \ffrac{p_r} {P_r}}^{m-j}
\end{align}$
>
>Starting with $P\P{m,1} = 1$ for $m \geq 1$ and $P\P{m,1} = 0$ for $m=0$.
>
>And this's how this recursion works. We can first find the $P\P{m,2}$ for $m = 2,3,\dots,n-\P{k-2}$ and then $P\P{m,3}$ for $m = 2,3,\dots,n-\P{k-3}$ and so on, up to $P\P{m,k-1}$ for $m = k-1,k,\dots,n-1$. Then we use the recursion to compute $P\P{n,k}$.

***

Now we extend our conclusion of how to calculate certain expectations using the conditioning fashion. Here's another formula for this:

$\bspace\EE{X \mid Y = y} = \begin{cases}
\d{\sum_{w} \EE{X \mid W = w, Y = y} \cdot P\CB{W = w \mid Y = y}}, & \text{if } W \text{ discrete} \\
\d{\int_{w} \EE{X \mid W = w, Y = y} \cdot f_{W \mid Y}P\P{w \mid y} \;\dd{w}}, & \text{if } W \text{ continuous}
\end{cases}$

and we write this as $\EE{X \mid Y} = \EE{\EE{X \mid Y,W}\mid Y}$

**e.g.**

Automobile insurance company classifies its policyholders as one of the types $1, 2, \dots, k$. It supposes that the numbers of accidents that a type $i$ policyholder has in the following years are independent Poisson random variables with mean $\lambda_i$. For a new policyholder, the probability that he being type $i$ is $p_i$.

Given that a policyholder had $n$ accidents in her first year, what is the expected number that she has in her second year? What is the conditional probability that she has $m$ accidents in her second year?

>Let $N_i$ denote the number of accidents the policyholder has in year $i$. To obtain $\EE{N_2 \mid N_1 = n}$, condition on her risk type $T$.

>$$\begin{align}
\EE{N_2 \mid N_1 = n} &= \sum_{j=1}^{k} \EE{N_2\mid T = j, N_1 = n} \cdot P\CB{T = j \mid N_1 = n} \\
&= \sum_{j=1}^{k} \EE{N_2\mid T = j} \cdot \ffrac{P\CB{T = j, N_1 = n}} {P\CB{N_1 = n}} \\
&= \ffrac{\sum\limits_{j=1}^{k} \lambda_j \cdot P\CB{N_1 = n \mid T = j} \cdot P\CB{T = j}} {\sum\limits_{j=1}^{k} P \CB{ N_1 = n \mid T = j}\cdot P\CB{T = j}} \\
&= \ffrac{\sum\limits_{j=1}^{k} e^{-\lambda_j} \lambda_j^{n+1}p_j} {\sum\limits_{j=1}^{k} e^{-\lambda_j} \lambda_j^{n} p_j}
\end{align}$$
>
>$$\begin{align}
P\CB{N_2 = m \mid N_1 = n} &= \sum_{j=1}^{k} P\CB{N_2 = m \mid T = j, N_1 = n} \cdot P\CB{T = j \mid N_1 = n} \\
&= \sum_{j=1}^{k} e^{-\lambda_j} \ffrac{\lambda_j^m} {m!} \cdot P\CB{T = j \mid N_1 = n} \\
&= \ffrac{\sum\limits_{j=1}^{k} e^{-2\lambda_j} \lambda_j^{m+n} p_j} {m! \sum\limits_{j=1}^{k} e^{-\lambda_j} \lambda_j^n p_j}
\end{align}$$

$Remark$

$\bspace P\P{A \mid BC} = \ffrac{P\P{AB \mid C}} {P\P{B \mid C}}$
***

## Some Applications
See the extended version later if possible.😥

## An Identity for Compound Random Variables

Let $X_1,X_2,\dots$ be a sequence of $i.i.d.$ $r.v.$ and let $S_n = \sum_{i=1}^{n} X_i$ be the sum of the first $n$ of them, $n \geq 0$, where $S_0 = 0$. Then the **compound random variable** is defined as $S_N = \sum\limits^N X_i$ with the distribution of $N$ called the **compounding distribution**.

To find $S_N$, first define $M$ as a $r.v.$ that is independnet of the sequence $X_1,X_2,\dots$, and which is such that $P\CB{M = n} = \ffrac{nP\CB{N = n}} {\EE{N}}$.

$Proposition.5$ The Compound $r.v.$ Identity

$\bspace$For any function $h$, $\EE{S_N h\P{S_N}} = \EE{N} \cdot \EE{X_1 h\P{S_M}}$

$Proof$

$$\begin{align}
\EE{S_N h\P{S_N}} &= \EE{\sum_{i=1}^{N} X_i h\P{X_N}} \\
&= \sum_{n=0}^{\infty} \EE{\sum_{i=1}^{N} X_i h\P{X_N}\mid N = n} \cdot P\CB{N = n} \\
&= \sum_{n=0}^{\infty} \EE{\sum_{i=1}^{n} X_i h\P{X_N}\mid N = n} \cdot P\CB{N = n} \\
&  \bspace\text{by the independence of }N \text{ and }X_1,X_2,\dots \\
&= \sum_{n=0}^{\infty} \EE{\sum_{i=1}^{n} X_i h\P{X_N}} \cdot P\CB{N = n} \\
&= \sum_{n=0}^{\infty} \sum_{i=1}^{n} \EE{X_i h\P{S_n}} \cdot P\CB{N = n} \\
&  \bspace\EE{X_ih\P{X_1 + X_2 + \cdots + X_n}} \text{ are symmetric} \\
&= \sum_{n=0}^{\infty} n \EE{X_1 h\P{S_n}} \cdot P\CB{N = n} \\
&= \EE{N} \sum_{n=0}^{\infty} \EE{X_1 h\P{S_n}} \cdot P\CB{M = n} \\
&= \EE{N} \sum_{n=0}^{\infty} \EE{X_1 h\P{S_n} \mid M = n} \cdot P\CB{M = n} \\
&  \bspace\text{independence of  }M \text{ and }X_1,X_2,\dots, X_n \\
&= \EE{N} \sum_{n=0}^{\infty} \EE{X_1 h\P{S_M} \mid M=n} \cdot P\CB{M = n} \\
&= \EE{N} \EE{X_1 h\P{S_M}}
\end{align}$$

$Corollary.6$

Suppose $X_i$ are positive integer valued $r.v.$, and let $\alpha_j = P\CB{X_1 = j}$, for $j > 0$. Then:

$\bspace\begin{align}
P\CB{S_N = 0} &= P\CB{N = 0} \\
P\CB{S_N = k} &= \ffrac{1}{k} \EE{N} \sum_{j=1}^{k} j \alpha_j P\CB{S_{M-1} = k-j}, k > 0
\end{align}$

$Proof$

>For $k$ fixed, let
>
>$\bspace h\P{x} = \begin{cases}
1, & \text{if } x = k \\
0, & \text{if } x \neq k
\end{cases}$
>
>and then $S_N h\P{S_N}$ is either equal to $k$ if $S_N = k$ or is equal to $0$ otherwise. Therefore,
>
>$$\EE{S_Nh\P{S_N}}  = k P\CB{S_N = k}$$
>
>and the compound identity yields:
>
>$$\begin{align}
kP\CB{S_N = k} &= \EE{N} \EE{X_1 h\P{S_M}} \\
&= \EE{N} \sum_{j=1}^{\infty} \EE{X_1 h\P{S_M} \mid X_1 = j} \alpha_j \\
&= \EE{N} \sum_{j=1}^{\infty} j \EE{h\P{S_M} \mid X_1 = j} \alpha_j \\
&= \EE{N} \sum_{j=1}^{\infty} j P\CB{S_M = k \mid X_1 = j} \alpha_j \\
\end{align}$$

>And now,
>
>$$\begin{align}
P\CB{S_M = k \mid X_1 = j} &= P\CB{\sum_{i=1}^{M} X_i = k \mid X_1 = j} \\
&= P\CB{j + \sum_{i=2}^{M} X_i = k \mid X_1 = j} \\
&= P\CB{j + \sum_{i=1}^{M-1} X_i = k} \\
&= P\CB{S_{M-1} = k-j}
\end{align}$$

$Remark$

That's almost the end. Later we will show how to use this relationship to solve the problem using recursion.

### Poisson Compounding Distribution

Using $Proposition.5$, if $N$ is the Poisson distribution with mean $\lambda$, then

$\bspace\begin{align}
P\CB{M-1 = n} &= P\CB{M = n+1} \\
&= \ffrac{\P{n+1}P\CB{N = n+1}} {\EE{N}}\\
&= \ffrac{1} {\lambda} \P{n+1} e^{-\lambda} \ffrac{\lambda^{n+1}} {\P{n+1}!} \\
&= e^{-\lambda} \ffrac{\lambda^n} {n!}
\end{align}$

Thus with $P_n = P\CB{S_N = n}$, the recursion given by $Corollary.6$ can be written as

$\bspace\begin{align}
P_0 = P\CB{S_N = 0} &= P\CB{N = 0} = e^{-\lambda} \\
P_k = P\CB{S_N = k} &= \ffrac{1}{k} \EE{N} \sum_{j=1}^{k} j \alpha_j P\CB{S_{M-1} = k-j} = \ffrac{\lambda} {k} \sum_{j=1}^{k} j \alpha_j P_{k-j}, \bspace k > 0
\end{align}$

$Remark$

When $X_i$ are chosen to be identical $1$, then the preceding expressions are reduced to a **Poisson** $r.v.$

$\bspace\begin{align}
P_0 &= e^{-\lambda} \\
P_n &= \ffrac{\lambda} {n} P\CB{N = n-1}, \bspace k > 0
\end{align}$
***

**e.g.**

Let $S$ be a compound **Poisson** $r.v.$ with $\lambda = 4$ and $P\CB{X_i = i} = 0.25, i=1,2,3,4$.

>$\bspace\begin{align}
P_0 &= e^{-\lambda} = e^{-4} \\
P_1 &= \lambda\alpha_1 P_0 = e^{-4} \\
P_2 &= \ffrac{\lambda} {2} \P{\alpha_1 P_1 + 2 \alpha_2 P_0} = \ffrac{3} {2} e^{-4} \\
P_3 &= \ffrac{\lambda} {3} \P{\alpha_1 P_2 + 2 \alpha_2 P_1 + 3 \alpha_3 P_0} = \ffrac{13} {6} e^{-4} \\
P_4 &= \ffrac{\lambda} {4} \P{\alpha_1 P_3 + 2 \alpha_2 P_2 + 3 \alpha_3 P_1 + 4 \alpha_4 P_0} = \ffrac{73} {24} e^{-4} \\
P_5 &= \ffrac{\lambda} {5} \P{\alpha_1 P_4 + 2 \alpha_2 P_3 + 3 \alpha_3 P_2 + 4 \alpha_4 P_1 + 5 \alpha_5 P_0} = \ffrac{381} {120} e^{-4} \\
\end{align}$

### Binomial Compounding Distribution

Suppose $N$ is a binomial $r.v.$ with parmeter $r$ and $p$, then

$\bspace\begin{align}
P\CB{M-1 = n} &= \ffrac{\P{n+1}P\CB{N = n+1}} {\EE{N}}\\
&= \ffrac{n+1} {rp} \binom{r} {n+1} p^{n+1} \P{1-p}^{r-n-1} \\
&= \ffrac{\P{r-1}!} {\P{r-1-n}!n!} p^n \P{1-p}^{r-1-n}
\end{align}$

Thus $M-1$ is also a binomial $r.v.$ with parameters $r-1$, $p$.
***
The missing part in this Charpter might be included in future bonus content... I need to carry on to the next chapter, the Markov Chain

Probability and Statistics/Applied Random Process/Chap_03.ipynb


# Building Linear Regression Model

## Learning Objectives

After learning this section, you'll know how to do the following:
- Convert from mathematical notations to Python code using NumPy library
- Build a Linear Regression model
- Evaluate a Linear Regression model

## Import Libraries

We begin with the following code to import the required libraries:

```python
%matplotlib inline

import matplotlib.pyplot as plt
import numpy as np
```

## Randomly Generate the Data

As usual, let's start with 2-dimensional data first. We will randomly generate the data with a dependent variable $y$ and only one independent variable $x$. The data includes 100 data points scattered about a line with a slope of 5 and an intercept of 7:

```python
num_data_points = 100

# get a new instance of random numbers generator
rng = np.random.default_rng(seed=12345)

# generate x
x = rng.random(size=(num_data_points, 1))

# generate y with the defined slope and intercept
slope = 5
intercept = 7

y = (slope * x + intercept) + rng.random(size=(num_data_points, 1))

print("Shape of x:", x.shape)
print("The first 5 rows of x:", x[:5])
print()
print("Shape of y:", y.shape)
print("The first 5 rows of y:", y[:5])
```

    Shape of x: (100, 1)
    The first 5 rows of x: [[0.22733602]
     [0.31675834]
     [0.79736546]
     [0.67625467]
     [0.39110955]]

    Shape of y: (100, 1)
    The first 5 rows of y: [[ 8.30530896]
     [ 8.82393576]
     [11.76683514]
     [10.58504095]
     [ 9.50759871]]

Let's visualize $\pmb{x}$ and $\pmb{y}$ to see how it looks:

```python
plt.scatter(x, y);
```

## Build Linear Regression Model

Let's review the Linear Regression model in matrix notation.

$$
\begin{align}
  \pmb{\hat{y}} &= \pmb{X}\pmb{w} \\
  \begin{bmatrix}
    \hat{y}^{(1)} \\
    \hat{y}^{(2)} \\
    \vdots \\
    \hat{y}^{(m)}
  \end{bmatrix} &=
    \begin{bmatrix}
      1 & x^{(1)}_1 & x^{(1)}_2 & \cdots & x^{(1)}_n \\
      1 & x^{(2)}_1 & x^{(2)}_2 & \cdots & x^{(1)}_n \\
      \vdots \\
      1 & x^{(m)}_1 & x^{(m)}_2 & \cdots & x^{(m)}_n
    \end{bmatrix} \begin{bmatrix}
      w_0 \\
      w_1 \\
      \vdots \\
      w_n
    \end{bmatrix}
\end{align}
$$

The goal of Linear Regression is to find a best fitting line to the observed data by picking the optimal $\pmb{w}$ parameters. These parameters can be found using the normal equation as follows:

$$\pmb{w} = (\pmb{X}^\top \pmb{X})^{-1} \pmb{X}^\top \pmb{y} $$

First, we need to form the $\pmb{X}$ matrix by adding a bias $x_0 = 1$ to each row of the column vector $\pmb{x}$. This can be done using the following function.

```python
def add_bias(x):
    return np.hstack([np.ones((x.shape[0], 1)), x])

X = add_bias(x)
print("The first 5 rows of X:", X[:5])
```

    The first 5 rows of X: [[1.         0.22733602]
     [1.         0.31675834]
     [1.         0.79736546]
     [1.         0.67625467]
     [1.         0.39110955]]

Then, we find the optimal model parameters $\pmb{w}$ using the normal equation. This can be implement using NumPy as follows:

```python
w = np.linalg.pinv(X.T.dot(X)).dot(X.T).dot(y)
print("w = ", w)
```

    w =  [[7.40740442]
     [5.21243721]]

Finally, we can estimate $\hat{y}$ using the computed $\pmb{w}$ values.

```python
y_hat = X.dot(w)
print("The first 5 rows of y_hat:", y_hat[:5])
```

    The first 5 rows of y_hat: [[ 8.59237916]
     [ 9.05848737]
     [11.5636218 ]
     [10.93233943]
     [ 9.44603839]]

Let's visualize the observed data point, best fitting line and its residual errors.

```python
plt.scatter(x, y, alpha=0.7)
plt.plot(x, y_hat, color="green", label="Best fitting line")

residuals = y - y_hat
plt.bar(x[:, 0], residuals[:, 0], width=0.01, label="Residual errors")

plt.legend();
```

## Evaluate Linear Regression Model

Now, it's time to evaluate our Linear Regression model performance. For regression problems, three common evaluation metrics are used.

1. **Mean Absolute Error** (MAE) is the mean of the absolute value of the residual errors.

$$\frac 1n\sum_{i=1}^n|y_i-\hat{y}_i|$$

2. **Mean Squared Error** (MSE) is the mean of the squared residual errors.

$$\frac 1n\sum_{i=1}^n(y_i-\hat{y}_i)^2$$

3. **Root Mean Squared Error** (RMSE) is the square root of the mean of the squared residual errors.

$$\sqrt{\frac 1n\sum_{i=1}^n(y_i-\hat{y}_i)^2}$$

The lower the residuals, the lower values of MAE, MSE, and RMSE. Hence, lower values of these metrics indicate better fit of data.

These evaluation metrics can be implement as follows:

```python
def mean_absolute_error(y_true, y_pred):
    return np.mean(np.abs(y_true - y_pred))

def mean_squared_error(y_true, y_pred):
    return np.mean(np.square(y_true - y_pred))

def root_mean_squared_error(y_true, y_pred):
    return np.sqrt(np.mean(np.square(y_true - y_pred)))
```

Let's define a function `evaluate` to evaluate our model performance using the above metrics.

```python
def evaluate(y, y_hat):
    mae = mean_absolute_error(y, y_hat)
    mse = mean_squared_error(y, y_hat)
    rmse = root_mean_squared_error(y, y_hat)

    print("MAE:", mae)
    print("MSE:", mse)
    print("RMSE:", rmse)

evaluate(y, y_hat)
```

    MAE: 0.25538144017166864
    MSE: 0.08714506146929557
    RMSE: 0.2952034238780024

## Linear Regression Class

It will be better if we gather the above code and functions into a `LinearRegression` class, so we can reuse it later. Its implementation is as follows:

```python
def mean_absolute_error(y_true, y_pred):
    return np.mean(np.abs(y_true - y_pred))

def mean_squared_error(y_true, y_pred):
    return np.mean(np.square(y_true - y_pred))

def root_mean_squared_error(y_true, y_pred):
    return np.sqrt(np.mean(np.square(y_true - y_pred)))

class LinearRegression:
    def __init__(self):
        self.w = None

    def fit(self, x, y):
        X = self._add_bias(x)
        self.w = np.linalg.inv(X.T.dot(X)).dot(X.T).dot(y)

    def predict(self, x):
        X = self._add_bias(x)
        return X.dot(self.w)

    def evaluate(self, x, y):
        y_pred = self.predict(x)

        mae = mean_absolute_error(y, y_pred)
        mse = mean_squared_error(y, y_pred)
        rmse = root_mean_squared_error(y, y_pred)

        print("MAE:", mae)
        print("MSE:", mse)
        print("RMSE:", rmse)

    def _add_bias(self, x):
        return np.hstack([np.ones((x.shape[0], 1)), x])
```

Let's use the new `LinearRegression` class to fit the generated data again.

```python
model = LinearRegression()
model.fit(x, y)

print("Model parameters:", model.w)
print("Evaluation results:")
model.evaluate(x, y)
```

    Model parameters: [[7.40740442]
     [5.21243721]]
    Evaluation results:
    MAE: 0.25538144017166836
    MSE: 0.08714506146929551
    RMSE: 0.2952034238780023

That's it! That's how you can implement Linear Regression model in Python using NumPy library. Now you have a `LinearRegression` class with a nice and easy-to-use API that can be applied in your own projects. Let's move to the next section where we use these implementation on real-world data. It's gonna be fun!

## Challenges

Why don't you challenge yourself a little bit? Here are some ideas:

- Generate 3-dimensional data with a dependent variable $y$ and two independent variable $x_1$, and $x_2$
- Use the `LinearRegression` class to create a model and fit it with the generated 3D data
- Visualize the fitting plane
- Why not add more independent variables, for example 1000 variables? Let's try to fit the model with a high-dimensional data and observe any changes in running time, evaluation results, etc.


# Non-linear Regression

this part comes after describing the concepts of correlation, linear and non-linear regression

An example of nonlinear regression in petrology is given by the application of the crystal-lattice-strain model  (Blundy & Wood, 1994) to interpret experimental data. In detail, this model provides a conceptual framework for quantifying partition coefficients $D_i$ in magmatic systems  (Blundy & Wood, 1994; Meltzer & Kessel,2020), where $D_i$ is given by

$\begin{equation}
{D_i = D_0  \exp\left \{ \frac{-4  \pi E  N_A  \left [ \frac{r_0}{2}(r_i - r_0)^2+ \frac{1}{3}(r_i - r_0)^3 \right ]}{R  T} \right \}},
\label{eq:crys_latt_strain_mod}\end{equation}$

where $T$ is the temperature, $r_i$ is the radius the trace element $i$ belonging to an isovalent set of elements,  $r_0$ is the radius of the ideal element that minimally strains the crystal lattice (i.e., characterized by the largest $D_i$), $D_0$ is the partition coefficient of the ideal element characterized by radius $r_0$,  $E$ is the apparent Young's modulus of the site, and $N_A$ and $R$ are Avogadro’s number and the universal gas constant, respectively  (Blundy & Wood, 1994; Meltzer & Kessel, 2020).
The above equation plots near-parabolically with ionic radius if you graph $\log_{10}(D_i)$ versus $r_i$ (Blundy& Wood, 2003).

Typically,  $r_0$, $D_0$, and $E$ are estimated by fitting by non-linear regression the above equation to the experimentally determined $D_i$.

In Python, the function scipy.optimize.curve applies the nonlinear least-squares method to fit a function to data and can be used to extract $r_0$, $D_0$, and $E$ from  experimental results for $D_i$. In detail, curve_fit() is based on three algorithms: the trust region reflective algorithm  (Branch et al., 1999), the dogleg algorithm with rectangular trust regions  (Voglis & Lagaris, 2004), and the Levenberg-Marquardt algorithm  (Moré, 1978). A detailed description of the algorithms governing the nonlinear regression is beyond the scope of this book but is available in more specialized books [see, e.g.,  Seber and Wild (1989)]. As an example, code listing reported below replicates the results reported in Fig. 2 of Meltzer and Kessel (2020).

```python
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit

def func(r, r0, D0, E):
    R=8.314462618
    scale = 1e-21 # r in Angstrom (r^3 -> 10^-30 m), E is GPa (10^9 Pa)
    T = 800 + 273.15
    Na=6.02e23
    return D0*np.exp((-4*np.pi*E*Na*((r0/2)*(r-r0)**2+(1/3)*(r-r0)**3)*scale)/(R*T))

def add_elements(ax):
    # to plot the name of the elements on the diagram
    names = ['La', 'Ce', 'Nd', 'Sm', 'Eu', 'Gd', 'Dy', 'Er', 'Yb', 'Lu', 'Y', 'Sc']
    annotate_xs = np.array([1.172 + 0.01, 1.15 + 0.01, 1.123 + 0.01, 1.098 - 0.031,
                            1.087 - 0.028, 1.078 - 0.04, 1.052 + 0.005, 1.03 + 0.02,
                            1.008 + 0.01, 1.001 - 0.015, 1.04 -0.02, 0.885 - 0.03])
    annotate_ys = np.array([0.468 + 0.1, 1.050 + 0.2, 10.305 + 3, 31.283 - 13,
                            45.634 -17, 74.633- 30, 229.279 + 80, 485.500,
                            583.828 + 200, 460.404 -220, 172.844 -70, 141.630])

    for name, annotate_x, annotate_y in zip(names, annotate_xs, annotate_ys):
        ax.annotate(name, (annotate_x, annotate_y))

Di = np.array([0.468, 1.050, 10.305, 31.283,	45.634,	74.633, 229.279, 485.500,
               583.828, 460.404, 172.844, 141.630])
I_r = np.array([1.172, 1.15, 1.123, 1.098, 1.087, 1.078,
                1.052, 1.03, 1.008, 1.001, 1.04, 0.885])

fig = plt.figure(figsize=(9,5))

# Trust Region Reflective algorithm
ax1 = fig.add_subplot(1,2,1)
ax1.set_title("Trust Region Reflective algorithm")
ax1.scatter(I_r, Di, s=80, color='#c7ddf4', edgecolors='k',
            label='4 GPa - 1073 K, Kessel et al., 2005')

popt1, pcov1 = curve_fit(func, I_r, Di, method='trf', bounds=([0.8,0,0],[1.3,1000,1000]))

x1 = np.linspace(0.85,1.2,1000)
y1 = func(x1,popt1[0],popt1[1], popt1[2])
ax1.plot(x1,y1, color='#ff464a', linewidth=2, linestyle ='--',
         label=r'$r_0$ = ' + '{:.3f}'.format(popt1[0]) + r', $D_0$ = ' +
         '{:.0f}'.format(popt1[1]) + ', E = ' + '{:.0f}'.format(popt1[2]))
add_elements(ax = ax1)
ax1.set_yscale('log')
ax1.set_xlabel(r'Ionic Radius ($\AA$)')
ax1.set_ylabel(r'$D_i$')
ax1.set_ylim(0.005,3000)
ax1.legend()

# Levenberg-Marquardt algorithm
ax2 = fig.add_subplot(1,2,2)
ax2.set_title("Levenberg-Marquardt algorithm")
ax2.scatter(I_r, Di, s=80, color='#c7ddf4', edgecolors='k',
            label='4 GPa - 1073 K, Kessel et al., 2005')

popt2, pcov2 = curve_fit(func, I_r, Di, method='lm', p0=(1.1,100,100))

x2 = np.linspace(0.85,1.2,1000)
y2 = func(x2,popt2[0],popt2[1], popt2[2])
ax2.plot(x2,y2, color='#4881e9', linewidth=2, linestyle ='--',
         label=r'$r_0$ = ' + '{:.3f}'.format(popt2[0]) + r', $D_0$ = ' +
         '{:.0f}'.format(popt2[1]) + ', E = ' + '{:.0f}'.format(popt2[2]))
add_elements(ax = ax2)
ax2.set_yscale('log')
ax2.set_xlabel(r'Ionic Radius ($\AA$)')
ax2.set_ylabel(r'$D_i$')
ax2.set_ylim(0.005,3000)
ax2.legend()

fig.tight_layout()
```

The above picture shows the best fit of the equation describing  crystal-lattice-strain model   using the trust region reflective algorithm (Branch et al., 1999) with bounds for $r_0$, $D_0$, and $E$ and the Levenberg-Marquardt algorithm (Moré, 1978) with an initial guess of  $r_0$, $D_0$, and $E$.

### References

Blundy, J., & Wood, B. (1994). Prediction of crystal-melt partition coefficients fromelastic moduli.Nature,372(6505), 452–454. https://doi.org/10.1038/372452a0

Blundy, J., & Wood, B. (2003). Partitioning of trace elements between crystals andmelts.Earth Planet Sci Lett,210, 383–397

Branch, M. A., Coleman, T. F., & Li, Y. (1999). A Subspace, Interior, and Con-jugate Gradient Method for Large-Scale Bound-Constrained Minimiza-tion Problems.SIAM Journal on Scientific Computing,21(1), 1–23. https://doi.org/10.1137/S1064827595289108

Meltzer, A., & Kessel, R. (2020). Modelling garnet-fluid partitioning in H2O-bearingsystems: a preliminary statistical attempt to extend the crystal lattice-straintheory to hydrous systems.Contributions to Mineralogy and Petrology,175(8), 80. https://doi.org/10.1007/s00410-020-01719-8

Moré, J. J. (1978). The Levenberg-Marquardt algorithm: Implementation and the-ory. In G. Watson (Ed.),Numerical analysis. lecture notes in mathematics(pp. 105–116). Springer, Berlin, Heidelberg. https://doi.org/10.1007/BFb0067700

Seber, G. A. F. & Wild, C. J. (1989). Nonlinear regression. Wiley.

Voglis, C., & Lagaris, I. E. (2004). A Rectangular Trust Region Dogleg Approachfor Unconstrained and Bound Constrained Nonlinear Optimization. In T.Simos & G. Maroulis (Eds.),Wseas international conference on appliedmathematics, corfu, greece(pp. 562–565). Taylor; Francis Inc. https://doi.org/10.1201/9780429081385-138

```python

```


# Poisson-Zero Hidden Markov Model

## Summary

The following exposition uses [`pymc3-hmm`](https://github.com/AmpersandTV/pymc3-hmm) to both simulate and estimate a hidden Markov model (HMM) with emissions consisting of a Poisson random variable and the point at zero.

```python
import numpy as np
import pandas as pd

import theano
import theano.tensor as tt

import pymc3 as pm

from pymc3_hmm.utils import compute_steady_state
from pymc3_hmm.distributions import Constant, SwitchingProcess, DiscreteMarkovChain
from pymc3_hmm.step_methods import FFBSStep, TransMatConjugateStep
```

## Introduction

Our observation model can be described as follows:

\begin{align}
  \label{eq:pois-zero-model}
  Y_t &= S_t \epsilon_t,\quad
  \epsilon_t \sim \operatorname{Pois}\left( \mu \right)
  \\
  S_t &\sim \operatorname{Bern}\left( \pi_t \right)
  \;,
\end{align}
where $y_t \sim Y_t$ are the observed values sampled from the observation distribution, $Y_t$, spanning $t \in \left\{0, \dots, T \right\}$.

The "hidden" state sequence, $\{S_t\}$, is driven by the following Markov relationship:

\begin{equation*}
    \operatorname{P}\left(S_t \mid S_{t-1}\right) \triangleq \Gamma_{t,t-1} \in \mathbb{R}^{2 \times 2}_{[0, 1]}
\end{equation*}

The marginal state probability, $\pi_t$, is then given by

\begin{equation*}
  \begin{aligned}
    \operatorname{P}\left( S_t \right)
    &= \int_{S_{t-1}} \operatorname{P}\left(S_t \mid S_{t-1}\right)
    \operatorname{dP}\left(S_{t-1}\right)
    \\
    &=
    \begin{pmatrix}
      \Gamma^{(0, 0)}_{t,t-1} & \Gamma^{(0, 1)}_{t,t-1}
      \\
      \Gamma^{(1, 0)}_{t,t-1} & \Gamma^{(1, 1)}_{t,t-1}
    \end{pmatrix}^\top
    \begin{pmatrix}
      \pi_{t-1}
      \\
      1 - \pi_{t-1}
    \end{pmatrix}
    \\
    &=
      \begin{pmatrix}
      \pi_{t}
      \\
      1 - \pi_{t}
    \end{pmatrix}
  \;.
  \end{aligned}
\end{equation*}

Our model is effectively a zero-inflated Poisson with a simple first-order Markov process as the mixing distribution.

In the remainder of this exposition, we will assume Dirichlet priors for each row of $\Gamma_{t, t-1}$, the conjugate Gamma prior for $\mu$, and the steady-state value of $\Gamma_{t, t-1}$ for $\pi_0$.

## Simulation

```python
def create_poisson_zero_hmm(mu, observed, p_0_a=np.r_[5, 1], p_1_a=np.r_[3, 5]):

    p_0_rv = pm.Dirichlet("p_0", p_0_a)
    p_1_rv = pm.Dirichlet("p_1", p_1_a)

    P_tt = tt.stack([p_0_rv, p_1_rv])
    P_rv = pm.Deterministic("P_t", tt.shape_padleft(P_tt))

    pi_0_tt = compute_steady_state(P_rv)

    S_rv = DiscreteMarkovChain("S_t", P_rv, pi_0_tt, shape=np.shape(observed)[-1])
    S_rv.tag.test_value = np.array(observed > 0, dtype=np.int32)

    Y_rv = SwitchingProcess(
        "Y_t", [Constant.dist(np.array(0, dtype=np.int64)), pm.Poisson.dist(mu)], S_rv, observed=observed
    )

    return Y_rv

```

```python
np.random.seed(2032)

mu_true = 5000

with pm.Model() as sim_model:
    _ = create_poisson_zero_hmm(mu_true, np.zeros(10000))

sim_point = pm.sample_prior_predictive(samples=1, model=sim_model)
sim_point["Y_t"] = sim_point["Y_t"].squeeze()

y_t = sim_point["Y_t"]
```

## Estimation

We will use the "true" data-generating observation model to estimate the parameters $\mu$ and $\Gamma_{t, t-1}$ (the latter as rows denoted by `p_0` and `p_1`).  For demonstration purposes, we choose hyper-parameters for the $\mu$ prior that are "far" from the true $\mu$ value.

The sampling steps for $S_t$ are performed using forward-filtering backward-sampling (FFBS).  The steps for $\Gamma_{t, t-1}$ are conjugate samples conditional on the sampled state sequence $\{S_t\}$ provided by FFBS.  Both of these step methods are available in `pymc3-hmm`.

While $\mu$ could also be sampled exactly using a conjugate step—conditional on $\{S_t\}$—we instead sample this parameter using PyMC3's built-in NUTS.

```python
with pm.Model() as test_model:
    E_mu, Var_mu = 1000.0, 10000.0
    mu_rv = pm.Gamma("mu", E_mu ** 2 / Var_mu, E_mu / Var_mu)

    _ = create_poisson_zero_hmm(mu_rv, y_t, p_0_a=np.r_[1, 1], p_1_a=np.r_[1, 1])

with test_model:
    transmat_step = TransMatConjugateStep(test_model.P_t)
    states_step = FFBSStep([test_model.S_t])
    mu_step = pm.NUTS([test_model.mu])

    posterior_trace = pm.sample(
        step=[transmat_step, states_step, mu_step],
        return_inferencedata=True,
        chains=1,
        progressbar=True,
    )
```

    Sequential sampling (1 chains in 1 job)
    CompoundStep
    >TransMatConjugateStep: [p_0, p_1]
    >FFBSStep: [S_t]
    >NUTS: [mu]

  <progress value='2000' class='' max='2000' style='width:300px; height:20px; vertical-align: middle;'></progress>
  100.00% [2000/2000 08:53<00:00 Sampling chain 0, 0 divergences]

    Sampling 1 chain for 1_000 tune and 1_000 draw iterations (1_000 + 1_000 draws total) took 534 seconds.
    Only one chain was sampled, this makes it impossible to run some convergence checks

## Posterior Samples

```python
import matplotlib.pyplot as plt

import arviz as az

ax = az.plot_trace(
    posterior_trace.posterior,
    var_names=[
        "mu",
        "p_0",
        "p_1",
    ],
    lines=[
        ("mu", {}, [mu_true]),
        ("p_0", {}, [sim_point["p_0"]]),
        ("p_1", {}, [sim_point["p_1"]]),
    ],
    compact=True,
    figsize=(15, 15),
)

plt.tight_layout()
```

## Posterior Predictive Samples

```python
with test_model:
    posterior_pred_trace = pm.sample_posterior_predictive(
        posterior_trace.posterior, var_names=["S_t", "Y_t"]
    )
```

  <progress value='1000' class='' max='1000' style='width:300px; height:20px; vertical-align: middle;'></progress>
  100.00% [1000/1000 00:44<00:00]

```python
from matplotlib.ticker import MaxNLocator

from pymc3_hmm.utils import plot_split_timeseries_histograms

columns = pd.MultiIndex.from_tuples(
    [("Y_t", i) for i in range(posterior_pred_trace["Y_t"].shape[0])],
    names=("var", "sample"),
)

plot_data = pd.DataFrame(
    posterior_pred_trace["Y_t"].T,
    index=pd.date_range(
        start="1/1/2020", periods=posterior_pred_trace["Y_t"].shape[1], freq="H"
    ),
    columns=columns,
)
plot_data["E[S_t]"] = posterior_pred_trace["S_t"].mean(0)
plot_data["E[Y_t]"] = posterior_pred_trace["Y_t"].mean(0)
plot_data["y_t"] = y_t
```

```python
def plot_fn(ax, data, **kwargs):
    ax.plot(
        data["y_t"],
        label="y_t",
        alpha=0.7,
        color="red",
        linewidth=0.8,
        drawstyle="steps",
    )

    ax.plot(
        data["E[Y_t]"],
        label="E[Y_t]",
        alpha=0.3,
        color="blue",
        linewidth=0.8,
        drawstyle="steps",
    )

axes_split_data = plot_split_timeseries_histograms(
    plot_data,
    sample_col="Y_t",
    plot_fn=plot_fn,
    split_max=10,
    figsize=(15, 30),
    twin_column_name="E[S_t]",
    twin_plot_kwargs={
        "color": "green",
        "drawstyle": "steps",
        "linestyle": "--",
        "alpha": 0.4,
    },
)

for (_, twin_ax), _ in axes_split_data:
    _ = twin_ax.set_ylabel(
        "E[S_t]", color=twin_ax.get_lines()[0].get_color()
    )
    _ = twin_ax.yaxis.set_major_locator(MaxNLocator(integer=True))

plt.tight_layout()
```

## Out-of-sample Posterior Predictives

```python
with test_model, theano.config.change_flags(compute_test_value="off"):

    test_model.S_t.distribution.shape = (24 * 7,)

    out_of_sample_pp = pm.sample_posterior_predictive(
        posterior_trace.posterior.drop_vars("S_t"),
        var_names=["S_t", "Y_t"],
        model=test_model,
    )
```

  <progress value='1000' class='' max='1000' style='width:300px; height:20px; vertical-align: middle;'></progress>
  100.00% [1000/1000 00:57<00:00]

```python
from pymc3_hmm.utils import plot_timeseries_histograms

columns = pd.MultiIndex.from_tuples(
    [("Y_t", i) for i in range(out_of_sample_pp["Y_t"].shape[0])],
    names=("var", "sample"),
)

out_of_sample_data = pd.DataFrame(
    out_of_sample_pp["Y_t"].T,
    index=pd.date_range(
        start="1/1/2021", periods=out_of_sample_pp["Y_t"].shape[1], freq="H"
    ),
    columns=columns
)
out_of_sample_data[("mean", 0)] = out_of_sample_pp["Y_t"].mean(0)

ax = plot_timeseries_histograms(axes=None, data=out_of_sample_data["Y_t"])
```


# linear algebra

Intially this was a type along from MXNet UC Bekeley Course on the Apache MXNet youtube channel

I've added to it examples from the mxnet docs for mxnet.ndarray and mxnet.contrib

[docs](https://mxnet.incubator.apache.org/versions/1.8.0/api/python/docs/api/ndarray/ndarray.html#)

```python
from mxnet import nd
import mxnet as mx
print ('mx version ', mx.__version__)
```

    mx version  1.9.0

In MXNet scalars are NDArrays with one element

```python
x = nd.array([3.0])
y = nd.array([2.0])

print('x + y = ', x + y)
print('x - y = ', x - y)
print('x * y = ', x * y)
print('x / y = ', x / y)
print('x ** y = ', x ** y)
print('x ** y = ', nd.power(x,y))
```

    x + y =
    [5.]
    <NDArray 1 @cpu(0)>
    x - y =
    [1.]
    <NDArray 1 @cpu(0)>
    x * y =
    [6.]
    <NDArray 1 @cpu(0)>
    x / y =
    [1.5]
    <NDArray 1 @cpu(0)>
    x ** y =
    [9.]
    <NDArray 1 @cpu(0)>
    x ** y =
    [9.]
    <NDArray 1 @cpu(0)>

We can convert any NDArray to a python float by calling its asscalar method.  NOTE: this is a bad idea.  The NDArray has to stop doing anything else in order to hand the result and process control back to Python.  Its not done in parallel.

```python
print(x)
print(x.asscalar())
```

    [3.]
    <NDArray 1 @cpu(0)>
    3.0

# Vectors
Vectors are [1, 3, 4, 2]  as a 1D NDArray

```python
x = nd.arange(5)
print('x = ', x)
```

    x =
    [0. 1. 2. 3. 4.]
    <NDArray 5 @cpu(0)>

```python
x[3]
```

    [3.]
    <NDArray 1 @cpu(0)>

# Length, dimensionality and shape
The length of a vector is commonly called its *dimension*.  As an ordianry Python array, we can acces the length of an NDArray by calling Python's built-in len() function.

We can also access a vectors length via its `.shape` attribute.  The shape is a tuple that lists it dimensionality along each of its axes.

```python
x.shape
```

    (5,)

The word dimension is overloaded between number of axes and number of elements.  To avoid confusion, when we say 2D array or 3D array, we mean an array with 2 or 3 axes respectively.  But, if we say `n-dimensional` vector, we mean a vector of length `n`.

```python
a = 2
x = nd.array([1,2,3])
y = nd.array([10,20,30])
print('a * x = ', a * x)
print('a * x + y = ', a * x + y)
```

    a * x =
    [2. 4. 6.]
    <NDArray 3 @cpu(0)>
    a * x + y =
    [12. 24. 36.]
    <NDArray 3 @cpu(0)>

# Matrices

Just as vectors generalize scalars from order 0 to order 1, matrices generalize vectors from 1D to 2D.  Matrices, which we'll typically denote with capital letters (A,B,C), are represented in codes as arrays with 2 axes.  Visually, we can draw a matrix as a table, where each entry $a_{ij}$ belongs to the i-th row and j-th column.

\begin{pmatrix}
a_{11} & a_{12} & a_{1m}\\
a_{21} & a_{22} & a_{2m}\\
a_{..} & a_{..} & a_{..}\\
a_{n1} & a_{n2} & a_{nm}
\end{pmatrix}

```python
print(nd.arange(20))
A = nd.arange(20).reshape((2,10)) # NOTE, rows, columns
print('A = ', A)
```

    [ 0.  1.  2.  3.  4.  5.  6.  7.  8.  9. 10. 11. 12. 13. 14. 15. 16. 17.
     18. 19.]
    <NDArray 20 @cpu(0)>
    A =
    [[ 0.  1.  2.  3.  4.  5.  6.  7.  8.  9.]
     [10. 11. 12. 13. 14. 15. 16. 17. 18. 19.]]
    <NDArray 2x10 @cpu(0)>

We can access elements $a_{ij}$ by specifying row *i* and column *j*.  Leaving them blank, selects all?

Selecting via `:` also takes all elements in the respective dimension.

We can transpose the matrix with the `T` method.  That is, if $B=A^{T}$ then $b_{ij}=a_{ji}\,\, \forall{i,j}$

```python
print('A[1] = ', A[1])
```

    A[1] =
    [10. 11. 12. 13. 14. 15. 16. 17. 18. 19.]
    <NDArray 10 @cpu(0)>

```python
print('A[1,:] = ', A[1,:])
```

    A[1,:] =
    [10. 11. 12. 13. 14. 15. 16. 17. 18. 19.]
    <NDArray 10 @cpu(0)>

```python
print('A.T = ', A.T)
```

    A.T =
    [[ 0. 10.]
     [ 1. 11.]
     [ 2. 12.]
     [ 3. 13.]
     [ 4. 14.]
     [ 5. 15.]
     [ 6. 16.]
     [ 7. 17.]
     [ 8. 18.]
     [ 9. 19.]]
    <NDArray 10x2 @cpu(0)>

## Subset a matrix

slice url

https://mxnet.apache.org/versions/master/api/python/docs/api/legacy/ndarray/ndarray.html?highlight=mxnet%20ndarray%20arange#mxnet.ndarray.slice

```python
four_by_four = nd.arange(16).reshape(4,4)
print('four_by_four = ', four_by_four)
```

    four_by_four =
    [[ 0.  1.  2.  3.]
     [ 4.  5.  6.  7.]
     [ 8.  9. 10. 11.]
     [12. 13. 14. 15.]]
    <NDArray 4x4 @cpu(0)>

```python
# when you use the :, its all rows.  But if numbers are on either side its a range, where the end is exclusive.
two_by_two = four_by_four[0:2,1:3]
print('two_by_two = ', two_by_two)
```

    two_by_two =
    [[1. 2.]
     [5. 6.]]
    <NDArray 2x2 @cpu(0)>

```python
# you can create a sliced copied
another2x2 = four_by_four.slice(begin=(1,1), end=(3,3))
print('another2x2 = ', another2x2)
```

    another2x2 =
    [[ 5.  6.]
     [ 9. 10.]]
    <NDArray 2x2 @cpu(0)>

## indexing

```
[start:stop inclusive : step]
```

```python
x = nd.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], dtype='int32')
x
```

    [0 1 2 3 4 5 6 7 8 9]
    <NDArray 10 @cpu(0)>

```python
# Select elements 1 through 7, and use a step of 2
x[1:7:2]
```

    [1 3 5]
    <NDArray 3 @cpu(0)>

# negative indexing

Negative i and j are interpreted as n + i and n + j where n is the number of elements in the corresponding dimension.

Negative k makes stepping go towards smaller indices.

```python
x[-2:10]
```

    [8 9]
    <NDArray 2 @cpu(0)>

```python
x[10:6:-1]
```

    [9 8 7]
    <NDArray 3 @cpu(0)>

Anothe example using a matrix and nd arrays to assit with indexing

```python
a = nd.arange(50).reshape(10, 5) # Array to be indexed
# b = 9 7 5 3 1  which means start with row 9 and step down to
b = nd.arange(9, -1, -2) # Indexing array. Start at 9, step down by 2 each time until 1
print('a = ', a)
print('b (indexing) = ', b)
print('nd.arange(a.shape[1]) = ', nd.arange(a.shape[1]))
result = a[b, nd.arange(a.shape[1])]
print('result = ', result)
```

    a =
    [[ 0.  1.  2.  3.  4.]
     [ 5.  6.  7.  8.  9.]
     [10. 11. 12. 13. 14.]
     [15. 16. 17. 18. 19.]
     [20. 21. 22. 23. 24.]
     [25. 26. 27. 28. 29.]
     [30. 31. 32. 33. 34.]
     [35. 36. 37. 38. 39.]
     [40. 41. 42. 43. 44.]
     [45. 46. 47. 48. 49.]]
    <NDArray 10x5 @cpu(0)>
    b (indexing) =
    [9. 7. 5. 3. 1.]
    <NDArray 5 @cpu(0)>
    nd.arange(a.shape[1]) =
    [0. 1. 2. 3. 4.]
    <NDArray 5 @cpu(0)>
    result =
    [45. 36. 27. 18.  9.]
    <NDArray 5 @cpu(0)>

# Expand dimensions

[here](https://mxnet.incubator.apache.org/versions/1.8.0/api/python/docs/api/ndarray/ndarray.html#mxnet.ndarray.expand_dims)

Prototype

```
mxnet.ndarray.expand_dims(data=None, axis=_Null, out=None, name=None, **kwargs)
```

Example

```
Inserts a new axis of size 1 into the array shape For example, given x with shape (2,3,4), then expand_dims(x, axis=1) will return a new array with shape (2,1,3,4).
```

```python
X = nd.array([[0,1],[2,3]])
print(X)
X = X.reshape(1,2,2)
print(X)
X = X.expand_dims(axis=0)
print(X)
```

    [[0. 1.]
     [2. 3.]]
    <NDArray 2x2 @cpu(0)>

    [[[0. 1.]
      [2. 3.]]]
    <NDArray 1x2x2 @cpu(0)>

    [[[[0. 1.]
       [2. 3.]]]]
    <NDArray 1x1x2x2 @cpu(0)>

# shape_array

[here](https://mxnet.incubator.apache.org/versions/1.8.0/api/python/docs/api/ndarray/ndarray.html#mxnet.ndarray.shape_array)

Returns a 1D int64 array containing the shape of data

prototype

```
nd.shape_array(data=None, out=None, name=None, **kwargs)
```

Example
```
shape_array([[1,2,3,4], [5,6,7,8]]) = [2,4]
```

```python
X = nd.array([[0,1],[2,3]])
print(X)
X = X.reshape(1,2,2)
print(X)
X = X.expand_dims(axis=0)
print(X)
X.shape_array()
```

    [[0. 1.]
     [2. 3.]]
    <NDArray 2x2 @cpu(0)>

    [[[0. 1.]
      [2. 3.]]]
    <NDArray 1x2x2 @cpu(0)>

    [[[[0. 1.]
       [2. 3.]]]]
    <NDArray 1x1x2x2 @cpu(0)>

    [1 1 2 2]
    <NDArray 4 @cpu(0)>

# Flatten an Array

```python
# takes a 3,4,.. array and converts to a 2D
X = nd.array([[0,1],[2,3]])
print(X)
X = X.reshape(1,2,2)
print(X)
print('X.flatten()',X.flatten())

Y = nd.array([[0,1],[2,3]])
print(Y)
print('Y.flatten()',Y.flatten())

```

    [[0. 1.]
     [2. 3.]]
    <NDArray 2x2 @cpu(0)>

    [[[0. 1.]
      [2. 3.]]]
    <NDArray 1x2x2 @cpu(0)>
    X.flatten()
    [[0. 1. 2. 3.]]
    <NDArray 1x4 @cpu(0)>

    [[0. 1.]
     [2. 3.]]
    <NDArray 2x2 @cpu(0)>
    Y.flatten()
    [[0. 1.]
     [2. 3.]]
    <NDArray 2x2 @cpu(0)>

# Choosing elements based on index

[docs](https://mxnet.apache.org/versions/1.8.0/api/python/docs/api/ndarray/ndarray.html#mxnet.ndarray.choose_element_0index)

Picks elements from an input array according to the input indices along the given axis.

prototype
```
ndarray.choose_element_0index(data=None, index=None, axis=_Null, keepdims=_Null, mode=_Null, out=None, name=None, **kwargs)
```

```python
X = nd.array([[0,1,2],[3,4,5],[6,7,8]])
print('X.shape ', X.shape)
print(X)
# these are equivalnet
#Y = nd.array([2,2,3]) # it seems if you specify a column > max column, it still picks the last column
Y = nd.array([2,2,2])

```

    X.shape  (3, 3)

    [[0. 1. 2.]
     [3. 4. 5.]
     [6. 7. 8.]]
    <NDArray 3x3 @cpu(0)>

```python
# these one makes sense. axis 1 is column
# for each row, pick colum specified by index, col=2, col=2, col=3, except no col=3? is it just the last one?
print(nd.choose_element_0index(X, Y)) # if axis is not specified, axis=1
print(nd.choose_element_0index(X, Y, axis=1))
```

    [2. 5. 8.]
    <NDArray 3 @cpu(0)>

    [2. 5. 8.]
    <NDArray 3 @cpu(0)>

```python
# For each column, pick row 2,2,2
print(nd.choose_element_0index(X, Y, axis=0))
# no axis=2
#print(nd.choose_element_0index(X, Y, axis=2))

```

    [6. 7. 8.]
    <NDArray 3 @cpu(0)>

```python
X = nd.array([[0,1,2],[3,4,5],[6,7,8]])
print('X.shape ', X.shape)
print(X)
```

    X.shape  (3, 3)

    [[0. 1. 2.]
     [3. 4. 5.]
     [6. 7. 8.]]
    <NDArray 3x3 @cpu(0)>

```python
Y = nd.array([[1],[0],[2]])
print('Y.shape ', Y.shape)
print(Y)
```

    Y.shape  (3, 1)

    [[1.]
     [0.]
     [2.]]
    <NDArray 3x1 @cpu(0)>

```python
# for each row, pick col=1,col=0, col=2 and maintain the rows
nd.choose_element_0index(X, Y, axis=1, keepdims=True)
```

    [[1.]
     [3.]
     [8.]]
    <NDArray 3x1 @cpu(0)>

# pick is equivalent

[docs](https://mxnet.apache.org/versions/1.8.0/api/python/docs/api/ndarray/ndarray.html#mxnet.ndarray.pick)

```python
X = nd.array([[0,1,2],[3,4,5],[6,7,8]])
print('X.shape ', X.shape)
print(X)
```

    X.shape  (3, 3)

    [[0. 1. 2.]
     [3. 4. 5.]
     [6. 7. 8.]]
    <NDArray 3x3 @cpu(0)>

```python
Y = nd.array([[1],[0],[2]])
print('Y.shape ', Y.shape)
print(Y)
```

    Y.shape  (3, 1)

    [[1.]
     [0.]
     [2.]]
    <NDArray 3x1 @cpu(0)>

```python
# picks elements with specified indices along axis 1 and dims are maintained
nd.pick(X, Y, axis=1, keepdims=True)
```

    [[1.]
     [3.]
     [8.]]
    <NDArray 3x1 @cpu(0)>

```python

```

```python

```

```python

```

```python

```

```python

```

# Tensors
Just as vectors genralize scalars, and matrices generalize vectors, we can increase the number of axes.  When working with images the axes correspond to the height, width and the three RGB color channels.

```python
X = nd.arange(24).reshape((2,3,4))  # two submaxtrices of three rows by four columns
print('X = ', X)
print('X.shape = ', X.shape)

```

    X =
    [[[ 0.  1.  2.  3.]
      [ 4.  5.  6.  7.]
      [ 8.  9. 10. 11.]]

     [[12. 13. 14. 15.]
      [16. 17. 18. 19.]
      [20. 21. 22. 23.]]]
    <NDArray 2x3x4 @cpu(0)>
    X.shape =  (2, 3, 4)

# Basic properties of tensor artihmetic

Given two tensors `X` and `Y` with the same shape, $\alpha X + Y$ has the same shape (numerical mathematicians call this the `AXPY` operation)

```python
a = 2
x = nd.ones(3)
y = nd.zeros(3)
print('x.shape = ', x.shape)
print('y.shape = ', y.shape)
print('(a * x).shape = ', (a * x).shape)
print('(a * x + y).shape = ', (a * x + y).shape)
```

    x.shape =  (3,)
    y.shape =  (3,)
    (a * x).shape =  (3,)
    (a * x + y).shape =  (3,)

# sums and means
In math we express sums using the $\sum$ symbol.  To express the sum of elements in a vector `u` of length `d`, we can write $\sum_{i=i}^{i=d}{u_{i}}$   In code, we can just call `nd.sum()`

```python
x = nd.arange(12)
x = x.reshape((3,4))
print('x = ', x)
print('nd.sum(x) = ', nd.sum(x))
```

    x =
    [[ 0.  1.  2.  3.]
     [ 4.  5.  6.  7.]
     [ 8.  9. 10. 11.]]
    <NDArray 3x4 @cpu(0)>
    nd.sum(x) =
    [66.]
    <NDArray 1 @cpu(0)>

Sum along a row

```python
print('nd.sum(x,0) = ', nd.sum(x,0))
# or
print('nd.sum(x,0) = ', nd.sum(x,axis=0))

```

    nd.sum(x,0) =
    [12. 15. 18. 21.]
    <NDArray 4 @cpu(0)>
    nd.sum(x,0) =
    [12. 15. 18. 21.]
    <NDArray 4 @cpu(0)>

sum along a column

```python
print('nd.sum(x,0) = ', nd.sum(x,axis=1))
# or
print('nd.sum(x,0) = ', nd.sum(x,1))

```

    nd.sum(x,0) =
    [ 6. 22. 38.]
    <NDArray 3 @cpu(0)>
    nd.sum(x,0) =
    [ 6. 22. 38.]
    <NDArray 3 @cpu(0)>

sum along a row and column

```python
print('nd.sum(x,0) = ', nd.sum(x,(0,1)))

```

    nd.sum(x,0) =
    [66.]
    <NDArray 1 @cpu(0)>

# Mean
A related quantity is the mean.  We calculate the mean by dividing the sum by the total number of elements.  In code this is `nd.mean()`

$$mean(u) = \frac{1}{d} \sum_{i=1}^{i=d} u_{i} $$

and

$$mean(A) = \frac{1}{n*m} \sum_{i=1}^{i=m} \sum_{j=1}^{j=n} a_{ij} $$

```python
print('A = ', A)
print('nd.mean(A) = ', nd.mean(A))
print('nd.sum(A) / A.size = ', nd.sum(A) / A.size)
```

    A =
    [[ 0.  1.  2.  3.  4.  5.  6.  7.  8.  9.]
     [10. 11. 12. 13. 14. 15. 16. 17. 18. 19.]]
    <NDArray 2x10 @cpu(0)>
    nd.mean(A) =
    [9.5]
    <NDArray 1 @cpu(0)>
    nd.sum(A) / A.size =
    [9.5]
    <NDArray 1 @cpu(0)>

# Dot products

Given two vectors `u` and `v`, the dot product $u^Tv$ is a sum over the products of the corresponding elements: $$u^Tv = \sum_{i=1}^{i=d} u_i * v_i$$

```python
x = nd.arange(4) + 1.0
y = nd.ones(4)
print('x = ', x)
print('y = ', y)
print('nd.dot(x,y) = ', nd.dot(x,y))
# also equivalent
print('(x * y).sum() = ', (x * y).sum())
```

    x =
    [1. 2. 3. 4.]
    <NDArray 4 @cpu(0)>
    y =
    [1. 1. 1. 1.]
    <NDArray 4 @cpu(0)>
    nd.dot(x,y) =
    [10.]
    <NDArray 1 @cpu(0)>
    (x * y).sum() =
    [10.]
    <NDArray 1 @cpu(0)>

Note that we can express the dot product of two vectors `nd.dot(u,v)` equivalently by performing an element-wise multiplicatoins and then a sum:

```python
nd.sum(x * y)
```

    [10.]
    <NDArray 1 @cpu(0)>

# Matrix-vector products

\begin{align}
\begin{vmatrix}
a_{11} & a_{12} \\
a_{21} & a_{22}
\end{vmatrix}
\begin{vmatrix} x \\ y \end{vmatrix} =
\begin{vmatrix}
a_1^T x \\
a_2^T y
\end{vmatrix} =
\begin{vmatrix}
x a_{11} + y a_{12} \\
x a_{12} + y a_{22}
\end{vmatrix}
\end{align}

$\in$ is member of

$\newcommand{\R}{\mathbb{R}} \R$ is real number. all numbers negative and positive including fractions

So you can think of multiplication by a matrix $\newcommand{\R}{\mathbb{R}} A\in\R^{mxn}$ as trans that projects vectors from $\R^m to \R^n$

We can also use matrix-vector products to describe the calculations of each layer in a neural network.  Expressing matrix-vector products in code with `ndarray`, we use the same `nd.dot()` function as for dot products.

```python
print('A = ', A) # a 2x10
print('x = ', x) # a 1x4

# He did this.  It does not work for me.  I look in the online help text
# and nothing is mentioned about truncating rows. ie. if 4 columns it needs 5 rows
# and not three as in his text.  (Another reason to look for the git repo rather than typing along!)
#A = A.reshape((3,4))

# later he refers to this as 3x4.  He must have had a 2x6?

# My choice
A = A.reshape((5,4))
print('A now is = ', A)

# * is an element by element multiply. it mults X by each row.
# first row is 1*0  2*1 3*2 4*3
# second row is 1*4 2*5 3*6 4*7 etc
print('A*x = ', A*x)

# dot is a row sum. Notice its a sum across a row
print('nd.dot(A,x) = ', nd.dot(A,x))

#
# He mentioned matlab .* notation is equivalent to nd *

# He also mentions broadcast of repeat entries of X until you get all elements of A.

```

    A =
    [[ 0.  1.  2.  3.  4.  5.  6.  7.  8.  9.]
     [10. 11. 12. 13. 14. 15. 16. 17. 18. 19.]]
    <NDArray 2x10 @cpu(0)>
    x =
    [1. 2. 3. 4.]
    <NDArray 4 @cpu(0)>
    A now is =
    [[ 0.  1.  2.  3.]
     [ 4.  5.  6.  7.]
     [ 8.  9. 10. 11.]
     [12. 13. 14. 15.]
     [16. 17. 18. 19.]]
    <NDArray 5x4 @cpu(0)>
    A*x =
    [[ 0.  2.  6. 12.]
     [ 4. 10. 18. 28.]
     [ 8. 18. 30. 44.]
     [12. 26. 42. 60.]
     [16. 34. 54. 76.]]
    <NDArray 5x4 @cpu(0)>
    nd.dot(A,x) =
    [ 20.  60. 100. 140. 180.]
    <NDArray 5 @cpu(0)>

# Matrix-Matrix multiplication

If you have gotten the hang of dot products and matrix-vector multiplication, then matrix-matrix multiplications should be pretty straight forward.

Say we have two matrices, $\newcommand{\R}{\mathbb{R}} A\in\R^{nxk}\, and\, B\in\R^{kxm}$

\begin{align}
A=\begin{vmatrix}
a_{11} & a_{12} \\
a_{21} & a_{nk}
\end{vmatrix}
B=\begin{vmatrix}
b_{11} & b_{12} \\
b_{21} & b_{km}
\end{vmatrix}
\end{align}

You can think of the matrix-matrix multiplication $AB$ as simply performing `m` matrix-vector products and stiching the results together.

\begin{align}
AB=\begin{vmatrix}
\cdots & a_1^T & \cdots \\
\cdots & a_2^T & \cdots \\
\cdots & a_n^T & \cdots
\end{vmatrix}
\begin{vmatrix}
\cdots & \cdots & \cdots \\
b_{1} & b_{2} & b_{m} \\
\cdots & \cdots & \cdots
\end{vmatrix}
=\begin{vmatrix}
a_1^Tb_1 & a_1^Tb_2 & .. & a_1^Tb_m \\
a_2^Tb_1 & a_2^Tb_2 & .. & a_1^Tb_m \\
a_n^Tb_1 & a_n^Tb_2 & .. & a_n^Tb_m
\end{vmatrix}
\end{align}

```python
A=nd.arange(4).reshape((2,2))
print('A = ', A)
B=nd.arange(1, stop=5, step=1).reshape((2,2))
print('B = ', B)

# This is for properly size matrices
nd.dot(A,B)

# If the dims are not correct, I've seen the transpose operator used
# nd.dot(A,B.T)
```

    A =
    [[0. 1.]
     [2. 3.]]
    <NDArray 2x2 @cpu(0)>
    B =
    [[1. 2.]
     [3. 4.]]
    <NDArray 2x2 @cpu(0)>

    [[ 3.  4.]
     [11. 16.]]
    <NDArray 2x2 @cpu(0)>

### Multiplication element wise versus normal matrix multiply

### element-wise multiplication

this is matlab .*? or is it .*.? Can't remember. need to test

```python
L=nd.array(nd.array([[1,2],[3,4]]))
#L=nd.array(nd.array([[1,1]]))
L.shape
```

    (2, 2)

```python
R=nd.array(nd.array([[0,-1],[1,0]]))
R
```

    [[ 0. -1.]
     [ 1.  0.]]
    <NDArray 2x2 @cpu(0)>

```python
L*R
```

    [[ 0. -2.]
     [ 3.  0.]]
    <NDArray 2x2 @cpu(0)>

### Normal multiplication

```python
nd.dot(L,R)
```

    [[ 2. -1.]
     [ 4. -3.]]
    <NDArray 2x2 @cpu(0)>

# Norms

All norms must satisfy a handful of properties:

* $  \left\Vert  \alpha A \right\Vert = \begin{vmatrix} \alpha \end{vmatrix} \left\Vert A \right\Vert$
* $  \left\Vert A + B \right\Vert \leq \left\Vert A \right\Vert + \left\Vert B \right\Vert$
* $  \left\Vert A  \right\Vert \geq 0$
* $  \text{If }\forall\,i,j,a_{ij} = 0, then \left\Vert A  \right\Vert = 0$

To calculate the $L_{2}$ norm, we can just call `nd.norm()`

```python
nd.norm(x)
```

    [5.477226]
    <NDArray 1 @cpu(0)>

To calculate the $L_1$ norm we can simply perform the absolute value and then sum over elements.

https://mxnet.apache.org/versions/1.6/api/r/docs/api/mx.symbol.sum.html

```python
print("L1 norm = ", nd.sum(nd.sum(x)))
# or
print("L1 norm = ", x.abs().sum())

```

    L1 norm =
    [10.]
    <NDArray 1 @cpu(0)>
    L1 norm =
    [10.]
    <NDArray 1 @cpu(0)>

# SUM

Computes the sum of array elements over given axes.

[docs](https://mxnet.apache.org/versions/1.8.0/api/python/docs/api/ndarray/ndarray.html#mxnet.ndarray.sum)

prototype

```
sum(data=None, axis=_Null, keepdims=_Null, exclude=_Null, out=None, name=None, **kwargs)
```

```python
X = nd.array([[0,1,2],[3,4,5],[6,7,8]])
print('X.shape ', X.shape)
print(X)
```

    X.shape  (3, 3)

    [[0. 1. 2.]
     [3. 4. 5.]
     [6. 7. 8.]]
    <NDArray 3x3 @cpu(0)>

```python
X.sum(axis=0)
```

    [ 9. 12. 15.]
    <NDArray 3 @cpu(0)>

```python
X.sum(axis=1, keepdims=True)
```

    [[ 3.]
     [12.]
     [21.]]
    <NDArray 3x1 @cpu(0)>

# CUMSUM

This is interesting.  It sums along the axis.  For output with axis=0/row,

* first row is just the first row.
* second row is sum of first and second row.
* third ....

[docs](https://mxnet.apache.org/versions/1.8.0/api/python/docs/api/ndarray/ndarray.html#mxnet.ndarray.cumsum)

prototype

```
ndarray.cumsum(a=None, axis=_Null, dtype=_Null, out=None, name=None, **kwargs)
```

```python
X = nd.array([[1,2,0],[3,4,0],[5,6,0]])
print('X.shape ', X.shape)
print(X)
```

    X.shape  (3, 3)

    [[1. 2. 0.]
     [3. 4. 0.]
     [5. 6. 0.]]
    <NDArray 3x3 @cpu(0)>

```python
nd.cumsum(X, axis=0)
```

    [[ 1.  2.  0.]
     [ 4.  6.  0.]
     [ 9. 12.  0.]]
    <NDArray 3x3 @cpu(0)>

# boolean_mask

[docs](https://mxnet.apache.org/versions/1.8.0/api/python/docs/api/contrib/ndarray/index.html#mxnet.contrib.ndarray.boolean_mask)

prototype

```
mxnet.contrib.ndarray.boolean_mask(data=None, index=None, axis=_Null, out=None, name=None, **kwargs)
```

```python
data = mx.nd.array([[1, 2, 3],[4, 5, 6],[7, 8, 9]])
index = mx.nd.array([0, 1, 0])
out = mx.nd.contrib.boolean_mask(data, index)
out
```

    [[4. 5. 6.]]
    <NDArray 1x3 @cpu(0)>

# non_zero

[docs](https://mxnet.apache.org/versions/1.8.0/api/python/docs/api/contrib/ndarray/index.html#mxnet.contrib.ndarray.getnnz)

```
mxnet.contrib.ndarray.getnnz(data=None, axis=_Null, out=None, name=None, **kwargs)
```

```python
# can not do this on non-csr arrays on CPU
#mx.contrib.ndarray.getnnz(X)
```

# where
Return the elements, either from x or y, depending on the condition.

[docs](https://mxnet.apache.org/versions/1.8.0/api/python/docs/api/ndarray/ndarray.html#mxnet.ndarray.where)

prototype

```
mxnet.ndarray.where(condition=None, x=None, y=None, out=None, name=None, **kwargs)
```

#### from api example

```python
# official example 1
#  [[1, 2]
#   [3, 4]]

# [[5, 6]
#  [7, 8]]
#

# [[ 0, 1]
#  [-1, 0 ]

# 0 pulls Y in same position
# 1 pulls X  "True pull x"
# -1 pulls X "-1 is non zero so true, pull x"
# 0 pulls Y  "0 is false pull y"
x = nd.array([[1, 2], [3, 4]])
y = nd.array([[5, 6], [7, 8]])
cond = nd.array([[0, 1], [-1, 0]])
# should return  = [[5, 2], [3, 8]]
nd.where(cond, x, y)
```

    [[5. 2.]
     [3. 8.]]
    <NDArray 2x2 @cpu(0)>

```python
# official example 2
csr_cond = nd.cast_storage(cond, 'csr')
# should return  = [[5, 2], [3, 8]]
nd.where(csr_cond, x, y)
```

    [[5. 2.]
     [3. 8.]]
    <NDArray 2x2 @cpu(0)>

```python
X = nd.array([[1,2,0],[3,4,0],[6,7,0]])
print('X.shape ', X.shape)
print(X)
```

    X.shape  (3, 3)

    [[1. 2. 0.]
     [3. 4. 0.]
     [6. 7. 0.]]
    <NDArray 3x3 @cpu(0)>

```python
Y = nd.array([1, 0, 0])
print('Y.shape ', Y.shape)
print(Y)
```

    Y.shape  (3,)

    [1. 0. 0.]
    <NDArray 3 @cpu(0)>

```python
nd.where(condition=Y, x=X, y=X)
```

    [[1. 2. 0.]
     [3. 4. 0.]
     [6. 7. 0.]]
    <NDArray 3x3 @cpu(0)>

### From Thomas Delteil

[here](https://github.com/apache/incubator-mxnet/issues/8546#issuecomment-402563089)

```python
# original sample
labels = nd.array([-1, 0, 1, 0, 1])
cls_scores = nd.array([0.1, 0.2, 0.3, 0.4, 0.5])
#cls_scores = nd.where(labels != -1, cls_scores, labels)
# Kind of merges x and y tensors, pulling y, when condition is not met. pulling x when met.
cls_scores = nd.where(labels != -1, cls_scores, labels)
cls_scores
```

    [-1.   0.2  0.3  0.4  0.5]
    <NDArray 5 @cpu(0)>

```python
X = nd.array([1, 0, 1, 0, 1])
Y = nd.array([2, 4, 6, 8, 10])
nd.where(X == 1, x=X, y=Y)
```

    [1. 4. 1. 8. 1.]
    <NDArray 5 @cpu(0)>

# Sequence Mask

[docs](https://mxnet.apache.org/versions/1.8.0/api/python/docs/api/ndarray/ndarray.html#mxnet.ndarray.SequenceMask)

```
mxnet.ndarray.SequenceMask(data=None, sequence_length=None, use_sequence_length=_Null, value=_Null, axis=_Null, out=None, name=None, **kwargs)¶
```

```python
X = nd.array([[1,2,0],[3,4,0],[6,7,0]])
print('X.shape ', X.shape)
print(X)
```

    X.shape  (3, 3)

    [[1. 2. 0.]
     [3. 4. 0.]
     [6. 7. 0.]]
    <NDArray 3x3 @cpu(0)>

```python
nd.SequenceMask(X)
```

    [[1. 2. 0.]
     [3. 4. 0.]
     [6. 7. 0.]]
    <NDArray 3x3 @cpu(0)>

```python
Y = nd.array([1,2,0])  # 1 value in Column 0, 2 values from column 1, 0 values from column 2
```

```python
nd.SequenceMask(X, sequence_length=Y, use_sequence_length=True, value=9, axis=0)
```

    [[1. 2. 9.]
     [9. 4. 9.]
     [9. 9. 9.]]
    <NDArray 3x3 @cpu(0)>

```python

```

```python

```

#### Using cumsum to find first zero

```python
X = nd.array([[1,2,0],[3,4,0],[6,7,0]])
print('X.shape ', X.shape)
print(X)
```

    X.shape  (3, 3)

    [[1. 2. 0.]
     [3. 4. 0.]
     [6. 7. 0.]]
    <NDArray 3x3 @cpu(0)>

```python
# create a sequence array using python
Sequence_array = nd.zeros(3)
for i in range(3):
    #print(i)
    Sequence_array[i] = i

Sequence_array
```

    [0. 1. 2.]
    <NDArray 3 @cpu(0)>

```python
An_array = nd.cumsum(X, axis=0)[2,:]

last_column_of_data = nd.where(An_array==0, x=An_array, y=Sequence_array).max()
last_column_of_data
```

    [1.]
    <NDArray 1 @cpu(0)>

```python

```