# Introduction to Copulas

## Probability Review

Let's start by reviewing some basic probability concepts.

We'll focus specifically on continuous random variables, which is what the Copulas library is primarily intended to support.

### Probability Density Function

A probability density function $f(x)$ captures the likelihood that a random sample from the distribution is equal to $x$. For example, the probability density function for the standard normal distribution is given by

\begin{equation}
f(x) = \frac{1}{2 \pi} e^{-x^2/2}
\end{equation}

Note that the probability density function does **not** return a probability but rather a "relative likelihood" which can take on values in the interval $[0, \infty)$; however, the integral over the probability density function from $-\infty$ to $\infty$ must be equal to one.

### Cumulative Distribution Function

In many cases, the probability density function can be hard to work with directly. Instead, we will use the cumulative distribution function $F(x)$ which is defined as the integral of the probability density function

\begin{equation}
F(x) = \int_{-\infty}^x f(x)
\end{equation}

The below figure shows the probability density function $f(x)$ and the cumulative distribution function $F(x)$ for a normal standard distribution with mean $0.0$ and variance $1$.


```python
import warnings

warnings.filterwarnings('ignore')
```


```python
import numpy as np
from matplotlib import pyplot as plt
from scipy import stats

def plot_cdf_pdf():
    # Generate 10000 evenly distributed values from -4 to 4
    x = np.linspace(-4.0, 4.0, 10000)

    # Compute their Probability Densities and Cumulative Distributions
    pdf = stats.norm.pdf(x)
    cdf = stats.norm.cdf(x)

    figure = plt.figure(figsize=(16, 4))
    figure.add_subplot(1, 2, 1)
    plt.plot(x, pdf)
    plt.title("Probability Density Function")
    plt.xlabel("x")
    plt.ylabel("f(x)")

    figure.add_subplot(1, 2, 2)
    plt.plot(x, cdf)
    plt.title("Cumulative Density Function")
    plt.xlabel("x")
    plt.ylabel("F(x)")
```


```python
plot_cdf_pdf()
```

### Probability Integral Transform

The probability integral transform is a key component in our toolkit for working with probability distributions. Suppose we have a random variable $X$ that comes from a distribution with cumulative density function $F(X)$. Then, we can define a random variable $Y$ as

\begin{equation}
Y = F(X)
\end{equation}

and prove that $Y$ follows a uniform distribution over the interval $[0.0, 1.0]$.

The figure below shows an example of this. We sample some data from a normal distribution and plot it on the left. Then, we use the CDF of the normal distribution to transform the data, plot it on the right, and observe that it resembles an uniform distribution.


```python
from scipy import stats
from matplotlib import pyplot as plt


X = stats.norm.rvs(size=10000)
X_pit = stats.norm.cdf(X)

fig = plt.figure(figsize=(16, 4))
fig.add_subplot(1, 2, 1)
plt.hist(X, density=True, bins=10)
plt.title("Samples") 
plt.xlabel("x")

fig.add_subplot(1, 2, 2)
plt.hist(X_pit, density=True, bins=10)
plt.title("Transformed Samples")
plt.xlabel("x")

plt.show()
```

## Copulas

The key intuition underlying copula functions is the idea that marginal distributions can be modeled independently from the joint distribution. For example, consider a dataset with two columns containing age and income. A copula-based modeling approach would:

1. Model age and income independently, transforming them into uniform distributions using the *probability integral transform* explained above.
2. Model the relationship between the transformed variables using the copula function.

In this section, we demonstrate a simplified example of a Gaussian copula.


```python
from copulas.datasets import sample_bivariate_age_income

df = sample_bivariate_age_income()
df.head()
```




<div>
<style scoped>
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
</style>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>age</th>
      <th>income</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>48.935913</td>
      <td>399.161393</td>
    </tr>
    <tr>
      <th>1</th>
      <td>39.234323</td>
      <td>364.225531</td>
    </tr>
    <tr>
      <th>2</th>
      <td>55.659901</td>
      <td>406.475105</td>
    </tr>
    <tr>
      <th>3</th>
      <td>31.810637</td>
      <td>341.276022</td>
    </tr>
    <tr>
      <th>4</th>
      <td>65.342336</td>
      <td>414.347815</td>
    </tr>
  </tbody>
</table>
</div>




```python
from copulas.visualization import scatter_2d

scatter_2d(df)
```

Here's what the age and income variables look like separately.


```python
from copulas.visualization import hist_1d, side_by_side

side_by_side(hist_1d, {'Age': df['age'], 'Income': df['income']})
```

To model this using a Gaussian copula, we can simply run the following:


```python
from copulas.multivariate import GaussianMultivariate

copula = GaussianMultivariate()
copula.fit(df)
```

The GaussianMultivariate class will automatically transform the columns using the best available distribution; let's take a look at what the transformed age and income variables look like.


```python
age_cdf = copula.univariates[0].cdf(df['age'])
inc_cdf = copula.univariates[1].cdf(df['income'])

side_by_side(hist_1d, {'Age': age_cdf, 'Income': inc_cdf})
```

Note that this transformed data looks much more uniform than the original values. Using this transformed data, we can then model the relationship between age and income more easily and generate some synthetic data.


```python
synthetic = copula.sample(len(df))
```


```python
synthetic.head()
```




<div>
<style scoped>
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
</style>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>age</th>
      <th>income</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>31.907286</td>
      <td>304.110961</td>
    </tr>
    <tr>
      <th>1</th>
      <td>48.012266</td>
      <td>371.410661</td>
    </tr>
    <tr>
      <th>2</th>
      <td>35.805468</td>
      <td>360.889437</td>
    </tr>
    <tr>
      <th>3</th>
      <td>53.500743</td>
      <td>350.469573</td>
    </tr>
    <tr>
      <th>4</th>
      <td>39.832329</td>
      <td>350.109369</td>
    </tr>
  </tbody>
</table>
</div>




```python
from copulas.visualization import compare_2d

compare_2d(df, synthetic)
```

9b7173e67cc41c5a78e66af4c735c6d48a062422
ipynb
Jupyter Notebook
tutorials/01_Introduction_to_Copulas.ipynb
yzR1991/Copulas
72c0d9c398f7fe3eb075b56591911fea377cdc33
MIT
2020-01-12T14:36:45.000Z
2022-03-26T16:53:44.000Z
tutorials/01_Introduction_to_Copulas.ipynb
yzR1991/Copulas
72c0d9c398f7fe3eb075b56591911fea377cdc33
MIT
2020-01-09T16:28:25.000Z
2022-03-01T08:13:22.000Z
tutorials/01_Introduction_to_Copulas.ipynb
yzR1991/Copulas
72c0d9c398f7fe3eb075b56591911fea377cdc33
MIT
2020-02-01T13:47:26.000Z
2022-03-15T06:18:24.000Z
Qwen/Qwen-72B
1. YES
2. YES