生成一定相关性的二元正态分布
摘要
本文讲简要叙述如何生成具有一定相关性 (correlation) 的服从二元正态分布的随机变量。
二元正态分布
二元正态分布概率密度函数
我们知道单变量标准正态分布
X
∼
N
(
0
,
1
)
X \sim N(0, \, 1)
X∼N(0,1) 的概率密度函数 (probability density function, pdf) 为
f
(
x
)
=
1
2
π
e
−
x
2
/
2
,
−
∞
<
x
<
∞
\displaystyle f(x) = \frac{1}{\sqrt{2\pi}} e^{-x^2 / 2}, \, -\infty < x < \infty
f(x)=2π1e−x2/2,−∞<x<∞。
那么二元正态分布是如何定义的呢?其定义与单变量的正态分布类似,不过引入了两个变量之间的相关性。具体的定义如下:
如果二元变量
(
X
,
Y
)
(X, \, Y)
(X,Y) 服从二元正态分布,那么
(
X
,
Y
)
(X, \, Y)
(X,Y) 的联合概率密度分布函数(joint density distribution function)为:
f
(
x
,
y
)
=
1
2
π
σ
X
σ
Y
1
−
ρ
2
×
exp
(
−
1
2
(
1
−
ρ
2
)
(
(
x
−
μ
X
σ
X
)
2
−
2
ρ
(
x
−
μ
X
σ
X
)
(
y
−
μ
Y
σ
Y
)
+
(
y
−
μ
Y
σ
Y
)
2
)
\begin{aligned} f(x, \, y) &= \frac{1}{2\pi \sigma_X \sigma_Y \sqrt{1 - \rho^2}} \times \\ & \exp \left( -\frac{1}{2(1 - \rho^2)} \Big( \big( \frac{x - \mu_X}{\sigma_X} \big)^2 - 2 \rho (\frac{x - \mu_X}{\sigma_X}) (\frac{y - \mu_Y}{\sigma_Y}) + (\frac{y - \mu_Y}{\sigma_Y})^2 \right) \end{aligned}
f(x,y)=2πσXσY1−ρ21×exp(−2(1−ρ2)1((σXx−μX)2−2ρ(σXx−μX)(σYy−μY)+(σYy−μY)2)
在公式中,我们称
μ
X
,
μ
Y
\mu_X, \, \mu_Y
μX,μY 是二元正态分布的均值(期望),
σ
X
2
,
σ
Y
2
\sigma_X^2, \, \sigma_Y^2
σX2,σY2 是二元正态分布的方差,
ρ
\rho
ρ 为二元正态分布的相关性。
二元正态分布随机数的生成
现在我们想生成两个服从标准正态分布的随机数,并且要求这两个随机数的相关性是一个给定的数 ρ \rho ρ。我们应该如何做呢?
如果我们想用一个程序生成服从标准正态分布的随机数,我们可以用Python numpy包中的 np.random.normal(0, 1, n)
,来生成
n
n
n 个服从
N
(
0
,
1
)
N(0, \, 1)
N(0,1) 的随机数。但是想要生成具有一定相关性的两个服从标准正态分布的随机数,我们就不能用简单得用两次np.random.normal(0, 1, n)
,因为这样我们只是生成了两个独立的随机变量,它们的相关性为 0。为了生成具有一定相关性的二元正态分布,这里我们采用构造线性组合的方法。具体如下。
假设
A
,
B
A, \, B
A,B 是两个独立的标准正态分布。 令
X
=
α
A
+
β
B
,
X = \alpha A + \beta B,
X=αA+βB,
Y
=
γ
A
+
δ
B
Y = \gamma A + \delta B
Y=γA+δB。
(
α
,
β
,
γ
,
δ
)
(\alpha, \, \beta, \, \gamma, \, \delta)
(α,β,γ,δ) 是四个待确定的参数。我们希望找到这样的
(
α
,
β
,
γ
,
δ
)
(\alpha, \, \beta, \, \gamma, \, \delta)
(α,β,γ,δ), 使得
X
∼
N
(
0
,
1
)
X \sim N(0, \, 1)
X∼N(0,1),
Y
∼
N
(
0
,
1
)
Y \sim N(0, \, 1)
Y∼N(0,1),并且
C
o
r
(
X
,
Y
)
=
ρ
\rm{Cor}(X, \, Y) = \rho
Cor(X,Y)=ρ。我们知道两个独立的正态分布相加依然是一个正态分布。我们有
X
∼
N
(
0
,
α
2
+
β
2
)
X \sim N(0, \, \alpha^2 + \beta^2)
X∼N(0,α2+β2),
Y
∼
N
(
0
,
γ
2
+
δ
2
)
Y \sim N(0, \, \gamma^2 + \delta^2)
Y∼N(0,γ2+δ2)。所以,我们须要
α
2
+
β
2
=
1
\alpha^2 + \beta^2 = 1
α2+β2=1,
γ
2
+
δ
2
=
1
\gamma^2 + \delta^2 = 1
γ2+δ2=1。为了简化运算,我们设
α
=
cos
θ
,
β
=
sin
θ
\alpha = \cos\theta, \, \beta = \sin \theta
α=cosθ,β=sinθ;
γ
=
sin
θ
,
δ
=
cos
θ
\gamma = \sin \theta, \, \delta = \cos \theta
γ=sinθ,δ=cosθ。这样我们的
X
,
Y
X, \, Y
X,Y 就分别服从标准正态分布。下面我们只需要找到
θ
\theta
θ,使得
C
o
r
(
X
,
Y
)
=
ρ
\rm{Cor}(X, \, Y) = \rho
Cor(X,Y)=ρ。
接下来我们来看条件 C o r ( X , Y ) = ρ \rm{Cor}(X, \, Y) = \rho Cor(X,Y)=ρ。 C o r ( X , Y ) = Cov ( X , Y ) Var ( X ) Var ( Y ) \displaystyle \rm{Cor}(X, \, Y) = \frac{\text{Cov}(X, \, Y)}{\sqrt{\text{Var}(X) \text{Var}(Y)}} Cor(X,Y)=Var(X)Var(Y)Cov(X,Y)。因为 X = ( cos θ ) A + ( sin θ ) B ∼ N ( 0 , 1 ) X = (\cos \theta) A + (\sin \theta) B \sim N(0, \, 1) X=(cosθ)A+(sinθ)B∼N(0,1),所以 Var ( X ) = 1 \text{Var}(X) = 1 Var(X)=1。同样的, Var ( Y ) = 1 \text{Var}(Y) = 1 Var(Y)=1。
所以我们只须要 Cov ( X , Y ) = ρ \text{Cov}(X, \, Y) = \rho Cov(X,Y)=ρ。
而
Cov
(
X
,
Y
)
=
Cov
(
(
cos
θ
)
A
+
(
sin
θ
)
B
,
(
sin
θ
)
A
+
(
cos
θ
)
B
)
=
cos
θ
sin
θ
+
sin
θ
cos
θ
=
sin
2
θ
\begin{aligned} \text{Cov}(X, \, Y) &= \text{Cov} \Big( (\cos \theta) A + (\sin \theta) B, (\sin \theta) A + (\cos \theta) B \Big) \\ &= \cos \theta \sin \theta + \sin \theta \cos \theta \\ &= \sin2\theta \end{aligned}
Cov(X,Y)=Cov((cosθ)A+(sinθ)B,(sinθ)A+(cosθ)B)=cosθsinθ+sinθcosθ=sin2θ。
注意,在上面的计算中我们用到了 C o r ( A , B ) = 0 \rm{Cor}(A, \, B) = 0 Cor(A,B)=0。
所以我们有
sin
2
θ
=
ρ
\sin2\theta = \rho
sin2θ=ρ。我们选取
θ
=
arcsin
ρ
2
\displaystyle \theta = \frac{ \arcsin \rho}{2}
θ=2arcsinρ。这样,我们通过
{
X
=
(
cos
θ
)
A
+
(
sin
θ
)
B
Y
=
(
sin
θ
)
A
+
(
cos
θ
)
B
\begin{cases} X = (\cos \theta) A + (\sin \theta) B \\ Y = (\sin \theta) A + (\cos \theta) B \end{cases}
{X=(cosθ)A+(sinθ)BY=(sinθ)A+(cosθ)B
计算得到的
X
,
Y
X, \, Y
X,Y 就是相关性为
ρ
\rho
ρ 的标准正态分布。
程序实现
我们用Python 来实现上述方法。
import numpy as np
import matplotlib.pyplot as plt
class bivariateNormal:
def __init__(self, rho: 'float', m: int):
"""
Suppose we want to generate a pair of
random variables X, Y, with X ~ N(0, 1),
Y ~ N(0, 1), and Cor(X, Y) = rho. m is
the number of data pairs we want to generate.
"""
self.rho = rho
self.m = m
def generateBivariate(self) -> 'tuple(np.array, np.array)':
"""
Generate two random variables X, Y, with X ~ N(0, 1),
Y ~ N(0, 1), and Cor(X, Y) = rho.
self.m is the number of sample points we generated.
We return a tuple (X, Y).
"""
theta = np.arcsin(self.rho) / 2
A = np.random.normal(0, 1, self.m)
B = np.random.normal(0, 1, self.m)
X = np.cos(theta) * A + np.sin(theta) * B
Y = np.sin(theta) * A + np.cos(theta) * B
return X, Y
我们试着生成m = 1000
个(X, Y),如下:
m = 10 ** 3
rho = -0.4
a = bivariateNormal(rho, m)
X, Y = a.generateBivariate()
np.corrcoef(X, Y)
得到的结果为:
array([[ 1. , -0.41408183],
[-0.41408183, 1. ]])
即X 与 Y 的相关性为-0.414,与其理论值-0.4 很接近。
多元正态分布的情况
生成服从 N ( μ , Σ ) N(\mathbf{\mu}, \, \Sigma) N(μ,Σ)的 n n n 元正态分布
假设我们想要生成服从 N ( μ , Σ ) N(\mathbf{\mu}, \, \mathbf{\Sigma}) N(μ,Σ) 的 n n n 元正态分布 X = ( x 1 , x 2 , ⋯ , x n ) T \mathbf{X} = (x_1, \, x_2, \cdots, \, x_n)^T X=(x1,x2,⋯,xn)T ( X \mathbf{X} X 是一个列向量,其每个分量 x i x_i xi 均为一个随机变量)。我们称 μ \mu μ 是 X \mathbf{X} X 的期望, Σ \Sigma Σ 是 X X X 的协方差矩阵。
想要生成这样一个随机变量向量, 我们可以采取如下步骤 [1]:
- 生成 n n n 个独立的标准正态分布 z 1 , z 2 , ⋯ , z n z_1, \, z_2, \cdots, \, z_n z1,z2,⋯,zn,记为 Z \mathbf{Z} Z。
- 找到矩阵 C C C,满足 C C T = Σ CC^T = \mathbf{\Sigma} CCT=Σ。
于是 X = μ + C Z \mathbf{X} = \mu + C \mathbf{Z} X=μ+CZ 即为满足要求的 n n n 元随机数。
具体证明的过程可以参考下面的定理:
假设 X \mathbf{X} X 是一个 n n n-dimensional 的随机变量向量, X \mathbf{X} X 的期望向量是 μ \mu μ, X \mathbf{X} X 的协方差矩阵为 Σ \Sigma Σ 。假设 C \mathbf{C} C 是一个 k × n k \times n k×n 的实矩阵, b \mathbf{b} b 是一个 k × 1 k \times 1 k×1 的列向量。则 Y = C X + b \mathbf{Y} = \mathbf{C} \mathbf{X} + \mathbf{b} Y=CX+b 的期望向量是 C μ + b \mathbf{C} \mu + \mathbf{b} Cμ+b,协方差矩阵为 Σ Y = C Σ C T \Sigma_{\mathbf{Y}} = \mathbf{C} \Sigma \mathbf{C}^T ΣY=CΣCT。
多元情况的程序实现
from scipy.linalg import cholesky
class generate_correlated_normal:
def __init__(self, n: int, mu: 'np.array', Sigma: 'np.ndarray', m: int):
"""
n is the dimension of the random vector.
mu is the expectation vector of the random vector.
mu.shape = (n, 1).
Sigma is the covariance matrix of the random sample
vector we want to generate. m is the number of
vectors we want to generate.
"""
self.mu = mu.reshape(n, 1)
self.Sigma = Sigma
self.m = m
def generate_random_vectors(self) -> 'np.ndarray':
"""
Generate m random vectors with expectation mu and
covariance matrix Sigma.
The returned value is of type np.ndarray, of shape
(n, m). n is the dimension of the random vector.
"""
C = cholesky(self.Sigma, lower=True)
n = self.Sigma.shape[0] # n is the dimension of the random vector
Z = np.random.normal(0, 1, (n, self.m))
return self.mu + np.dot(C, Z)
n = 3
mu = np.array([0, 0, 0])
Sigma = np.array([[1, 0.2, 0.3], [0.2, 1, 0.4], [0.3, 0.4, 1]])
m = 10 ** 4
b = generate_correlated_normal(n, mu, Sigma, m)
X = b.generate_random_vectors()
np.corrcoef(X[0,:], X[1,:])
array([[1. , 0.2136722],
[0.2136722, 1. ]])
np.corrcoef(X[0,:], X[2,:])
array([[1. , 0.31754401],
[0.31754401, 1. ]])
np.corrcoef(X[1,:], X[2,:])
array([[1. , 0.39934556],
[0.39934556, 1. ]])
可以看出,生成的随机向量元素之间的相关性是符合预期的 [2]。
参考文献
[1] Math stackexchange, Generate Correlated Normal Random Variables