复现 Pandas 指数加权方差

了解为什么计算指数加权方差不能正确估计方差。

作者：Adrian Letchford 译者：stmoonar

你很可能熟悉用指数加权法计算平均数的方法。指数加权平均法的原理是对较新的信息赋予较高的权重。指数加权平均法的权重如下：

wt=(1−α)t\displaystyle{w_t = (1 - \alpha)^t}wt​=(1−α)t

对于t∈[0,…,T]t \in [0, \dots, T]t∈[0,…,T]，序列 Xt X_tXt​ 的指数加权平均值是这样的：

X ˉ T=1∑t wt∑t wtXt\displaystyle{\bar{X}_T = \frac{1}{\sum_t w_t} \sum_t w_t X_t}XˉT​=∑t​wt​1​t∑​wt​Xt​

在 Pandas 中，您可以通过以下面的方法轻松计算指数加权移动平均线：

Copy
df.ewm(alpha=0.1).mean()

如果你自己计算指数加权平均值，你会发现它与 Pandas 给出的结果一致。但是，我们即将看到，如果我们尝试用方差来计算，我们将得到一个很差的估计值。这就是所谓的估计偏差（estimation bias）。

什么是偏差（bias）？

估计量的偏差是指估计量的预期值与被估计参数（本例中为方差）的真实值之间的差值。有偏差的估计值与真实值之差不为零，而无偏差的估计值与真实值之差为零。

让我们试着测量方差，看看会发生什么。随机变量 X XX 的方差是：

σ 2=E[(X−μ)2]\displaystyle{\sigma^2 = E[(X - \mu)^2]}σ2=E[(X−μ)2]

如果我们有 X XX 的 n nn 值样本，我们可以尝试用样本的平均值代替期望值 E[⋅]E[\cdot]E[⋅] 来估计方差：

1 n∑i(Xi−μ)2\displaystyle{\frac{1}{n} \sum_i \left(X_i - \mu \right)^2}n1​i∑​(Xi​−μ)2

然后用样本的平均值代替 μ\muμ：
X ˉ=1 n∑i Xiσ^2=1 n∑i(Xi−X ˉ)2\displaystyle{\begin{aligned} \bar{X} &= \frac{1}{n}\sum_i X_i \\ \hat{\sigma}^2 &= \frac{1}{n} \sum_i \left(X_i - \bar{X} \right)^2 \end{aligned}}Xˉσ^2​=n1​i∑​Xi​=n1​i∑​(Xi​−Xˉ)2​

我们可以用 Python 写一个简单的模拟，看看我们的估计量得到的 σ^2\hat{\sigma}^2σ^2 有多好。

Copy
from scipy.stats import norm

num_simulations = 10_000_000
n = 5 # 样本数

# 这样就得到了一个数组，其中每一行都是一个模拟，而列则是样本值。
simulations = norm.rvs(loc=0, scale=1, size=(num_simulations, n))

# 由此得出每个模拟的估计平均值。
avg = simulations.mean(axis=1).reshape(-1, 1)

# 使用我们的估计器来估计每个模拟的方差。
estimates = ((simulations - avg)**2).sum(1) / n

译者注：

代码使用了 norm.rvs 方法从正态分布中随机生成样本值，loc=0 表示正态分布的均值为 0，scale=1 表示标准差为 1，size=(num_simulations, n) 表示生成的数组将有 num_simulations 行，每行有 n 个样本值。

现在我们有 1000 万个方差估计值，而真实方差为 1。那么我们的平均估计值是多少呢？计算估计值的平均值：

Copy
estimates.mean()

得出约 0.8！ 我们的估计值偏差了 0.2。 这就是偏差！

偏差从哪里来？

让我们回到方差的定义以及如何将其转化为样本估计值。为了计算我们的估计值，我们将 μ\muμ 换成了样本平均值：

1 n∑i(Xi−μ)2⇒1 n∑i(Xi−X ˉ)2\displaystyle{\frac{1}{n} \sum_i \left(X_i - \mu \right)^2 \quad\Rightarrow\quad \frac{1}{n} \sum_i \left(X_i - \bar{X} \right)^2}n1​i∑​(Xi​−μ)2⇒n1​i∑​(Xi​−Xˉ)2

这就是偏差产生的原因。小样本的平均值（X ˉ\bar{X}Xˉ）会比总体的平均值（μ\muμ）更接近这些样本，通过下面的例子可以更加直观。

图 1 显示了一个有 100 个随机点的例子，这些点的中心点（即平均值）为 0，其中有 5 个点被随机选中，它们的平均值用黑叉表示。这 5 个样本的平均值就是最接近这 5 个样本的点。根据定义，样本的平均数会比总体平均数更接近样本。因此：

1 n∑i(Xi−X ˉ)2<1 n∑i(Xi−μ)2\displaystyle{\frac{1}{n} \sum_i \left(X_i - \bar{X} \right)^2 \quad\lt\quad \frac{1}{n} \sum_i \left(X_i - \mu \right)^2}n1​i∑​(Xi​−Xˉ)2<n1​i∑​(Xi​−μ)2

我们的估计值会低于（underestimate ） X XX 的方差！

图 1：偏差示意图。 这是一幅平均值为（0，0）的 100 个随机点的图。图中随机选取了 5 个点并突出显示。黑叉表示这 5 个随机点的平均值。
事实上，如果重复一次上面的 Python 模拟，但是使用 0（总体平均值）代替样本平均值：

Copy
avg = 0

那么得到的样本方差的平均值就是 1：

Copy
estimates.mean()

如果知道总体均值，我们就可以从一组样本中得到方差的无偏估计值。但实际上，我们并不知道总体均值。幸运的是，我们可以量化偏差并加以纠正。

量化偏差

到目前为止，我们已经看到 σ^2\hat{\sigma}^2σ^2 是对总体方差的一个有偏差的估计。我们通过模拟多个样本得到 σ^2\hat{\sigma}^2σ^2 并取平均值发现了这一点。模拟结果表明：

E[σ^2]≠σ 2\displaystyle{E[\hat{\sigma}^2] \ne \sigma^2}E[σ^2]=σ2

我们现在要摆脱模拟，计算 E[σ^2]E[\hat{\sigma}^2]E[σ^2] 的精确值。我们可以通过展开式（原文：expanding it out）来实现。我们从：

E[σ^2]=E[1 n∑i(Xi−X ˉ)2]\displaystyle{E[\hat{\sigma}^2] = E \left[ \frac{1}{n} \sum_i \left(X_i - \bar{X} \right)^2 \right]}E[σ^2]=E[n1​i∑​(Xi​−Xˉ)2]

开始。

我们可以让X ˉ=μ−(μ−X ˉ)\bar{X} = \mu - (\mu - \bar{X})Xˉ=μ−(μ−Xˉ)，这意味着我们可以展开为

E[σ^2]=E[1 n∑i((Xi−μ)−(X ˉ−μ))2]\displaystyle{E[\hat{\sigma}^2] = E \left[ \frac{1}{n} \sum_i \left((X_i - \mu)- (\bar{X} - \mu) \right)^2 \right]}E[σ^2]=E[n1​i∑​((Xi​−μ)−(Xˉ−μ))2]

通过一些代数知识，我们可以展开平方式：

E[σ^2]=E[1 n∑i((Xi−μ)2−2(X ˉ−μ)(Xi−μ)+(X ˉ−μ)2)]=E[1 n∑i(Xi−μ)2−2(X ˉ−μ)1 n∑i(Xi−μ)+1 n∑i(X ˉ−μ)2]=E[1 n∑i(Xi−μ)2−2(X ˉ−μ)1 n∑i(Xi−μ)+(X ˉ−μ)2]\displaystyle{\begin{aligned} E[\hat{\sigma}^2] &= E \left[ \frac{1}{n} \sum_i \left((X_i - \mu)^2 - 2(\bar{X} - \mu)(X_i - \mu) + (\bar{X} - \mu)^2\right) \right] \\ &= E \left[ \frac{1}{n} \sum_i (X_i - \mu)^2 - 2(\bar{X} - \mu) \frac{1}{n} \sum_i(X_i - \mu) + \frac{1}{n} \sum_i(\bar{X} - \mu)^2 \right] \\ &= E \left[ \frac{1}{n} \sum_i (X_i -\mu)^2 - 2(\bar{X} - \mu) \frac{1}{n} \sum_i(X_i - \mu) + (\bar{X} -\mu)^2 \right] \end{aligned}}E[σ^2]​=E[n1​i∑​((Xi​−μ)2−2(Xˉ−μ)(Xi​−μ)+(Xˉ−μ)2)]=E[n1​i∑​(Xi​−μ)2−2(Xˉ−μ)n1​i∑​(Xi​−μ)+n1​i∑​(Xˉ−μ)2]=E[n1​i∑​(Xi​−μ)2−2(Xˉ−μ)n1​i∑​(Xi​−μ)+(Xˉ−μ)2]​

现在，请注意：

1 n∑i(Xi−μ)=1 n∑i Xi−1 n∑i μ=1 n∑i Xi−μ=X ˉ−μ\displaystyle{\frac{1}{n} \sum_i(X_i - \mu) = \frac{1}{n} \sum_i X_i - \frac{1}{n} \sum_i \mu = \frac{1}{n} \sum_i X_i - \mu = \bar{X} - \mu}n1​i∑​(Xi​−μ)=n1​i∑​Xi​−n1​i∑​μ=n1​i∑​Xi​−μ=Xˉ−μ

这意味着：

E[σ^2]=E[1 n∑i(Xi−μ)2−2(X ˉ−μ)2+(X ˉ−μ)2]=E[1 n∑i(Xi−μ)2−(X ˉ−μ)2]=E[1 n∑i(Xi−μ)2]−E[(X ˉ−μ)2]\displaystyle{\begin{aligned} E[\hat{\sigma}^2] &= E \left[ \frac{1}{n} \sum_i (X_i - \mu)^2 - 2(\bar{X} - \mu)^2 + (\bar{X} - \mu)^2 \right] \\ &= E \left[ \frac{1}{n} \sum_i (X_i - \mu)^2 - (\bar{X} - \mu)^2 \right] \\ &= E \left[ \frac{1}{n} \sum_i (X_i - \mu)^2 \right] - E \left[ (\bar{X} - \mu)^2 \right] \end{aligned}}E[σ^2]​=E[n1​i∑​(Xi​−μ)2−2(Xˉ−μ)2+(Xˉ−μ)2]=E[n1​i∑​(Xi​−μ)2−(Xˉ−μ)2]=E[n1​i∑​(Xi​−μ)2]−E[(Xˉ−μ)2]​

这里的妙处是：

E[1 n∑i(Xi−μ)2]=σ 2\displaystyle{E \left[ \frac{1}{n} \sum_i (X_i - \mu)^2 \right] = \sigma^2}E[n1​i∑​(Xi​−μ)2]=σ2

意味着：

E[σ^2]=σ 2−E[(X ˉ−μ)2]\displaystyle{\begin{aligned} E[\hat{\sigma}^2] &= \sigma^2 - E \left[ (\bar{X} - \mu)^2 \right] \end{aligned}}E[σ^2]​=σ2−E[(Xˉ−μ)2]​

项 E[(X ˉ−μ)2]E \left[ (\bar{X} - \mu)^2 \right]E[(Xˉ−μ)2] 是样本平均数的方差。 根据Bienaymé’s identity ，我们知道它等于

E[(X ˉ−μ)2]=1 n σ 2\displaystyle{E \left[ (\bar{X} - \mu)^2 \right] = \frac{1}{n}\sigma^2}E[(Xˉ−μ)2]=n1​σ2

这让我们得到：

E[σ^2]=(1−1 n)σ 2=(1−15)×1=0.8\displaystyle{E[\hat{\sigma}^2] = (1 - \frac{1}{n}) \sigma^2 = (1 - \frac{1}{5}) \times 1 = 0.8}E[σ^2]=(1−n1​)σ2=(1−51​)×1=0.8

回想一下我们的 Python 模拟；样本数为 n=5 n=5n=5，真实方差为 σ 2=1\sigma^2 = 1σ2=1，估计方差为 σ^2=0.8\hat{\sigma}^2 = 0.8σ^2=0.8。 如果我们将 n nn 和 σ 2\sigma^2σ2 插入上述公式，就会得到有偏差的答案：

E[σ^2]=(1−1 n)σ 2=(1−15)×1=0.8\displaystyle{E[\hat{\sigma}^2] = (1 - \frac{1}{n}) \sigma^2 = (1 - \frac{1}{5}) \times 1 = 0.8}E[σ^2]=(1−n1​)σ2=(1−51​)×1=0.8

无偏估计量

现在我们知道了 E[σ^2]E[\hat{\sigma}^2]E[σ^2] 的精确值，就可以想办法修正 σ^2\hat{\sigma}^2σ^2，使其成为 σ 2\sigma^2σ2 的无偏估计值。

修正项为：

n n−1\displaystyle{\frac{n}{n-1}}n−1n​

通过演算，我们可以看到：

E[n n−1 σ^2]=n n−1 E[σ^2]=n n−1(1−1 n)σ 2=n(1−1 n)n−1 σ 2=n−1n−1 σ 2=σ 2\displaystyle{\begin{aligned} E[\frac{n}{n-1} \hat{\sigma}^2] &=\frac{n}{n-1} E[\hat{\sigma}^2] \\ &= \frac{n}{n-1}(1 - \frac{1}{n}) \sigma^2 \\ &= \frac{n(1 - \frac{1}{n})}{n-1} \sigma^2 \\ &= \frac{n - 1}{n-1} \sigma^2 \\ &= \sigma^2 \end{aligned}}E[n−1n​σ^2]​=n−1n​E[σ^2]=n−1n​(1−n1​)σ2=n−1n(1−n1​)​σ2=n−1n−1​σ2=σ2​

因此，从一组样本中得到的 σ 2\sigma^2σ2 的无偏估计值是：

n n−1 σ^2=n n−11 n∑i(Xi−X ˉ)2=1 n−1∑i(Xi−X ˉ)2\displaystyle{\begin{aligned} \frac{n}{n-1} \hat{\sigma}^2 &= \frac{n}{n-1} \frac{1}{n} \sum_i \left(X_i - \bar{X} \right)^2 \\ &= \frac{1}{n-1} \sum_i \left(X_i - \bar{X} \right)^2 \end{aligned}}n−1n​σ^2​=n−1n​n1​i∑​(Xi​−Xˉ)2=n−11​i∑​(Xi​−Xˉ)2​

无偏加权估计量

现在，我们将上述内容扩展到样本加权的情况。 加权样本平均值为：

X ˉ=1∑i wi∑i wiXi\displaystyle{\bar{X} = \frac{1}{\sum_i w_i} \sum_i w_i X_i}Xˉ=∑i​wi​1​i∑​wi​Xi​

加权方差为：

σ^2=1∑iwi∑iwi(Xi−X ˉ)2\hat{\sigma}^2 = \frac{1}{\sum_i w_i} \sum_i w_i\left(X_i - \bar{X} \right)^2σ^2=∑i​wi​1​∑i​wi​(Xi​−Xˉ)2

按照与之前完全相同的展开过程，我们得出：

E[σ^2]=σ 2−E[(X ˉ−μ)2]\displaystyle{\begin{aligned} E[\hat{\sigma}^2] &= \sigma^2 - E \left[ (\bar{X} - \mu)^2 \right] \end{aligned}}E[σ^2]​=σ2−E[(Xˉ−μ)2]​

平均值的方差为：

E[(X ˉ−μ)2]=var(X ˉ)=var(1∑wi∑wiXi)=1(∑wi)2∑var(wiXi)=1(∑wi)2∑wi 2 var(Xi)=∑wi 2(∑wi)2 σ 2\displaystyle{\begin{aligned} E \left[ (\bar{X} - \mu)^2 \right] &= \text{var}(\bar{X}) \\ &= \text{var}\left(\frac{1}{\sum w_i} \sum w_i X_i \right) \\ &= \frac{1}{(\sum w_i)^2} \sum \text{var} (w_i X_i) \\ &= \frac{1}{(\sum w_i)^2} \sum w_i^2 \text{var} (X_i) \\ &= \frac{\sum w_i^2}{(\sum w_i)^2} \sigma^2 \end{aligned}}E[(Xˉ−μ)2]​=var(Xˉ)=var(∑wi​1​∑wi​Xi​)=(∑wi​)21​∑var(wi​Xi​)=(∑wi​)21​∑wi2​var(Xi​)=(∑wi​)2∑wi2​​σ2​

var varvar是计算方差。

这样我们就得到：

E[σ^2]=σ 2−∑wi 2(∑wi)2σ 2=(1−∑wi 2(∑wi)2)σ 2\displaystyle{\begin{aligned} E[\hat{\sigma}^2] &= \sigma^2 - \frac{\sum w_i^2}{(\sum w_i)^2} \\ \sigma^2 &= \left(1 - \frac{\sum w_i^2}{(\sum w_i)^2} \right)\sigma^2 \end{aligned}}E[σ^2]σ2​=σ2−(∑wi​)2∑wi2​​=(1−(∑wi​)2∑wi2​​)σ2​

偏差修正项为：

b=(∑wi)2(∑wi)2−∑wi 2\displaystyle{b = \frac{(\sum w_i)^2}{(\sum w_i)^2 - \sum w_i^2}}b=(∑wi​)2−∑wi2​(∑wi​)2​

这意味着方差的无偏加权估计值为：

b σ^2\displaystyle{b \hat{\sigma}^2}bσ^2

复现 Pandas 指数加权方差

现在，我们拥有了复现 Pandas 指数加权方差所需的所有工具。

Copy
import numpy as np
import pandas as pd

N = 1000

# 创建一些fake data
df = pd.DataFrame()
df['data'] = np.random.randn(N)

# 为 EWM 设置半衰期，并将其转换为 alpha 值进行计算。
halflife = 10
a = 1 - np.exp(-np.log(2)/halflife) # alpha

# 这是 Pandas 的 ewm
df['var_pandas'] = df.ewm(alpha=a).var()

# 初始化变量
varcalc = np.zeros(len(df))

# 计算指数移动方差
for i in range(0, N):

x = df['data'].iloc[0:i+1].values

# Weights
n = len(x)
w = (1-a)**np.arange(n-1, -1, -1) # 顺序相反，以便与序列顺序一致

# 计算指数移动平均线
ewma = np.sum(w * x) / np.sum(w)

# 计算偏差修正项
bias = np.sum(w)**2 / (np.sum(w)**2 - np.sum(w**2))

# 计算带偏差修正的指数移动方差
varcalc[i] = bias * np.sum(w * (x - ewma)**2) / np.sum(w)

df['var_calc'] = varcalc