分散や標準偏差の分母はnかn-1か?シミュレーションを交えて説明

R

統計学を勉強していくと、テキストによって、標準偏差や分散の分母がnだったり、n-1だったりして混乱しますよね。結論を言うと

  • 目の前にある標本がどの程度ばらついているか表現したい場合はnで割る
  • 標本から母集団の分散を「推定」する場合はn-1で割った方が推定精度が高い

ということです。この理由や使い分けについてモヤモヤしている方は、まずこちらをご覧ください。

統計学 : 標準偏差や分散はなぜnで割ったりn-1で割ったりするのか
標準偏差や分散の計算って、nで割ったり、n-1で割ったり、よく分からなかったりしませんか? 結論から言うと、ばらつきの表現と言う意味での分散の定義はデータ数nで割るのが正しいのだけれど、標本から母集団の分散を「推定」する場合はn-1で割った方が妥当だということです。

このページでは、R言語を用いて、乱数を用いたシミュレーションを行い、その結果を交えながら、nなのかn-1なのかの説明をしています。

実験内容

平均0、標準偏差1の正規分布に従う乱数からn個のデータをサンプリング(\(x_i, i=1…n\))して、以下の2つの分散(標本分散と不偏分散)を計算します。この実験をN回実施して両者の違いを確認してみます。

\(x\sim N(0, 1^2)\)

\(\bar{x}=\frac{1}{n}\Sigma_{i=1}^n{x_i}\)

標本分散: \(\sigma_1^2 = \frac{1}{n}\Sigma_{i = 1}^n(x_i-\bar{x})^2\)

不偏分散: \(\sigma_2^2 = \frac{1}{n-1}\Sigma_{i = 1}^n(x_i-\bar{x})^2\)

 

nについては、n = 3(違いが現れやすいケース)とn = 100(違いが現れにくいケース)の2ケース、繰り返し数Nは両者とも10万回としました。

結果

n = 3の場合

3個の乱数から標本分散、不偏分散を計算する作業を10万回行うプログラムをR言語で書いて試してみます。

ライブラリとしてtidyverseをロードします。tidyverseはデータの整形、グラフ化、文字列操作など統計学、データサイエンスの分野で必要な操作を使いやすくまとめたパッケージです。

library(tidyverse)

次にtibble表示の桁数を設定しておきます。tibble型はdata.frame型の拡張版でtidyverseパッケージでよく使われるデータ型です。デフォルトでは表示の桁数が有効数字3桁なので、ここでは5桁に修正しておきます。

# tibble表示の桁数設定
tibble_opt <- list(
    "pillar.sigfig" = 5
)
options(tibble_opt)
getOption("pillar.sigfig")

次に乱数を発生させます。ここでは、平均(mean) = 0, 分散(\(sd^2 = 1^2\))の正規分布に従う乱数を3×10万個発生させて、そのデータを3列×10万行のdara.frameに代入しています。

n <- 3
N <- 100000

# 乱数の発生
set.seed(0)
x <- rnorm(n * N, mean = 0, sd = 1 ) %>%
    matrix(ncol = n) %>%
    as.data.frame()

標本分散、不偏分散のいずれを計算するにしても、偏差平方和を計算する必要がありますので、関数fun()として準備します。

\(fun(x) = \Sigma_{i = 1}^n(x_i – \bar{x})^2\)

# 偏差平方和計算用の関数(xx : データ)
fun <- function(xx){
    sum((xx - mean(xx))^2)
}

xの各行について、偏差平方和yを求め、標本分散(y1)と不偏分散(y2)を求めます。

# xの各行について分散を計算
y <- apply(x, MARGIN = 1, FUN = fun)
y1 <- y / n
y2 <- y / (n - 1)

標本分散(y1)と不偏分散(y2)はサンプリングしたxの値に応じてばらつきます。どのようにばらついているのか確認するため、ヒストグラムを書いてみます。ヒストグラムを書くためにxとy1,y2を一つにまとめて、整形しておきます。

# グラフ化しやすいように整形
x$y1 <- y1
x$y2 <- y2
x <- x %>%
    pivot_longer(cols =c(y1, y2), 
                 names_to = "method", 
                 values_to = "y") %>%
    mutate(method = ifelse(method == "y1", "標本分散", "不偏分散"))

整形後のデータはこんな感じ。V1〜V3が乱数で、methodの方法で計算した結果がyにまとめられています。

ヒストグラムを描くためのコードは以下の通りです。グラフを描くだけなら最初の2行だけで良いのですが、色を調整したり、軸を調整したり等のコードを書いているのでちょっと長くなっています。
# 計算された分散をグラフ化

ggplot(data = x) +
    geom_histogram(aes(x = y, fill = method),
                                  alpha = 0.5,
                                  position = "identity",
                                  binwidth = 0.2, boundary = 0) +
    labs(x = "標本分散 or 不偏分散", y = "個数", fill = "計算方法") +
    scale_x_continuous(breaks = seq(0, 6, by = 1)) +
    coord_cartesian(xlim = c(0, 6)) +
    theme_bw(base_family = "HiraKakuProN-W3") +
    scale_fill_manual(values = c("blue", "red"))

xの真の分散は1であるのに(標準偏差は1であるのに)、標本分散は0〜4くらいまでばらついています。不偏分散は標本分散よりやや高く0〜6くらいまでばらついています。真の分散は1であるのに、かなり広くばらつきますね。

10万個の標本分散、不偏分散のデータの平均値を求めてみます。標本分散と不偏分散の両方がyに入っているので、一旦、method(=標本分散 or 不偏分散)でグループ分けをしてから、平均の計算をしています。

# 計算された分散(N個)の平均

x %>%
    group_by(method) %>%
    summarise(mean = mean(y))
結果は以下の通り。
# A tibble: 2 × 2
method mea
<chr> <dbl>
1 標本分散 0.66509
2 不偏分散 0.99764
標本分散を10万回計算したときの平均値は….、不偏分散を10万回計算したときの平均値は….となります。標本分散は明らかに真の値とずれているのに対し、不偏分散は真の値にほぼ一致しています。回数をもっと大きくしていくと、標本分散は0.66666….、不偏分散は1に収束します。

手元にあるデータから、母集団の分散(標準偏差)を推定するにあたり、推定である以上、真の値には一致はしないのですが、不偏分散の方が近しい値を算出する可能性が高いのです。不偏分散を使用するのはここに理由があります。

なお、標本分散、不偏分散とも確率的にばらつくので、データによっては標本分散の方が、真の値に近い場合もあり得ます。あくまでも、この推定方法で何回も行った場合に、不偏分散の方が平均的に真の値に近い値が得られるということです。

n = 100の場合

ソースコードはn <- 3となっていた部分のみ書き換えるだけなので、省略します。

n <- 100    # n <- 3の部分を書き換える

グラフは以下のようになります。

また、100個のデータから標本分散、不偏分散を計算する作業を10万回繰り返した場合のそれぞれの値の平均値は以下のようになります。

# A tibble: 2 × 2
method mean
<chr> <dbl>
1 標本分散 0.99024
2 不偏分散 1.0002

不偏分散は真の値である1に近く、標本分散は真の値の\(\frac{n-1}{n} = \frac{99}{100}\)倍に近い値になっています。

\(\chi^2\)分布との関係

標本分散にしても、不偏分散にしても分母が違うだけで分子は同じ値(偏差平方和)です。

\(x\sim N(0, 1^2)\)

\(\bar{x}=\frac{1}{n}\Sigma_{i=1}^n{x_i}\)

分子(偏差平方和): \(Z =  \Sigma_{i = 1}^n(x_i-\bar{x})^2\)

偏差平方和(Z)の分布について考えてみます。

ヒストグラムだと高さがNによって変わってきてしまうので、縦軸を密度で表します(ヒストグラムの棒の総面積が1になるように無次元化します)。n = 3,100の時のヒストグラム(密度)と自由度2, 99の\(\chi^2\)分布の確率密度関数を重ねて描くと以下のようになります。

分散の分子である偏差平方和と\(\chi^2\)分布が密接に関係があるということです。また、\(\chi^2\)分布の自由度が和をとった個数nではなく、n-1であるところも興味深いところです。不偏分散の分母の値と偏差平方和が従う分布の自由度が同じn-1であることは、決して偶然ではなく、根本は同じ理由で説明されます。このため、不偏分散の分母がn-1である理由を、自由度が一つ減るからと説明されたりもします。

\(\chi^2\)分布については、こちらでも記事を書いていますのでぜひ参照ください。

統計学:χ2乗分布(カイ2乗分布)の意味
統計を学びたての頃、正規分布は何となくわかり始めた後に出てくるのが、\( \chi^2 \)分布。これ何?どうやって使うの?そもそも何て読むの?ってなりますよね。始めはとっつきにくい\(\chi^2\)分布ですが、分散の検定、適合度検定など...

まとめ

標本分散、不偏分散の意味をRによるシミュレーションで説明してみました。また、分散計算で出てくる偏差平方和と\(\chi^2\)分布の関係についてもシミュレーションで試してみました。

今回、Rを用いてシミュレーションを行いました。Rは統計計算に特化していると言われることも多いプログラミング言語ですが、グラフを作るのも得意ですし、今回のようにモンテカルロシミュレーションを行ったりもできます。また、公式サイトに登録されているパッケージを利用すれば、機械学習、ディープラーニング、微分方程式などの数値計算を行ったり、Webスクレイピングなどもできます。Rはデータに関わるさまざまな問題に対応できるプログラミング言語です。しかも無料(笑)。これを機にRに触れてみてはいかがでしょうか?

R言語とは?Rでできること。現役Rユーザーが語る。
R言語は、データ分析、統計解析に特化した言語であるとよく言われます。しかし「データ分析」「統計解析」しかできないと考えてしまっては損をしています。C/C++のような汎用のプログラミング言語同様の制御構造を備えていますし、CRANで公開されているパッケージを利用すれば、はるかに簡単に望みの機能を実装することができる言語です。
RとKeras(TensorFlow)でディープラーニング
Keras/TensorFlowを使えばRでもディープラーニングを行うことができます。しかも、とても簡単に。Keras/TensorFlowのインストールから、簡単な例題までを、はじめてディープラーニングにトライする方に向けてまとめています。
競馬予想AIの作り方 無料ツールのみで作成する方法
競馬AIを作る方法について説明しています。競馬AIを作るために使える無料ツール「R」と「RStudio」、そして競馬AI作成の中核となる「データ収集」「AIモデルの作成」といった作業についても具体的に紹介しています。