ラベル matplotlib の投稿を表示しています。 すべての投稿を表示
ラベル matplotlib の投稿を表示しています。 すべての投稿を表示

2021年5月8日土曜日

macOSのPython3にNumPy, SciPy, matplotlibをインストールする.

macOSに付属していたPython 2では NumPy, SciPy, matplotlib がプリインストールされていました.一方で,Python 3にはそれらがインストールされていないため,別途インストールが必要です.

インストールする際には,ターミナルで以下のようなコマンドを実行します.

まずは,NumPyのインストールです.

% python3 -m pip install --upgrade numpy

python3 -m pip install コマンドを使って,NumPyをインストールします.
--upgrade オプションをつけると,古いバージョンがインストールされている場合には,新しいバージョンに更新されます.
以下は,上記のコマンドを実行した結果です.

Requirement already satisfied: numpy in /usr/local/lib/python3.9/site-packages (1.20.2)

Collecting numpy

  Downloading numpy-1.21.5-cp39-cp39-macosx_10_9_x86_64.whl (17.0 MB)

     |████████████████████████████████| 17.0 MB 915 kB/s 

Installing collected packages: numpy

  Attempting uninstall: numpy

    Found existing installation: numpy 1.20.2

    Uninstalling numpy-1.20.2:

      Successfully uninstalled numpy-1.20.2

Successfully installed numpy-1.21.5

次にSciPyのインストールです.実行するコマンドは以下です.

% python3 -m pip install --upgrade scipy

すると,以下のようにインストールが実行されます.

Collecting scipy

  Downloading scipy-1.7.3-cp39-cp39-macosx_10_9_x86_64.whl (33.2 MB)

     |████████████████████████████████| 33.2 MB 1.2 MB/s 

Requirement already satisfied: numpy<1.23.0,>=1.16.5 in /usr/local/lib/python3.9/site-packages (from scipy) (1.21.5)

Installing collected packages: scipy

Successfully installed scipy-1.7.3

最後にmatplotlibのインストールです.実行するコマンドは以下です.

python3 -m pip install --upgrade matplotlib

そうすると,インストールが始まります.

Requirement already satisfied: matplotlib in /usr/local/lib/python3.9/site-packages (3.4.2)

Collecting matplotlib

  Downloading matplotlib-3.5.1-cp39-cp39-macosx_10_9_x86_64.whl (7.3 MB)

     |████████████████████████████████| 7.3 MB 1.2 MB/s 

Collecting fonttools>=4.22.0

  Downloading fonttools-4.28.5-py3-none-any.whl (890 kB)

     |████████████████████████████████| 890 kB 1.2 MB/s 

Requirement already satisfied: cycler>=0.10 in /usr/local/lib/python3.9/site-packages (from matplotlib) (0.10.0)

Requirement already satisfied: packaging>=20.0 in /usr/local/lib/python3.9/site-packages (from matplotlib) (20.9)

Requirement already satisfied: pyparsing>=2.2.1 in /usr/local/lib/python3.9/site-packages (from matplotlib) (2.4.7)

Requirement already satisfied: python-dateutil>=2.7 in /usr/local/lib/python3.9/site-packages (from matplotlib) (2.8.1)

Requirement already satisfied: numpy>=1.17 in /usr/local/lib/python3.9/site-packages (from matplotlib) (1.21.5)

Requirement already satisfied: pillow>=6.2.0 in /usr/local/lib/python3.9/site-packages (from matplotlib) (8.2.0)

Requirement already satisfied: kiwisolver>=1.0.1 in /usr/local/lib/python3.9/site-packages (from matplotlib) (1.3.1)

Requirement already satisfied: six in /usr/local/lib/python3.9/site-packages (from cycler>=0.10->matplotlib) (1.15.0)

Installing collected packages: fonttools, matplotlib

  Attempting uninstall: matplotlib

    Found existing installation: matplotlib 3.4.2

    Uninstalling matplotlib-3.4.2:

      Successfully uninstalled matplotlib-3.4.2

Successfully installed fonttools-4.28.5 matplotlib-3.5.1


ちなみに,Seabornもデフォルトではインストールされていないので,別途インストールするには,以下のようになります.

hide@MBP ~ % pip3 install seaborn

Collecting seaborn

  Using cached seaborn-0.11.2-py3-none-any.whl (292 kB)

Requirement already satisfied: pandas>=0.23 in /usr/local/lib/python3.9/site-packages (from seaborn) (1.2.4)

Requirement already satisfied: numpy>=1.15 in /usr/local/lib/python3.9/site-packages (from seaborn) (1.21.5)

Requirement already satisfied: matplotlib>=2.2 in /usr/local/lib/python3.9/site-packages (from seaborn) (3.5.1)

Requirement already satisfied: scipy>=1.0 in /usr/local/lib/python3.9/site-packages (from seaborn) (1.7.3)

Requirement already satisfied: python-dateutil>=2.7 in /usr/local/lib/python3.9/site-packages (from matplotlib>=2.2->seaborn) (2.8.1)

Requirement already satisfied: pillow>=6.2.0 in /usr/local/lib/python3.9/site-packages (from matplotlib>=2.2->seaborn) (8.2.0)

Requirement already satisfied: kiwisolver>=1.0.1 in /usr/local/lib/python3.9/site-packages (from matplotlib>=2.2->seaborn) (1.3.1)

Requirement already satisfied: packaging>=20.0 in /usr/local/lib/python3.9/site-packages (from matplotlib>=2.2->seaborn) (20.9)

Requirement already satisfied: pyparsing>=2.2.1 in /usr/local/lib/python3.9/site-packages (from matplotlib>=2.2->seaborn) (2.4.7)

Requirement already satisfied: cycler>=0.10 in /usr/local/lib/python3.9/site-packages (from matplotlib>=2.2->seaborn) (0.10.0)

Requirement already satisfied: fonttools>=4.22.0 in /usr/local/lib/python3.9/site-packages (from matplotlib>=2.2->seaborn) (4.28.5)

Requirement already satisfied: six in /usr/local/lib/python3.9/site-packages (from cycler>=0.10->matplotlib>=2.2->seaborn) (1.15.0)

Requirement already satisfied: pytz>=2017.3 in /usr/local/lib/python3.9/site-packages (from pandas>=0.23->seaborn) (2021.1)

Installing collected packages: seaborn

Successfully installed seaborn-0.11.2


-----
pipコマンドを使ってインストールする際に以下のような警告が出る場合があります.

WARNING: You are using pip version 21.0.1; however, version 21.3.1 is available.

You should consider upgrading via the '/opt/homebrew/opt/python@3.9/bin/python3.9 -m pip install --upgrade pip' command.


/opt/homebrew/opt/python@3.9/bin/は,Python3(この場合は ver. 3.9)のインストールされている場所なので,環境によって異なります.
警告はpipを最新にするようにというないようなので,以下のように実行しておきます.

% /opt/homebrew/opt/python@3.9/bin/python3.9 -m pip install --upgrade pip

Requirement already satisfied: pip in /opt/homebrew/lib/python3.9/site-packages (21.0.1)

Collecting pip

  Downloading pip-21.3.1-py3-none-any.whl (1.7 MB)

     |████████████████████████████████| 1.7 MB 4.7 MB/s 

Installing collected packages: pip

  Attempting uninstall: pip

    Found existing installation: pip 21.0.1

    Uninstalling pip-21.0.1:

      Successfully uninstalled pip-21.0.1

Successfully installed pip-21.3.1


2019年6月8日土曜日

[Jupyter Notebook] matplotlibのインライン展開

matplotlibで描画をしようとした際に,インライン展開されない場合は,以下のように
%matplotlib inline
と書くことで,インライン展開されます.

例えば,matplotlibでグラフを示そうとしているのに,図が現れず
<Figure size 640x480 with 1 Axes>
というような表示のみが返ってきているときはインライン展開されていません.このような時は描画する命令の前に,上記のように
%matplotlib inline
と書くとグラフが表示されるようになるはずです.

2019年5月8日水曜日

[Statistics] III 推定

Pythonで学ぶあたらしい統計学の教科書のまとめやコードを実行してみた際のメモです.

母数(母集団のパラメタ)を推定します.例えば,母集団分布に正規分布を仮定した場合,母数がわかれば母集団分布が推定できます.
以下の例では,単純な点推定の後に推定誤差を加味した区間推定の例を示します.

まずはターミナルからPython3を立ち上げて,必要なライブラリの読み込みを行います.
% python3
数値計算用ライブラリ

>>> import numpy as np

>>> import pandas as pd
>>> import scipy as sp
>>> from scipy import stats
グラフ描画用ライブラリ

>>> from matplotlib import pyplot as plt

import seaborn as sns
sns.set()
>>> import seaborn as sns
>>> sns.set()

分析の対象となる魚の体長を測定したデータを読み込みます.

>>> fish = pd.read_csv("3-7-1-fish_length.csv")["length"]

>>> fish
0    4.352982
1    3.735304
2    5.944617
3    3.798326
4    4.087688
5    5.265985
6    3.272614
7    3.526691
8    4.150083
9    3.736104
Name: length, dtype: float64

点推定
点推定:母数(母集団分布のパラメタ)をある一つの値として指定する推定方法.母平均を推定する場合は,標本平均を推定量として使う.

標本から平均値を計算するだけで,推定が完了となります.標本平均は不偏性も一致性も満たす統計量です.望ましい性質を持つ統計量であるため,母平均の推定値としては標本平均が使われます.
>>> mu = sp.mean(fish)
>>> mu
4.187039324504523
(ここでは,Scipy.meanを使っていますが,Scipyのmean関数は廃止されるとのことなので,今後は以下のようにNumpyを使う必要があるかもしれません).
>>> mu = np.mean(fish)
>>> mu
4.187039324504523

点推定では,標本平均が4.187だったので,母平均も4.187だろうと推定します.

実用上多く使われる母平均の推定の方法を中心に説明します.母分散の推定に関しても同じように標本から計算された統計量を使用します.母分散の点推定値としては,不偏分散を用います.
>>> sigma_2 = sp.var(fish, ddof = 1)
>>> sigma_2
0.6803017080832622
(ここでは,上記の例と同様に,Scipyのvar関数は廃止されるとのことなので,今後は以下のようにNumpyを使う必要があるかもしれません)
>>> sigma_2 = np.var(fish, ddof = 1)
>>> sigma_2
0.6803017080832622

区間推定
区間推定:推定値に幅を持たせた推定方法のこと.推定値の幅の計算には確率の考え方を用います.
幅を持たせることで,推定誤差を加味することができます.推定誤差が小さければ,区間推定の幅は狭くなります.サンプルサイズが大きくても,区間推定の幅は狭くなります.

信頼係数
信頼係数:区間推定の幅における信頼の度合いを確率で表現したもの.
信頼区間:ある信頼係数を満たす区間のこと.

同じデータを対象としている場合は,信頼係数が大きいほど信頼区間の幅は広くなります.信頼の度合いを上げようと思うと,安全のために幅を大きく取らざるを得なくなります.

信頼限界
信頼限界:信頼区間の下限値(下限信頼限界).・上限値(上限信頼限界)のこと.

信頼区間の計算方法
t値はt分布に従います.
t値 = (標本平均 - 母平均)/ 標準偏差
区間推定の際は,t分布のパーセント点(ある確率になる基準値)を用います.
信頼係数を95%とするならば,t分布における2.5%点と97.5%点を計算します.t分布に従う変数がこの区間に入る確率は95%ということになるので,この区間を使います.

区間推定
区間推定に必要となる情報は,自由度(サンプルサイズ-1),標本平均,標準誤差の3つです.標本平均は上記でに計算済みなので,残りの2つを求めます.

まずは自由度を求めます.自由度は単にサンプルサイズから1を引くだけです.
>>> df = len(fish) - 1
>>> df
9

続いて標準誤差(standard error - se)を求めます.
>>> sigma = sp.std(fish, ddof = 1)
>>> se = sigma / sp.sqrt(len(fish))
>>> se
0.2608259396768776
(Scipyのstd関数とsqrt関数の代わりにNumpyを使用すると,以下のようになります.)
>>> sigma = np.std(fish, ddof = 1)
>>> se = sigma / np.sqrt(len(fish))
>>> se
0.2608259396768776

ここまでで,自由度,標本平均,標準誤差が求められたので,これらの値を使って信頼区間を計算します.
信頼区間の計算には stats.t.interval 関数を用います.引数には信頼係数 alpha,自由度 df,標本平均 loc,標準誤差 scaleを指定します.出力の1つ目が下側信頼限界,2つ目が上側信頼限界です.
>>> interval = stats.t.interval(alpha = 0.95, df = df, loc = mu, scale = se)
>>> interval
(3.597010056835825, 4.777068592173221)
この結果より信頼区間は3.597から4.777となることがわかります.

信頼区間の求め方の詳細
上記の例のように,Pythonでは関数を使うことで簡単に信頼区間を計算することが可能です.この関数の中の計算手順は以下のようになります.
  1. ある自由度を持つt分布における97.5%点を計算する.
    1. t分布における97.5%点を$t_{0.975}$と表記する.
    2. t分布は平均に対して左右対称なので,2.5%点は$-t_{0.975}$となる.
    3. t分布に従う変数が$t_{-0.975}$以上,$t_{0.975}$以下になる確率は95%である.
      1. この時の95%が信頼係数となる.
  2. 標本平均 $t_{-0.975} \times$標準誤差 が下側信頼限界となる.
  3. 標本平均 $t_{0.975} \times$標準誤差 が上側信頼限界となる.
まずは,t値の計算式は
t値$= \cfrac{{\hat{\mu}} - \mu}{\hat{se}}$
t値が$t_{-0.975}$以上$t_{0.975}$以下になる確率は95%です.
$P \left( t_{-0.975} \leq \cfrac{{\hat{\mu}} - \mu}{\hat{se}} \leq t_{0.975} \right)$ = 95%

これを母平均$\mu$について解くと以下のようになります.
$P ( {\hat{mu}} - t_{-0.975} \times {\hat{se}} \leq \mu \leq {\hat{mu}} + t_{0.975} \times {\hat{mu}}$ = 95%

これをPythonで確認すると以下のようになります.
>>> t_975 = stats.t.ppf(q = 0.975, df = df)
>>> t_975
2.2621571627409915
>>> lower = mu - t_975 * se
>>> lower
3.597010056835825
>>> upper = mu + t_975 * se
>>> upper
4.777068592173221

信頼区間の幅を決める要素
標本における分散が大きければ
データは平均値から離れている → 平均値をあまり信用できない
ことになるので,信頼区間の幅が大きくなります.
確認のために,標本標準偏差を10倍にしてから95%信頼区間を計算してみます.
>>> se2 = (sigma * 10) / sp.sqrt(len(fish))
>>> stats.t.interval(alpha = 0.95, df = df, loc = mu, scale = se2)
(-1.7132533521824618, 10.087332001191509)
Numpyを使う場合は以下のようになります.

>>> se2 = (sigma * 10) / np.lib.scimath.sqrt(len(fish))

>>> stats.t.interval(alpha = 0.95, df = df, loc = mu, scale = se2)
(-1.7132533521824618, 10.087332001191509)

信頼区間の幅が広いということは,
真の母平均がどこに位置しているのかよくわからない
ということだと解釈すれば直感的な理解と一致します.

サンプルサイズが大きくなると,標本平均の信頼度が上がるため,信頼区間は狭くなります.これをPythonで計算してみます.
例としてサンプルサイズを10倍にしてみます.サンプルサイズが大きくなると自由度が大きくなり,標準誤差が小さくなることに注意します.
>>> df2 = (len(fish) * 10) - 1
>>> se3 = sigma / sp.sqrt(len(fish) * 10)
>>> stats.t.interval(alpha = 0.95, df = df2, loc = mu, scale = se3)
(4.0233803082774395, 4.350698340731607)
Numpyを使う場合は以下のようになります.

>>> df2 = (len(fish) * 10) - 1

>>> se3 = sigma / np.lib.scimath.sqrt(len(fish) * 10)
>>> stats.t.interval(alpha = 0.95, df = df2, loc = mu, scale = se3)
(4.0233803082774395, 4.350698340731607)

全く同じデータである場合は,信頼係数が大きいほど,安全を見込んで信頼区間の幅は広く取られます.99%信頼区間は以下のように計算されます.
>>> stats.t.interval(alpha = 0.99, df = df2, loc = mu, scale = se)
(3.5020046533703786, 4.872073995638668)
95%信頼区間よりも幅が広くなっています.

区間推定結果の解釈
信頼係数95%の"95%"は以下のようにして得られます.
  1. 真の母集団分布から標本を抽出する.
  2. 今回と同じやり方で95%信頼区間を計算する.
  3. この試行を複数回(たくさん)繰り返す.
  4. 全ての試行のうち,真の母数が信頼区間に含まれている割合が95%である.
このことをシミュレーション(試行回数:20000回)を通して確認します.
注意点は以下のとおりです.
  • 信頼区間が母平均 (4) を含んでいれば True を取る変数を準備します.
  • 試行回数は20000回なので,要素数20000の配列になります.
  • 得られるのは True か False のみなので,"type = bool"とします.

>>> be_included_array = np.zeros(20000, dtype = "bool")

>>> be_included_array
array([False, False, False, ..., False, False, False])

シミュレーションを実行してみます*

>>> np.random.seed(1)

>>> norm_dist = stats.norm(loc = 4, scale = 0.8)
>>> for i in range(0, 20000):
...     sample = norm_dist.rvs(size = 10)
...     df = len(sample) - 1
...     mu = sp.mean(sample)
...     std = sp.std(sample, ddof = 1)
...     se = std / sp.sqrt(len(sample))
...     interval = stats.t.interval(0.95, df, mu, se)
...     if(interval[0] <= 4 and interval[1] >= 4): 
...         be_included_array[i] = True

信頼区間が母平均(4)を含んでいた割合を求めます.およそ0.95となります.

>>> sum(be_included_array) / len(be_included_array)

0.948

* ScipyでなくNumpyで計算する場合は以下のようになります.なお,最終的に得られる値(0.948)はScipyでもNumpyでも一致します.
>>> np.random.seed(1)
>>> norm_dist = stats.norm(loc = 4, scale = 0.8)
>>> for i in range(0, 20000):
...     sample = norm_dist.rvs(size = 10)
...     df = len(sample) - 1
...     mu = np.mean(sample)
...     std = np.std(sample, ddof = 1)
...     se = std / np.lib.scimath.sqrt(len(sample))
...     interval = stats.t.interval(0.95, df, mu, se)
...     if(interval[0] <= 4 and interval[1] >= 4): 
...         be_included_array[i] = True

上記の事柄をJupyter Notebookで実行した結果はGitHubで見ることができます.

2019年5月7日火曜日

[Statistics] III 正規分布とその応用

Pythonで学ぶあたらしい統計学の教科書のまとめやコードを実行してみた際のメモです.

まずはライブラリをまとめてインポートします.
>>> import numpy as np
>>> import numpy as pd
>>> import scipy as sp
>>> from scipy import stats

>>> from matplotlib import pyplot as plt
>>> import seaborn as sns
>>> sns.set()


確率密度
正規分布の確率密度を計算します.
まずは関数を使わずにそのまま(?)処理をするコードを書いてみることにします.

正規分布の確率密度関数は以下のようになります.

$\mathcal{N} (x | \mu, \sigma^{2}) = \cfrac{1}{\sqrt{2 \pi \sigma^{2}}} \exp {\{ -\cfrac{(x - \mu)^{2}}{2 \sigma^{2}} \}}$


上記の式には円周率$\pi$と自然対数の底$e$の指数が含まれています(これらはScipyに含まれる).
円周率はScipyのsp.piという変数に格納されています.
>>> sp.pi
3.141592653589793

次は自然対数の底$e$はsp.expという変数に格納されています.
>>> sp.exp(1)
2.718281828459045
これは,$e$の1乗という意味です.

これらの変数を使って,正規分布の確率密度を計算してみます.例として平均4,分散0.64,標準偏差0.8の正規分布において,確率変数が3である時の確率密度($\mathcal{N}  (3 | 4, 0.8^{2})$)は以下のように計算できます.
>>> x = 3
>>> mu = 4
>>> sigma = 0.8
>>> 
>>> 1 / (sp.sqrt(2 * sp.pi * sigma**2)) * sp.exp(- ((x - mu)**2) / (2 * sigma**2))
0.22831135673627742

上記の計算は,scipy.statsの関数を使うことでも計算できます.
>>> stats.norm.pdf(loc = 4, scale = 0.8, x = 3)
0.2283113567362774

平均4, 標準偏差0.8を持つ正規分布のインスタンスを生成してからpdf関数を適用しても結果は同じになります.
>>> norm_dist = stats.norm(loc = 4, scale = 0.8)
>>> norm_dist.pdf(x = 3)
0.2283113567362774

標本がある値以下となる割合を求めるには
ある値以下となったデータの個数 ÷ サンプルサイズ
を計算すれば良いことになります.
例として,まず母集団分布が$\mathcal{N}  (x | 4, 0.8^{2})$である母集団からの標本抽出シミュレーションを実行してみます.サンプルサイズは100000としています.
>>> np.random.seed(1)
>>> simulated_sample = stats.norm.rvs(
...     loc = 4, scale = 0.8, size = 100000)
>>> simulated_sample
array([5.29947629, 3.51059487, 3.5774626 , ..., 4.06498025, 4.27523694,
       3.401955  ])

(例えば)標本が3以下となるデータの個数は比較演算子を使って計算します.
>>> sp.sum(simulated_sample <= 3)
10371
上記で得られた個数をサンプルサイズで割ると,約10.4%という値が得られます.
>>> sp.sum(simulated_sample <= 3) / len(simulated_sample)
0.10371

累積分布関数
確率変数$X$に対して,$x$を実数とするとき,下式のように表される$F(X)$を累積分布関数あるいは分布関数と呼びます.累積分布関数はある値以下となる確率を計算します.
$F(X) = P(X \leq z)$
累積分布関数を用いれば,上記で,標本がある値以下となる割合を数え上げる必要が無くなります.例として,上記の例(標本が3以下となる)の確率を計算するには
$P(X \leq 3) = \int_{-\infty}^{3} \cfrac{1}{\sqrt{2 \pi \sigma^{2}}} \exp \{- \cfrac{(x - \mu)^{2})}{2 \sigma^{2}} \} dx$
上記の計算は$- \infty \sim 3$までの確率密度を積分(足し合わせ)したものと解釈できます.
実装してみます.実装にはPythonの累積分布関数はstats.normのcdf関数(cdf: Cumulative Distribution Function)を用います.
母集団分布が$\mathcal{N}  (x | 4, 0.8^{2})$である時に,この確率分布から得られた確率変数が3以下の値となる確率を計算します.
>>> stats.norm.cdf(loc = 4, scale = 0.8, x = 3)
0.10564977366685535
上記の関数を使わない実装では約10.4%となっていましたが,ほぼ同じ値が得られています.
正規分布は平均に対して左右対称なので,データが平均値以下になる確率は50%と計算されます.
>>> stats.norm.cdf(loc = 4, scale = 0.8, x = 4)
0.5
このように,データを数え上げることなく,積分計算によって確率を計算できることが,母集団分布に正規分布を仮定したことのメリットになります.

下側確率とパーセント点
データがある値以下となる確率のことを下側確率と呼びます.下側確率は,上記の累積分布関数を用いることで得ることができます.
逆に,ある確率になる基準値のことをパーセント点(下側パーセント点)と呼びます.
パーセント点の計算はPythonのstats.normのppf関数(ppf: Percent Point Function)を用いることで計算可能です.
母集団分布が$\mathcal{N}  (x | 4, 0.8^{2})$であるときに,下側確率が2.5%となるパーセント点を求めます.
>>> stats.norm.ppf(loc = 4, scale = 0.8, q = 0.025)
2.4320288123679563
下側確率とパーセント点の関係から,以下の関係が成り立ちます.
>>> LowerProb = stats.norm.cdf(loc = 4, scale = 0.8, x = 3)
>>> stats.norm.ppf(loc = 4, scale = 0.8, q = LowerProb)
3.0000000000000004
上記のコードの解説は以下のようになります.
1行目:cdf関数の引数に $x = 3$と指定して,3以下になる確率を求める(i.e. 確率変数の値を確率に変換する).
2行目:パーセント点を求めるppf関数の引数にcdf関数の結果を入れると元に戻る(i.e. 確率がまた確率変数の値に戻る).

先と同様に,下側確率が50%となるパーセント点は平均と一致する.
>>> stats.norm.ppf(loc = 4, scale = 0.8, q = 0.5)
4.0

標準正規分布
平均0,分散(標準偏差)1の正規分布のことを標準正規分布($\mathcal{N}  (x | 0, 1)$)と呼びます.

t値
t値は,下式で計算される統計量です.
$t = \cfrac{\hat{\mu} - \mu}{\hat{\sigma} / \sqrt{N}}$
$t値 = \cfrac{標本平均 - 母平均}{標準誤差}$
ここで,$\hat{\mu}$:標本平均,$\mu$:母平均,$\hat{\sigma}$:標本から計算された標準偏差(不偏分散の平方根)です.
t値は標準化とよく似た計算式です(標準化:平均を0,分散を1にする変換,(データ - 平均)/ 標準偏差)).標準誤差は標本平均の標準偏差と見なすことができます.そこで,(標本平均 - 母平均)÷ 標準偏差 は標本平均に対する標準化とみなすことができそうですが,この計算をしても分散は1にはなりません.なぜなら,標本から計算した標準誤差で割っているからです.

t値の標本平均
母集団分布に正規分布を仮定すると,標本分布も理論的に求めることが可能です.
まずは,t値の標本分布をシミュレーションで確認してみます.
  1. 母集団分布が$\mathcal{N}  (x | 4, 0.8^{2})$である母集団から標本抽出シミュレーションを行う(サンプルサイズ:10).
  2. 得られた標本から標本平均を求める.
  3. 得られた標本から標準誤差を求める.
  4. (標本平均 - 母平均)÷ 標準誤差 という計算により,t値を計算する.
  5. 上記の試行を10000回繰り返す.
>>> # 乱数の種
>>> np.random.seed(1)
>>> # t値を格納する配列
>>> t_value_array = np.zeros(10000)
>>> # 正規分布クラスのインスタンス
>>> norm_dist = stats.norm(loc = 4, scale = 0.8)
>>> # シミュレーションの実行
>>> for i in range(0, 10000):
...     sample = norm_dist.rvs(size = 10)
...     sample_mean = sp.mean(sample)
...     sample_std = sp.std(sample, ddof = 1)
...     sample_se = sample_std / sp.sqrt(len(sample))
...     t_value_array[i] = (sample_mean - 4) / sample_se
シミュレーション結果は t_mean_array という配列(変数)に格納します. t_mean_arrayには10000試行分のt値が格納されます.sample_seは標準誤差です.
上記のコードで実行した10000試行分のt値のヒストグラムを描いてみます.図にはカーネル密度推定の結果も併せて図示しています.点線のプロットは標準正規分布の確率密度です.実線(ヒストグラムのカーネル密度推定)と点線(標準正規分布)に若干のズレがあることがわかります.

不偏性が満たされているので,標本平均の平均値は母平均と等しくなります.なので,(標本平均 - 母平均)÷ 標準誤差 の結果における分布の中心は0になっています.一方で,標準誤差で割っているにも関わらず,分布の裾が少し広くなっています(分散が1よりも大きくなっている).この原因は,標本から計算された標準誤差で割っているからです.

t分布
母集団分布が正規分布である時のt値の標本分布のことをt分布と呼びます.
サンプルサイズがNの時,N-1としたものを自由度と呼びます.サンプルサイズが10の場合は,自由度は9になります.t分布の形状は自由度によって変化します.自由度をnと置くと,t分布はt(n)と表記されます.
t分布の平均値は0になります.t分布の分散は1よりも少し大きくなります.自由度をnと置くと下式のように計算されます.
$t(n)の分散 = \cfrac{n}{n-2}$
ただし,nは2よりも大きい
自由度(i.e. サンプルサイズ)が大きくなると,分散は1に近づき,標準正規分布とほとんど違いがなくなってきます.なので,サンプルサイズが小さい場合は,差が大きくなります.

t分布の確率密度と標準正規分布の確率密度を図示してみます.
>>> plt.plot(x, stats.norm.pdf(x = x), 
...         color = 'black', linestyle = 'dotted')
[<matplotlib.lines.Line2D object at 0x119186450>]
>>> plt.plot(x, stats.t.pdf(x = x, df = 9), 
...         color = 'black')
[<matplotlib.lines.Line2D object at 0x116a62f50>]
実線で示されているt分布の方が,やや裾が広い分布になっています.子では,平均値と大きく異なるデータが発生しやすいと解釈できます.
先のシミュレーションの結果と合わせると,カーネル密度推定の結果とほぼ一致します.
標本から計算された標準誤差で標準化された標本平均の分布とt分布の確率密度の比較

t分布の意義は,母分散がわかっていない状況でも,標本平均の分布について言及することができることです.t分布の導出の際には,標本から計算された標準誤差で標本平均を標準化しています.これは,母分散がわかっていない状況で標準化するという,大胆(?)なことをしています(この計算をしても分散は1にはならないので,厳密には標準化と言えませんが...).
標本から計算された標準誤差で標本平均を標準化した時の結果がt分布に従うという事実を使うことで,得られた標本平均に関する不確実性を見積もることが可能となります.

Pythonで学ぶあたらしい統計学の教科書に関連した過去の投稿
I 統計学の基礎 1. 統計学,2. 標本が得られるプロセス,3. 標本が得られるプロセスの抽象化
I 統計学の基礎 4. 記述統計の基礎,6. 確率質量関数と確率密度関数
I 統計学の基礎 7. 統計量の基礎
I 統計学の基礎 8. 確率論の基本
I 統計学の基礎 9. 確率変数と確率分布
III データ分析 記述統計:1変量データ編
III データ分析 記述統計:多変量データ編
III データ分析 母集団からの標本抽出シミュレーション
III 標本の統計量の性質

2019年5月6日月曜日

[Statistics] III 標本の統計量の性質

Pythonで学ぶあたらしい統計学の教科書のまとめやコードを実行してみた際のメモです.

試行,標本分布
試行:一回の調査や実験のこと.全く同じ条件下で調査や実験を繰り返すことができる場合は,試行を複数回行うことができる.
試行回数:何度も試行を繰り返すことができる場合,繰り返し回数のことを試行回数と呼ぶ.
標本分布:標本の統計量が従う確率分布のこと.

通常は,調査は1回のみしか行いません(全く同じ調査を,同じ条件で行うことは,ほぼない...と言って良い).サンプルサイズに関わらず,1回の調査から得られる標本は1つのみです.以下で考える標本分布は「もしも」同じ調査を何度も繰り返したならばどうなるか(例えば,試行回数が3回の場合は,標本平均が3つ得られる)ということを考えたものです.

まずは,数値計算に必要なライブラリをインポートします.
>>> import numpy as np
>>> import pandas as pd
>>> import scipy as sp
>>> from scipy import stats

グラフを描画するライブラリをインポートします.
>>> from matplotlib import pyplot as plt
>>> import seaborn as sns
>>> sns.set()

REPLでは必要ありませんが,Jupyter Notebookでは表示桁数の指定と,グラフを表示させるための指定を行います.
>>> %precision 3
>>> %matplotlib inline



ここでは,母集団は常に平均 4.00, 標準偏差 0.80(分散 0.64)の正規分布とすることにします.これらの条件を設定した population を定義します.

>>> population = stats.norm(loc = 4, scale = 0.8)

標本平均の計算
試行回数を10000回としてみます(標本平均が10000個得られる).
平均値を格納する配列(長さ 10000のアレイ)を作成し,10000個の標本平均を格納します.
>>> sample_mean_array = np.zeros(10000)
>>> np.random.seed(1)
>>> for i in range(0, 10000): 
...     sample = population.rvs(size = 10)
...     sample_mean_array[i] = sp.mean(sample)

    1行目:乱数の種を指定しています.

    2行目:for構文を用いて10000回の繰り返しを指定しています.

    3行目以降:母集団からの標本抽出シミュレーション.sample_mean_array のi番目に,計算された標本平均を格納しています.標本の抽出は population.rvs 関数で行い(stats.norm.rvs(loc = 4, scale = 0.8, size = 10 としても同じこと),サンプルサイズは10になります.得られた標本に対して sp.mean 関数を用いて平均値を計算しています.

10000個の標本平均は以下のようになります.
>>> sample_mean_array
array([3.92228729, 3.86432929, 4.06953003, ..., 4.13616562, 4.57489661,
       4.09896685])

標本平均の平均値と母平均の関係
標本平均の平均値を計算します.シミュレーションで 10000個の標本平均が得られており,10000個の標本平均の平均値を得るという計算になります.
>>> sp.mean(sample_mean_array)
4.004202422791747
母平均は 4 なので,かなり近い値になっています.

標本平均の標準偏差を計算してみます.
>>> sp.std(sample_mean_array, ddof = 1)
0.2513580850860402
母標準偏差は0.8なので,母標準偏差よりも小さくなっていることがわかります.

標本平均のヒストグラム(平均値の標本分布)を描いてみると,以下のようになります.
>>> sns.distplot(sample_mean_array, color = 'black')
<matplotlib.axes._subplots.AxesSubplot object at 0x11b7ea0d0>

サンプルサイズが大きくなると標本平均は母平均に近くなることの確認
サンプルサイズと標本平均と母平均の関係をみてみます.シミュレーションは以下の条件で行います.
  • 対象:標本平均
  • 変化させるもの:サンプルサイズ
  • 調べること:サンプルサイズが大きくなった時に,標本平均と母平均の関係はどうなるか
まず,10 〜 100010まで100区切りで変化させたサンプルサイズを用意します.
>>> size_array = np.arange(start = 10, stop = 100100, step = 100)
>>> size_array
array([    10,    110,    210, ...,  99810,  99910, 100010])

標本平均を格納する配列を準備します.
>>> sample_mean_array_size = np.zeros(len(size_array))

シミュレーションを実行します(実行は,標本平均を求める試行を,サンプルサイズを変えながら何度も実行する).
>>> np.random.seed(1)
>>> for i in range(0, len(size_array)): 
...     sample = population.rvs(size = size_array[i])
...     sample_mean_array_size[i] = sp.mean(sample)

横軸にサンプルサイズをとって,縦軸に標本平均をとった折れ線グラフを描いてみます.
>>> plt.plot(size_array, sample_mean_array_size, color = 'black')
[<matplotlib.lines.Line2D object at 0x11cdd4b10>]
>>> plt.xlabel("sample size")
Text(0.5, 0, 'sample size')
>>> plt.ylabel("sample mean")
Text(0, 0.5, 'sample mean')
このグラフから,サンプルサイズが大きくなるにつれて,標本平均が母平均(4)に近づくことがわかります.

標本平均を何度も計算する関数
シミュレーションを行うために,標本平均を何度も計算する関数(calc_sample_mean()関数)を作ってみます.
>>> def calc_sample_mean(size, n_trial):
...     sample_mean_array = np.zeros(n_trial)
...     for i in range(0, n_trial): 
...             sample = population.rvs(size = size)
...             sample_mean_array[i] = sp.mean(sample)
...     return(sample_mean_array)
この関数の特徴は,引数としてサンプルサイズ(size)と試行回数(n_trial)を指定できるようにしてあることです.このようにしておけば,サンプルサイズや試行回数を変えて,標本平均を何度も計算することが可能になります.なお,標本平均は試行回数の数(n_trial個)だけ得られることになります.
上記のコードの説明:
    2行目:試行回数分の標本平均を格納する配列を準備しています.
    3行目:繰り返し構文.試行回数だけ繰り返すようにします.
    4行目:平均 4,標準偏差 0.8 の正規分布に従う母集団から,size個の標本を抽出します.
    5行目:sample_mean_array に標本平均を格納します.
    6行目:n_trial回計算された標本平均を返します.

サンプルサイズを変化させた時の標本平均の分布
動作確認(データを10個選んで標本平均を得る試行を10000回繰り返す)
>>> np.random.seed(1)
>>> sp.mean(calc_sample_mean(size = 10, n_trial = 10000))
4.004202422791747

この関数を用いて,サンプルサイズの影響をより詳細に確かめてみます.
確かめる方法は,サンプルサイズを10, 20, 30と変化させた時の標本平均の分布をバイオリンプロットで確認することとします.試行回数は10000回とします.

乱数の種を指定
>>> np.random.seed(1)

サンプルサイズ 10 の場合
>>> 
>>> size_10 = calc_sample_mean(size = 10, n_trial = 10000)
>>> size_10_df = pd.DataFrame({
...     "sample_mean":size_10, 
...     "size"       :np.tile("size 10", 10000)
... })

サンプルサイズ 20の場合
>>> size_20 = calc_sample_mean(size = 20, n_trial = 10000)
>>> size_20_df = pd.DataFrame({
...     "sample_mean":size_20, 
...     "size"       :np.tile("size 20", 10000) 
... })

サンプルサイズ 30の場合
>>> size_30 = calc_sample_mean(size = 30, n_trial = 10000)
>>> size_30_df = pd.DataFrame({
...     "sample_mean":size_30,
...     "size"       :np.tile("size 30", 10000) 
... })

各結果を結合する.
>>> sim_result = pd.concat(
...      [size_10_df, size_20_df, size_30_df])

結果の表示(最初だけ)
>>> print(sim_result.head())
   sample_mean     size
0     3.922287  size 10
1     3.864329  size 10
2     4.069530  size 10
3     3.857140  size 10
4     4.184654  size 10

上記のコードでは,サンプルサイズを10, 20, 30と変化させた時の標本平均をpandasフレームにまとめています.
また,各場合(10, 20, 30)の試行回数が10000回(10000行)なので,結合した sim_result は30000行になります.

このデータをバイオリンプロットで描いてみます.
>>> sns.violinplot(x = "size", y = "sample_mean", data = sim_result, color = 'gray')

この図から,サンプルサイズが大きくなると,標本平均のばらつきが小さくなり,母平均(4)の近くに集中する様子がわかります.

標本平均の標準偏差が母標準偏差よりも小さいこと
サンプルサイズが大きくなると標本平均のばらつきが小さくなることを,標本平均の標準偏差をサンプルサイズ別にみてみることで確認してみます.シミュレーションは以下の要領で行います.
  • 対象:標本平均の標準偏差
  • 変化させるもの:サンプルサイズ
  • 調べる内容:サンプルサイズが大きくなると,標本平均の標準偏差は小さくなっていくこと.すなわち,サンプルサイズを大きくすると,標本平均はより信頼できる値になっていく.
まずは,2- 100まで2区切りで変化させたサンプルサイズを用意します.
>>> size_array = np.arange(
...     start =2, stop = 102, step =2)
>>> size_array
array([  2,   4,   6,   8,  10,  12,  14,  16,  18,  20,  22,  24,  26,
        28,  30,  32,  34,  36,  38,  40,  42,  44,  46,  48,  50,  52,
        54,  56,  58,  60,  62,  64,  66,  68,  70,  72,  74,  76,  78,
        80,  82,  84,  86,  88,  90,  92,  94,  96,  98, 100])

続いて,標本平均の標準偏差を格納する入れ物を用意します.
>>> sample_mean_std_array = np.zeros(len(size_array))

シミュレーションを,試行回数は100回とします.
>>> np.random.seed(1)
>>> for i in range(0, len(size_array)): 
...     sample_mean = calc_sample_mean(size = size_array[i], n_trial = 100)
...     sample_mean_std_array[i] = sp.std(sample_mean, ddof = 1)

横軸にサンプルサイズをとり,縦軸に標本平均の標準偏差をとった折れ線グラフを描いてみます.

>>> plt.plot(size_array, sample_mean_std_array, color = 'black')

[<matplotlib.lines.Line2D object at 0x119c87ed0>]
>>> plt.xlabel("sample size")
Text(0.5, 0, 'sample size')
>>> plt.ylabel("mean_std value")
Text(0, 0.5, 'mean_std value')

標準誤差
理論上の標本平均の標準偏差の大きさは,計算することができ,これを標準誤差(Standard Error, SE)と呼びます.
標準誤差(SE)$= \cfrac{\sigma}{\sqrt{N}}$

標準誤差とシミュレーションの結果を比較してみます.
まずは,標準誤差を求めます.
>>> standard_error = 0.8 / np.sqrt(size_array)
>>> standard_error
array([0.56568542, 0.4       , 0.32659863, 0.28284271, 0.25298221,
       0.23094011, 0.21380899, 0.2       , 0.18856181, 0.17888544,
       0.17056057, 0.16329932, 0.15689291, 0.15118579, 0.14605935,
       0.14142136, 0.13719887, 0.13333333, 0.12977714, 0.12649111,
       0.12344268, 0.12060454, 0.11795356, 0.11547005, 0.11313708,
       0.11094004, 0.10886621, 0.1069045 , 0.10504515, 0.10327956,
       0.1016001 , 0.1       , 0.09847319, 0.09701425, 0.09561829,
       0.0942809 , 0.09299811, 0.09176629, 0.09058216, 0.08944272,
       0.08834522, 0.08728716, 0.08626622, 0.08528029, 0.0843274 ,
       0.08340577, 0.0825137 , 0.08164966, 0.0808122 , 0.08      ])

先の折れ線グラフに標準誤差を重ね合わせて描画してみます.
>>> plt.plot(size_array, sample_mean_std_array, color = 'black')
>>> plt.plot(size_array, standard_error, color = 'black', linestyle = 'dotted')
>>> plt.xlabel("sample size")
>>> plt.ylabel("mean_std value")
Text(0, 0.5, 'mean_std value')
この例では,linestyle = 'dotted' と点線で描くように指定しています.シミュレーション結果と標準誤差の値は,ほぼ一致しています.

標本分散の平均値と母分散のずれ
標本分散を対象としたシミュレーション(10000回)を実行して,標本分散の平均値を求めてみます.

まずは,標本分散を格納する変数(入れ物)を準備します.
>>> sample_var_array = np.zeros(10000)

シミュレーションを実行します.シミュレーションは,データを10個選んで,標本分散を求める思考としています.
>>> np.random.seed(1)
>>> for i in range(0, 10000): 
...     sample = population.rvs(size = 10)
...     sample_var_array[i] = sp.var(sample, ddof = 0)

標本分散の平均値は以下のようになります.

>>> sp.mean(sample_var_array)

0.5746886877332101
母分散は0.8の2乗で0.64となるはずですが,計算結果は0.575と分散を過小評価していることがわかります.

不偏分散を使うとバイアスが解消される
標本分散には,母集団の分散(母分散)と比べて分散を過小評価してしまうというバイアスがあります.不偏分散は,これを修正するためのものです.
以下に,不偏分散を使ってそのことを確認してみます.

不偏分散を格納する変数(入れ物)を準備する.
>>> unbias_var_array = np.zeros(10000)

データを10個選んで不偏分散を求める試行を10000回繰り返す.
>>> np.random.seed(1)
>>> for i in range(0, 10000): 
...     sample = population.rvs(size = 10)
...     unbias_var_array[i] = sp.var(sample, ddof = 1)

不偏分散の平均値を求める.
>>> sp.mean(unbias_var_array)
0.6385429863702334

先の標本分散を用いた結果よりも,不偏分散を用いた結果の方が,0.64に近い値となっていることがわかります.

サンプルサイズが大きくなると不偏分散は母分散に近づく
サンプルサイズと母分散の関係を調べるためのシミュレーションを行ってみます.
シミュレーションは以下の要領で行ってみます.
  • 対象:不偏分散
  • 変化させるもの:サンプルサイズ
  • 調べる内容:サンプルサイズが大きくなると不偏分散は母分散に近づくこと
まずは,10 - 100010 まで100区切りで変化させたサンプルサイズを用意します.
>>> size_array = np.arange(
...     start = 10, stop = 100100, step = 100)
>>> size_array
array([    10,    110,    210, ...,  99810,  99910, 100010])

続いて不偏分散を格納する変数(入れ物)を用意します.
>>> unbias_var_array_size = np.zeros(len(size_array))

不偏分散を求める試行をサンプルサイズを変えて実行します.
>>> np.random.seed(1)
>>> for i in range(0, len(size_array)): 
...     sample = population.rvs(size = size_array[i])
...     unbias_var_array_size[i] = sp.var(sample, ddof = 1)

横軸にサンプルサイズをとって,縦軸に不偏分散をとった折れ線グラフを描いてみます.

>>> plt.plot(size_array, unbias_var_array_size, color = 'black')

[<matplotlib.lines.Line2D object at 0x119c95fd0>]
>>> plt.xlabel("sample size")
Text(0.5, 0, 'sample size')
>>> plt.ylabel("unbias var")
Text(0, 0.5, 'unbias var')
この図から,サンプルサイズが大きくなるにつれて,不偏分散が母分散(0.64)に近ずく様子がわかります.

不偏性:推定量の期待値が真の母数(母集団のパラメタ)となる特性のこと.
不偏性があるということは,平均すると,過大にも過少にもなっていない,すなわち偏りがない推定量であるということになります.

一致性:サンプルサイズが大きくなると,推定量が真の母数に近づいていく特性
一致性があるということは,言い換えると,サンプルサイズが無限だった場合には,推定量と母数が一致するということになります.

分析の目的は,母集団分布を推定することです.母集団分布が分かれば,未知のデータに対して予測や推測を行うことができるからです.

母集団分布に正規分布を仮定すると,正規分布の母数(パラメータ)を推定することで,母集団分布を推定することができます.正規分布の母数は平均と分散の2つになります.正規分布の母数は一般的にはわからないので,代わりに標本平均と不偏分散を用いることになります.
上記のシミュレーションで,
  • 標本平均と不偏分散が母数の推定量として好ましい性質を持つこと
がわかりました.また,
  • 標本平均の平均値は母平均とほぼ同じであること
  • 不偏分散の平均値は母分散とほぼ同じとみなせること
もわかりました.標本平均と不偏分散は不偏性を持っていると言うことができます.さらに,
  • サンプルサイズを大きくすると,標本平均は母平均に近づくこと
  • サンプルサイズを大きくすると,不偏分散は母分散に近づくこと
もわかりました.標本平均と不偏分散は一致性を持っていると言うことができます.

大数の法則:標本の大きさが大きくなるにつれて,標本平均が母平均に近づく近づき方を表現した法則,Upton, Cook (2010)
大数の法則には,大数の弱法則と大数の強法則がある.

中心極限定理:母集団分布が何であっても,サンプルサイズが大きい場合には,確率変数の和は正規分布に近いものになること.
中心極限定理をシミュレーションで確認してみます.シミュレーションは10000枚のコインを同時に投げて,表が出た枚数を数える試行を指定した回数だけ繰り返すことにします.

サンプルサイズと試行回数を指定します.
>>> n_size = 10000
>>> n_trial = 50000

表ならば1,裏ならば0とします.
>>> coin = np.array([0, 1])

表が出た回数
>>> count_coin = np.zeros(n_trial)

コインをn_size回投げる試行をn_trial回行います.
>>> np.random.seed(1)
>>> for i in range(0, n_trial):
...     count_coin[i] = sp.sum(
...         np.random.choice(coin, size = n_size, replace = True))

ヒストグラムを描いてみます.

>>> sns.distplot(count_coin, color = 'black')

<matplotlib.axes._subplots.AxesSubplot object at 0x115f41a10>

上記のコードをJupyter Notebookで実行した結果はGitHubで見ることができます.

Pythonで学ぶあたらしい統計学の教科書に関連した過去の投稿
I 統計学の基礎 1. 統計学,2. 標本が得られるプロセス,3. 標本が得られるプロセスの抽象化
I 統計学の基礎 4. 記述統計の基礎,6. 確率質量関数と確率密度関数
I 統計学の基礎 7. 統計量の基礎
I 統計学の基礎 8. 確率論の基本
I 統計学の基礎 9. 確率変数と確率分布
III データ分析 記述統計:1変量データ編
III データ分析 記述統計:多変量データ編
III データ分析 母集団からの標本抽出シミュレーション
III 標本の統計量の性質