CUBE SUGAR CONTAINER

技術系のこと書きます。

統計: 二つのグループの平均と分散を合成する

例えば、あるグループ A と B が別々にテストを受けたとする。 それぞれのグループの人数と平均点、そして分散は分かっているとしよう。 このとき、グループ A と B を合わせた全体での平均や分散は計算することができるだろうか?

結論から言うと、これはできる。 今回は、その手順について書いてみることにする。 また、同時に Python を使ってサンプルデータを生成したり計算の裏付けをしてみよう。

使った環境は次の通り。

$ sw_vers 
ProductName:    Mac OS X
ProductVersion: 10.11.6
BuildVersion:   15G1212
$ python --version
Python 3.5.2

題材とするデータを生成する

平均や分散の計算を楽にするために、まずは NumPy をインストールしておこう。

$ pip install numpy

そして Python の REPL を起動する。

$ python

起動したら NumPy をインポートしよう。

>>> import numpy as np

そして、まずはグループ A を生成する。 パラメータは適当だけど、点数は 40 から 90 の範囲で 10 人分作っておく。

>>> A = np.random.randint(40, 90, 10)
>>> A
array([57, 67, 74, 79, 82, 53, 80, 46, 74, 47])

上記の平均と分散はそれぞれ 65.9176.09 になった。

>>> A.mean()
65.900000000000006
>>> A.var()
176.09

同じようにグループ B も生成する。 こちらは平均が 72.466... で分散が 60.9155... になった。

>>> B = np.random.randint(55, 85, 15)
>>> B
array([83, 77, 77, 59, 67, 57, 77, 71, 68, 77, 72, 76, 84, 64, 78])
>>> B.mean()
72.466666666666669
>>> B.var()
60.915555555555549

そして、上記を合わせたグループについても計算しておこう。 便宜的に、ここではこれをグループ C と呼ぶことにする。

>>> C = np.r_[A, B]
>>> C
array([57, 67, 74, 79, 82, 53, 80, 46, 74, 47, 83, 77, 77, 59, 67, 57, 77,
       71, 68, 77, 72, 76, 84, 64, 78])

グループ C の平均と分散は次のようになった。 今回は、この値をグループ A と B の平均と分散とデータ点数からだけで求めるのが最終的な目的となる。

>>> C.mean()
69.840000000000003
>>> C.var()
117.3344

平均を合成する

平均の合成はすごく簡単。 まず、それぞれのグループの平均値に人数をかけると各グループの点数の合計が得られる。 あとは、それをまとめてから全体の人数で割れば合成した平均が得られる。

数式にすると、こんな感じ。

 \bar{x_C} = \frac { \bar{x_A} \times n_A + \bar{x_B} + n_B }{ n_A + n_B }

ここで  \bar{x_A} \bar{x_B} は各グループの平均値を表している。 そして  n_A n_B は各グループの人数を表している。

実際に Python を使って計算してみよう。

>>> C_average = (A.mean() * 10 + B.mean() * 15) / (10 + 15)
>>> C_average
69.840000000000003

上記の値は、実際にグループをまとめて計算した平均値と一致している。

>>> C.mean()
69.840000000000003

分散を合成する

分散の合成については、もうちょっと厄介な計算が必要になる。 また、分散の計算方法についても詳しく知っておかないといけない。

分散について

まず、最も基本的な分散の計算式は次のようになる。 分散というのは、各要素から平均値を引いたもの (偏差) を二乗して足し合わせて、それをデータ点数で割ることで得られる。

 s^{2} = \frac{1}{n} \displaystyle \sum_{i = 1}^{n} (x_i - \bar{x})^{2}

上記で  \bar{x} x の平均値となっているので、計算式で書くとこう。

 \bar{x} = \frac{1}{n} \displaystyle \sum_{i = 1}^{n} x_i

また、分散の公式は次のように式変形できる。 この式は、ようするに各要素を二乗して足し合わせてデータ点数で割ったものから、平均値の二乗を引いて計算することができる、ということ。

 s^{2} = \frac{1}{n} \displaystyle \sum_{i = 1}^{n} (x_i - \bar{x})^{2} = \frac{1}{n} \displaystyle \sum_{i = 1}^{n} x_i^{2} - \bar{x}^{2}

また、上記の「二乗したものを足し合わせてデータ点数で割る」という処理に着目してみよう。 これは言い換えると二乗したものの平均を計算していることになる。 バー記号は平均を表現する。

 \frac{1}{n} \displaystyle \sum_{i = 1}^{n} x_i^{2} = \bar{x^{2}}

つまり、分散は次のような式でも表現できることが分かった。 ようするに、分散は各要素を二乗した平均値 ( \bar{x^{2}})から各要素の平均値の二乗 ( \bar{x}^2) を引くことで計算できる。

 s^{2} = \bar{x^{2}} - \bar{x}^2

分散を合成する手順

さて、ここまでの説明で分散の計算方法は分かった。 改めて、求めなければいけないものの式を書いてみよう。

 s_C^{2} = \bar{x_C^{2}} - \bar{x_C}^2

上記の中で  \bar{x_C} については、既に平均の合成をしたところで計算が済んでいる。 問題は  \bar{x_C^{2}} の方で、これはまだ未知の値なので計算しなきゃいけない。

しかし  \bar{x_C^{2}} は、どうすれば求まるだろうか? 実は、これも先ほどの分散の公式から導くことができる。

 s^{2} = \bar{x^{2}} - \bar{x}^2

上記の中で、各グループ毎の  s^{2} \bar{x} は既に分かっている。 つまり、式を移行して  \bar{x^{2}} を求めるようにできるということ。

 \bar{x^{2}} = s^{2} + \bar{x}^2

次のように、各グループごとに二乗した平均値を求めることができる。

 \bar{x_A^{2}} = s_A^{2} + \bar{x_A}^2

 \bar{x_B^{2}} = s_B^{2} + \bar{x_B}^2

各グループごとの二乗した平均値を求めることができれば、あとは簡単で最初にやった平均の合成をすれば良い。

 \bar{x_C^{2}} = \frac{ \bar{x_A^{2}} \times n_A + \bar{x_B^{2}} \times n_B }{ n_A + n_B }

上記が計算できれば合成した分散を計算するのに必要な要素が揃うので、あとは定義通りにやるだけ。

 s_C^{2} = \bar{x_C^{2}} - \bar{x_C}^2

確かめてみる

それでは、上記の式を Python で実行して確かめてみよう。

まずはグループ A の二乗した平均値から求める。

>>> A_square_average = A.var() + A.mean() ** 2
>>> A_square_average
4518.9000000000005

上記は、次の式に対応している。

 \bar{x_A^{2}} = s_A^{2} + \bar{x_A}^2

そして、同じようにグループ B の二乗した平均値を求める。

>>> B_square_average = B.var() + B.mean() ** 2
>>> B_square_average
5312.333333333333

対応する式は次の通り。

 \bar{x_B^{2}} = s_B^{2} + \bar{x_B}^2

そして、グループをまとめたときの二乗した平均値を求める。

>>> C_square_average = (A_square_average * 10 + B_square_average * 15) / (10 + 15)
>>> C_square_average
4994.96

対応する式はこちら。

 \bar{x_C^{2}} = \frac{ \bar{x_A^{2}} \times n_A + \bar{x_B^{2}} \times n_B }{ n_A + n_B }

上記で必要な材料は揃ったので、あとは定義通りに計算するだけ。

>>> C_var = C_square_average - C_average ** 2
>>> C_var
117.33439999999973

実際に計算した値と比較してみよう。

>>> C.var()
117.3344
>>> C_var
117.33439999999973

微妙に誤差が出てるけど、バッチリ合ってるね!

まとめ

  • 二つのグループの平均と分散とデータ点数だけが分かっている状況であってもグループ全体の平均と分散は計算できる