指数加权方差が正確に分散を推定できない理由を理解する。
著者:Adrian Letchford 翻訳者:stmoonar

あなたはおそらく、指数加重法を用いて平均を計算する方法に慣れているでしょう。指数加重平均法の原理は新しい情報に高い重みを与えることです。指数加重平均法の重みは次のようになります:
wt=(1−α)t
t∈[0,…,T] の場合、系列 Xt の指数加重平均は次のようになります:
XˉT=∑twt1t∑wtXt
Pandas では、次の方法で簡単に指数加重移動平均を計算できます:
もし自分で指数加重平均を計算すると、Pandas が提供する結果と一致することがわかります。しかし、これから見るように、分散を計算しようとすると、非常に悪い推定値が得られます。これがいわゆる推定バイアス(estimation bias)です。
バイアスとは何ですか?#
推定量のバイアスは、推定量の期待値と推定されるパラメータ(この場合は分散)の真の値との差を指します。有偏推定値は真の値との差がゼロではなく、無偏推定値は真の値との差がゼロです。
分散を測定してみましょう。確率変数 X の分散は次のようになります:
σ2=E[(X−μ)2]
もしXのn値のサンプルがあれば、サンプルの平均値で期待値 E[⋅] を置き換えて分散を推定することができます:
n1i∑(Xi−μ)2
次に、サンプルの平均値で μ を置き換えます:
Xˉσ^2=n1i∑Xi=n1i∑(Xi−Xˉ)2
Python で簡単なシミュレーションを書いて、我々の推定量が得られる σ^2 がどれほど良いかを見てみましょう。
from scipy.stats import norm
num_simulations = 10_000_000
n = 5 # サンプル数
# これにより、各行が1つのシミュレーションで、列がサンプル値の配列が得られます。
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 です。では、我々の平均推定値はどのくらいでしょうか?推定値の平均を計算します:
約 0.8 が得られます!我々の推定値は 0.2 バイアスがあります。これがバイアスです!
バイアスはどこから来るのか?#
分散の定義に戻り、それをサンプル推定値に変換する方法を見てみましょう。推定値を計算するために、μ をサンプル平均に置き換えました:
n1i∑(Xi−μ)2⇒n1i∑(Xi−Xˉ)2
これがバイアスが生じる理由です。小さいサンプルの平均(Xˉ)は母集団の平均(μ)よりもこれらのサンプルに近くなります。以下の例でより直感的に理解できます。
図 1 は、中心点(すなわち平均)が 0 の 100 個のランダムな点の例を示しています。その中から 5 つの点がランダムに選ばれ、これらの平均は黒いバツで示されています。この 5 つのサンプルの平均は、これら 5 つのサンプルに最も近い点です。定義により、サンプルの平均は母集団の平均よりもサンプルに近くなります。したがって:
n1i∑(Xi−Xˉ)2<n1i∑(Xi−μ)2
我々の推定値は X の分散を過小評価します!

図1:バイアスの概念図。 これは平均が(0,0)の100個のランダムな点の図です。図中から5つの点がランダムに選ばれ、強調表示されています。黒いバツはこの5つのランダムな点の平均を示しています。
実際、上記の Python シミュレーションを繰り返し、サンプル平均の代わりに 0(母集団の平均)を使用すると:
得られるサンプル分散の平均は 1 になります:
母集団の平均がわかれば、サンプルのセットから分散の無偏推定値を得ることができます。しかし実際には、母集団の平均はわかりません。幸いなことに、我々はバイアスを定量化し、修正することができます。
バイアスの定量化#
これまでのところ、σ^2 が母集団分散の有偏推定であることを見てきました。我々は複数のサンプルをシミュレーションして σ^2 を得て、その平均値を取ることでこれを発見しました。シミュレーションの結果は次のように示しています:
E[σ^2]=σ2
今、我々はシミュレーションを取り除き、E[σ^2] の正確な値を計算します。展開することでこれを実現できます。次のように始めます:
E[σ^2]=E[n1i∑(Xi−Xˉ)2]
Xˉ=μ−(μ−Xˉ) と置くことができるので、展開すると次のようになります:
E[σ^2]=E[n1i∑((Xi−μ)−(Xˉ−μ))2]
いくつかの代数を使って、平方を展開できます:
E[σ^2]=E[n1i∑((Xi−μ)2−2(Xˉ−μ)(Xi−μ)+(Xˉ−μ)2)]=E[n1i∑(Xi−μ)2−2(Xˉ−μ)n1i∑(Xi−μ)+n1i∑(Xˉ−μ)2]=E[n1i∑(Xi−μ)2−2(Xˉ−μ)n1i∑(Xi−μ)+(Xˉ−μ)2]
ここで注目すべきは:
n1i∑(Xi−μ)=n1i∑Xi−n1i∑μ=n1i∑Xi−μ=Xˉ−μ
これにより:
E[σ^2]=E[n1i∑(Xi−μ)2−2(Xˉ−μ)2+(Xˉ−μ)2]=E[n1i∑(Xi−μ)2−(Xˉ−μ)2]=E[n1i∑(Xi−μ)2]−E[(Xˉ−μ)2]
ここでのポイントは:
E[n1i∑(Xi−μ)2]=σ2
これは次のことを意味します:
E[σ^2]=σ2−E[(Xˉ−μ)2]
項 E[(Xˉ−μ)2] はサンプル平均の分散です。Bienaymé の定理により、これは次のように等しいことがわかります:
E[(Xˉ−μ)2]=n1σ2
これにより、次のようになります:
E[σ^2]=(1−n1)σ2=(1−51)×1=0.8
我々の Python シミュレーションを思い出してください;サンプル数は n=5 で、真の分散は σ2=1 で、推定分散は σ^2=0.8 です。もし n と σ2 を上記の公式に代入すると、有偏な答えが得られます:
E[σ^2]=(1−n1)σ2=(1−51)×1=0.8
無偏推定量#
今、我々は E[σ^2] の正確な値を知っているので、σ^2 を修正して σ2 の無偏推定値にする方法を考えます。
修正項は:
n−1n
演算を通じて、次のことがわかります:
E[n−1nσ^2]=n−1nE[σ^2]=n−1n(1−n1)σ2=n−1n(1−n1)σ2=n−1n−1σ2=σ2
したがって、サンプルのセットから得られる σ2 の無偏推定値は:
n−1nσ^2=n−1nn1i∑(Xi−Xˉ)2=n−11i∑(Xi−Xˉ)2
無偏加重推定量#
今、上記の内容をサンプル加重の状況に拡張します。加重サンプル平均は:
Xˉ=∑iwi1i∑wiXi
加重分散は:
σ^2=∑iwi1∑iwi(Xi−Xˉ)2
以前と全く同じ展開プロセスに従って、次のように得られます:
E[σ^2]=σ2−E[(Xˉ−μ)2]
平均値の分散は:
E[(Xˉ−μ)2]=var(Xˉ)=var(∑wi1∑wiXi)=(∑wi)21∑var(wiXi)=(∑wi)21∑wi2var(Xi)=(∑wi)2∑wi2σ2
varは分散を計算するためのものです。
これにより、次のようになります:
E[σ^2]σ2=σ2−(∑wi)2∑wi2=(1−(∑wi)2∑wi2)σ2
バイアス修正項は:
b=(∑wi)2−∑wi2(∑wi)2
これは、分散の無偏加重推定値が:
bσ^2 であることを意味します。
Pandas の指数加重分散を再現する#
現在、Pandas の指数加重分散を再現するために必要なすべてのツールを持っています。
import numpy as np
import pandas as pd
N = 1000
# フェイクデータを作成
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
# 重み
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
これにより、次のような DataFrame が得られます:
