Copula - 多變量聯合分佈

[1]:
import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns
from scipy import stats

sns.set_style("darkgrid")
sns.mpl.rc("figure", figsize=(8, 8))
[2]:
%%javascript
IPython.OutputArea.prototype._should_scroll = function(lines) {
    return false;
}

在建模系統時,經常會遇到涉及多個參數的情況。每個參數都可以使用給定的機率密度函數 (PDF) 來描述。如果我們想要能夠生成一組新的參數值,我們需要能夠從這些分佈(也稱為邊緣分佈)中取樣。主要有兩種情況:(i) PDF 是獨立的;(ii) 存在依賴關係。一種對依賴關係建模的方法是使用 copula

從 copula 中取樣

讓我們使用一個雙變數範例,並首先假設我們有一個先驗,並且知道如何對兩個變數之間的依賴關係進行建模。

在這種情況下,我們使用 Gumbel copula 並將其超參數 theta=2 固定。我們可以將其二維 PDF 可視化。

[3]:
from statsmodels.distributions.copula.api import (
    CopulaDistribution, GumbelCopula, IndependenceCopula)

copula = GumbelCopula(theta=2)
_ = copula.plot_pdf()  # returns a matplotlib figure
../../../_images/examples_notebooks_generated_copula_5_0.png

我們可以對 PDF 進行取樣。

[4]:
sample = copula.rvs(10000)
h = sns.jointplot(x=sample[:, 0], y=sample[:, 1], kind="hex")
_ = h.set_axis_labels("X1", "X2", fontsize=16)
/opt/hostedtoolcache/Python/3.10.15/x64/lib/python3.10/site-packages/statsmodels/tools/rng_qrng.py:54: FutureWarning: Passing `None` as the seed currently return the NumPy singleton RandomState
(np.random.mtrand._rand). After release 0.13 this will change to using the
default generator provided by NumPy (np.random.default_rng()). If you need
reproducible draws, you should pass a seeded np.random.Generator, e.g.,

import numpy as np
seed = 32839283923801
rng = np.random.default_rng(seed)"

  warnings.warn(_future_warn, FutureWarning)
../../../_images/examples_notebooks_generated_copula_7_1.png

讓我們回到我們的兩個變數。在這種情況下,我們認為它們分別呈 gamma 分佈和常態分佈。如果它們彼此獨立,我們可以分別從每個 PDF 中取樣。在這裡,我們使用一個方便的類別來執行相同的操作。

可重複性

從 copula 中生成可重複的隨機值需要明確設置 seed 引數。seed 接受已初始化的 NumPy GeneratorRandomState,或任何可接受 np.random.default_rng 的引數,例如,整數或整數序列。此範例使用整數。

直接在 np.random 分佈中公開的單例 RandomState 不會使用,且設定 np.random.seed 對生成的值沒有影響。

[5]:
marginals = [stats.gamma(2), stats.norm]
joint_dist = CopulaDistribution(copula=IndependenceCopula(), marginals=marginals)
sample = joint_dist.rvs(512, random_state=20210801)
h = sns.jointplot(x=sample[:, 0], y=sample[:, 1], kind="scatter")
_ = h.set_axis_labels("X1", "X2", fontsize=16)
../../../_images/examples_notebooks_generated_copula_9_0.png

現在,在上面我們使用 copula 表示了變數之間的依賴關係,我們可以使用此 copula 來取樣一組新的觀測值,並使用相同的方便類別。

[6]:
joint_dist = CopulaDistribution(copula, marginals)
# Use an initialized Generator object
rng = np.random.default_rng([2, 0, 2, 1, 0, 8, 0, 1])
sample = joint_dist.rvs(512, random_state=rng)
h = sns.jointplot(x=sample[:, 0], y=sample[:, 1], kind="scatter")
_ = h.set_axis_labels("X1", "X2", fontsize=16)
../../../_images/examples_notebooks_generated_copula_11_0.png

這裡有兩點需要注意。(i) 與獨立情況一樣,邊緣分佈正確地顯示了 gamma 分佈和常態分佈;(ii) 兩個變數之間存在可見的依賴關係。

估計 copula 參數

現在,假設我們已經有了實驗數據,並且知道可以使用 Gumbel copula 表示依賴關係。但是我們不知道 copula 的超參數值。在這種情況下,我們可以估計該值。

我們將使用剛剛生成的樣本,因為我們已經知道應該得到的超參數值:theta=2

[7]:
copula = GumbelCopula()
theta = copula.fit_corr_param(sample)
print(theta)
2.049379621506455

我們可以發現,估計的超參數值接近先前設定的值。


上次更新:2024 年 10 月 03 日