{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# 大標本特性" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "<div name=\"html-admonition\" style=\"font-size: 0.8em\">\n", "<input type=\"button\" onclick=\"location.href='https://translate.google.com/translate?hl=&sl=ja&tl=en&u='+window.location;\" value=\"Google translation\" style=\"color:#ffffff;background-color:#008080; height:25px\" onmouseover=\"this.style.background='#99ccff'\" onmouseout=\"this.style.background='#008080'\"/> in English or the language of your choice.\n", "</div><br>" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import matplotlib.pyplot as plt\n", "import numpy as np\n", "import pandas as pd\n", "import statsmodels.formula.api as smf\n", "import wooldridge\n", "\n", "from scipy.stats import gaussian_kde, t\n", "from statsmodels.api import qqplot\n", "from statsmodels.stats.stattools import jarque_bera, omni_normtest\n", "from numba import njit\n", "\n", "# 警告メッセージを非表示\n", "import warnings\n", "warnings.filterwarnings(\"ignore\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "ここでは大標本特性(Large sample properties)もしくは漸近的特性(Asymptotic properties)と呼ばれる特性について考察する。この特性は\n", "\n", "**仮定6(正規性)が満たされない場合**\n", "\n", "に重要となる推定量の性質である。" ] }, { "cell_type": "markdown", "metadata": { "heading_collapsed": true }, "source": [ "## 一致性(Consistency)" ] }, { "cell_type": "markdown", "metadata": { "heading_collapsed": true, "hidden": true }, "source": [ "### 説明" ] }, { "cell_type": "markdown", "metadata": { "hidden": true }, "source": [ "不偏性は推定量の望ましい特性である。一方で,全ての推定量が不偏性を満たすわけではない。推定量について一般にいわれていることは,最低限**一致性(consistency)**\n", "は必要だということである。一致性を記号で表現すると\n", "\n", "$$\n", "\\underset{n\\rightarrow\\infty}{\\text{plim}}\\;\\hat{\\beta}=\\beta\n", "$$\n", "\n", "となり,$\\text{plim}$(probability limit)は確率収束を表している。この式の意味を理解するために$\\hat{\\beta}$は確率変数であることを思い出そう(シミュレーションの結果が毎回異なることを思い出そう)。不偏性と比較して直感的に説明することにする。\n", "* ある母集団から標本の大きさ$n$のサンプルを$N$回復元抽出し($N$は標本数),そのたびにOLS推定値$\\hat{\\beta}$を計算するとしよう。その平均が母集団のパラメータの値と等しいというのが不偏性($\\text{E}\\left(\\hat{\\beta}\\right)={\\beta}$)である。\n", "* 上の例での標本数は$N$であるため,$N$個のOLS推定量$\\hat{\\beta}$があり,その分布を考えることができる。GM仮定1〜4のもとで$N$が大きければ,連続分布関数をイメージすれば良い(例えば,単峰性の左右非対称の連続分布関数)。ここで標本数$N$を固定して,標本の大きさ$n$を増やしたとしよう。$n$の増加によりOLS推定量はより正確になり,推定値の分布はより分散が小さな形に変わっていく(分布の「幅が狭くなる」)。更に,$n\\rightarrow\\infty$とすると,推定値の分布は$\\beta$の点に退化(一点に集中)することになる。即ち,標本の大きさが大きければ,OLS推定値$\\hat{\\beta}$の分布自体が真の値$\\beta$に限りなく近づいていくのである。これが上の式の意味である。" ] }, { "cell_type": "markdown", "metadata": { "hidden": true }, "source": [ "```{figure} ./images/consistency.jpg\n", ":align: center\n", "\n", "推定値の分布:不偏性と一致性\n", "```" ] }, { "cell_type": "markdown", "metadata": { "hidden": true }, "source": [ "この章ではシミュレーションを使って分布の収束過程を示すが,その前に定理としてまとめる。\n", "\n", "---\n", "**定理**\n", "\n", "仮定1〜4(GM仮定)のもとで,OLS推定量は一致性を満たす。\n", "\n", "---\n", "この定理は,OLS推定量は不偏性と一致性を兼ね備えていることを示している。" ] }, { "cell_type": "markdown", "metadata": { "hidden": true }, "source": [ "更に,一致性だけに着目すれば仮定4を次の仮定4aに緩めることが可能となる。\n", "\n", "仮定4a:$\\text{E}(u)=0,\\quad\\text{Cov}(u,x)=0$\n", "\n", "* 仮定4$\\text{E}\\left(u|x\\right)=0$の意味をもう一度考えてみるために、線形もしくは非線形のランダムな関係を考えてみよう。[このウィキペディアの画像](https://en.wikipedia.org/wiki/Correlation#/media/File:Correlation_examples2.svg)にある関係をイメージすると良いだろう。これらの図の横軸と縦軸には2つの変数がり,それぞれを$x$と$u$としよう。上段と下段に着目すると,上段は通常イメージする線形の相関関係に近い。一方,下段は非線形の相関関係を表示している。$\\text{E}\\left(u|x\\right)=0$は,上段の中央の図を意味する。即ち,$u$と$x$には,線形及び非線形の関係は存在し**ない**ことを意味する強い仮定になっている。\n", "\n", "この仮定を少し緩めたものが仮定4aであり,線形の相関関係がないことを意味する。ここで重要な点は,非線形の相関関係は対象外ということである。従って,$\\text{Cov}(u,x)=0$は,上述のウィキペディアの画像の下段の相関関係が存在しないことを意味しない。更に付け加えると、$u$と$x$に非線形の関係がある場合,$\\text{Cov}(u,x)=0$であっても,$\\text{E}\\left(u|x\\right)\\neq0$になりえるのである。即ち,仮定4は仮定4aを意味するが,仮定4aは仮定4を必ずしも意味しない。\n", "* $\\text{Cov}(u,x)=0$は,$\\text{E}(ux)=\\text{E}(u)x=0$を意味する。\n", "\n", "仮定4より緩い仮定4aのもとでは,少なくとも一致性を満たす推定量としてより多くの状況に対応でき利点がある。" ] }, { "cell_type": "markdown", "metadata": { "hidden": true }, "source": [ "### シミュレーション:一致性" ] }, { "cell_type": "markdown", "metadata": { "hidden": true }, "source": [ "#### 仮定" ] }, { "cell_type": "markdown", "metadata": { "hidden": true }, "source": [ "シミュレーションを使い一致性の分布収束を確かめる。仮定1〜4には誤差項の正規性や均一性は含まれていない。この点を捉えるために,誤差項は2つの異なる分布から確率的に発生すると仮定する。具体的には,確率$p$で誤差項は正規分布\n", "\n", "$$u_N\\sim\\text{Normal(0,0.5)}$$\n", "\n", "に従い発生し,確率$1-p$で\n", "\n", "$$u_c=\\frac{v-1}{\\sqrt{2}},\\qquad v\\sim\\chi^2(1)$$\n", "\n", "が発生する。ここで,$\\chi^2(1)$は自由度1のカイ二乗分布であり,平均は$1$,分散は$2$である。従って,$u_c$の平均は$0$,分散は$1$となる。\n", "* 両方のランダム変数の平均は$0$なので,誤差項の平均は$0$となる。\n", " * $\\text{E}(u_N)=\\text{E}(u_c)=\\text{E}(u)=0$。\n", "* 正規分布の分散は0.5であり,カイ二乗分布の分散は1であるため仮定5(均一分散)は満たされない。(説明変数によって誤差項の分散が異なると解釈できる。)\n", "* 明らかに仮定6(正規分布)も満たされない。" ] }, { "cell_type": "markdown", "metadata": { "hidden": true }, "source": [ "#### 誤差項のプロット" ] }, { "cell_type": "markdown", "metadata": { "hidden": true }, "source": [ "実際に,ここで仮定する誤差項はどのような分布になるか図示する。" ] }, { "cell_type": "markdown", "metadata": { "hidden": true }, "source": [ "ランダム変数の数" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "hidden": true }, "outputs": [], "source": [ "n = 10_000" ] }, { "cell_type": "markdown", "metadata": { "hidden": true }, "source": [ "このシミュレーションでは,`numpy`の関数`zeros()`を使い`for`ループで生成される誤差項の値を格納する`array`を用意する。`zeros()`は`0`が並ぶ`array`を作成する関数であり,一回のループ毎に`0`が誤差項の値と置換されることになる。1行・`n`列の`array`を設定する。" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "hidden": true }, "outputs": [], "source": [ "u = np.zeros(n)\n", "u" ] }, { "cell_type": "markdown", "metadata": { "hidden": true }, "source": [ "正規分布の標準偏差の値。" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "hidden": true }, "outputs": [], "source": [ "u_sd = 0.5" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "hidden": true }, "outputs": [], "source": [ "for i in range(n): #1\n", "\n", " random_var = { #2\n", " 'normal':np.random.normal(loc=0, scale=u_sd), #3\n", " 'chi2':(np.random.chisquare(1)-1)/np.sqrt(2) #4\n", " }\n", " \n", " dist = ['normal','chi2'] #5\n", "\n", " choice = np.random.choice(dist, p=[0.05,1-0.05]) #6\n", " error = random_var[choice] #7\n", " u[i] = error #8" ] }, { "cell_type": "markdown", "metadata": { "hidden": true }, "source": [ "<上のコードの説明>\n", "* `#1`:`n`回`for`ループの開始\n", "* `#2`:`random_var`は,`#3`と`#4`で生成するランダム変数を格納する辞書名\n", "* `#3`:`normal`がキーであり,値には正規分布に従い発生した1つのランダム変数を設定\n", "* `#4`:`chi2`がキーであり,値にはカイ二乗分布に従い発生した1つのランダム変数を設定\n", "* `#5`:`#3`と`#4`のキーを要素とするリスト\n", "* `#6`:`np.random.choice()`関数は,第一引数`dist`のリストから1つの要素をランダムに選び,その確率は第二引数`p=[0.05,1-0.05]`によって指定される\n", "* `#7`:`#6`で選択されたキーを使って,`#2`の`random_var`からランダム変数を抽出し,それを誤差項として`error`に割り当てる\n", "* `#8`:`#7`の誤差項を`u[]`の`i`番目に代入\n", "\n", "`u`の最初の10の値を確認してみる。" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "hidden": true }, "outputs": [], "source": [ "u[:10]" ] }, { "cell_type": "markdown", "metadata": { "hidden": true }, "source": [ "次に`u`を図示してみよう。" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "hidden": true }, "outputs": [], "source": [ "x=np.linspace(-2.0,2.0,100) # 図を作成するために-2から2までの横軸の値を設定\n", "kde_model=gaussian_kde(u) # カーネル密度推定を設定\n", "ufunc = kde_model(x) # カーネル密度推定を使い誤差項の分布を推定\n", "plt.plot(x, ufunc) # 誤差項の分布をプロット\n", "pass" ] }, { "cell_type": "markdown", "metadata": { "hidden": true }, "source": [ "確認のために、生成された`n`個のランダム変数(誤差項)の平均を計算してみる。" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "hidden": true }, "outputs": [], "source": [ "u.mean()" ] }, { "cell_type": "markdown", "metadata": { "hidden": true }, "source": [ "#### 一致性" ] }, { "cell_type": "markdown", "metadata": { "hidden": true }, "source": [ "<シミュレーションの内容>\n", "* 母集団のパラメータを決める。\n", "* 単回帰分析\n", "\n", " $$ y=\\beta_0 + \\beta_1 x + u$$\n", " \n", "* 標本の大きさ$n=30,\\;100,\\;250,\\;500$を考える。\n", "* それぞれ10,000回推定し$\\hat{\\beta}_1$(10,000個)の分布を比べる。" ] }, { "cell_type": "markdown", "metadata": { "hidden": true }, "source": [ "母集団のパラメータ" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "hidden": true }, "outputs": [], "source": [ "b0 = 1 # 定数項\n", "b1 = 0.5 # 説明変数の係数" ] }, { "cell_type": "markdown", "metadata": { "hidden": true }, "source": [ "標本数(シミュレーションの回数)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "hidden": true }, "outputs": [], "source": [ "N = 10_000" ] }, { "cell_type": "markdown", "metadata": { "hidden": true }, "source": [ "シミュレーションをおこなう関数を定義する。\n", "* 引数:標本の大きさ`n`\n", "* 返り値:`b1`の推定値の`numpy`の`array`" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "hidden": true }, "outputs": [], "source": [ "@njit # 計算の高速化\n", "def sim_consistency(n):\n", " \n", " b1_arr = np.zeros(N) # b1の推定値を格納するarray\n", "\n", " for i in range(N): # forループで N回シミュレーション\n", " \n", " x = np.random.normal(loc=4, scale=0.5, size=n) # 説明変数\n", " \n", " # 残差の生成(上で説明したもの)\n", " u = np.zeros(n)\n", " \n", " for j in range(n):\n", " prob = 0.05 # 正規分布の確率\n", " dist_1 = np.random.normal(loc=0, scale=u_sd)\n", " dist_2 = (np.random.chisquare(1) - 1) / np.sqrt(2)\n", " error = prob*(dist_1)+(1-prob)*(dist_2)\n", " u[j] = error\n", " \n", " y = b0 + b1 * x + u # yの抽出\n", " \n", " var_cov = np.cov(y,x) # 分散共分散行列\n", " cov_yx = var_cov[0,1] # 共分散\n", " var_y = var_cov[0,0] # yの分散\n", " var_x = var_cov[1,1] # xの分散 \n", " b1hat = cov_yx / var_x # スロープ係数 \n", " b1_arr[i] = b1hat # スロープ係数を b1_arrに格納\n", "\n", " return b1_arr # 推定値のリストを返す" ] }, { "cell_type": "markdown", "metadata": { "hidden": true }, "source": [ "関数`sim_consistency()`を使い$n=30,\\;100,\\;250,\\;500$の4パターンのシミュレーションをおこなうが、その際、次のようなコードを書くことも可能である。\n", "\n", "```\n", "# n=30のシミュレーション\n", "sim_30 = sim_consistency(30)\n", "\n", "# n=100のシミュレーション\n", "sim_100 = sim_consistency(100)\n", "\n", "# n=250のシミュレーション\n", "sim_250 = sim_consistency(250)\n", "\n", "# n=500のシミュレーション\n", "sim_500 = sim_consistency(500)\n", "```\n", "もちろん,この方法でも問題はないが,パターンが増えると書く行も増えていく。別の方法として辞書を使う方法を紹介する。`n`の値である`30`や`100`をキーに設定し,値にシミュレーションの結果を格納する方法である。" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "hidden": true }, "outputs": [], "source": [ "data_consistency = {} # 空の辞書の作成\n", "\n", "for i in [30,100,250,500]:\n", " data_consistency[str(i)] = sim_consistency(i)" ] }, { "cell_type": "markdown", "metadata": { "hidden": true }, "source": [ "最後の行の右辺は`sim_consistency()`の返り値である`array`であり,それをキー`'30'`や`'100'`とのペアに設定し,それらから構成される辞書`data_consistency`を作成している。イメージとしては次のような辞書となっている。\n", "```\n", "{'30':sim_consistency(30),'100':sim_consistency(100),....}\n", "```\n", "シミュレーションの結果にアクセスするにはキーを使う。例えば,`sim_consistency(30)`の結果にアクセスする場合は" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "hidden": true }, "outputs": [], "source": [ "data_consistency['30']" ] }, { "cell_type": "markdown", "metadata": { "hidden": true }, "source": [ "とする。次に図をプロットしよう。" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "hidden": true }, "outputs": [], "source": [ "# 図を作成するために横軸の値を設定\n", "xx=np.linspace(0.0,1.0,100)\n", "\n", "# n = 30\n", "kde_model_30=gaussian_kde(data_consistency['30']) # カーネル密度推定を使いOLS推定量の分布を推定\n", "plt.plot(xx, kde_model_30(xx), 'k', label='n=30') # OLS推定量の分布プロット\n", "\n", "# n = 100\n", "kde_model_100=gaussian_kde(data_consistency['100'])\n", "plt.plot(xx, kde_model_100(xx), 'g', label='n=100')\n", "\n", "# n = 250\n", "kde_model_250=gaussian_kde(data_consistency['250'])\n", "plt.plot(xx, kde_model_250(xx), 'b', label='n=250')\n", "\n", "# n = 500\n", "kde_model_500=gaussian_kde(data_consistency['500'])\n", "plt.plot(xx, kde_model_500(xx), 'r', label='n=500')\n", "\n", "\n", "# 母集団のパラメータの値に縦の線\n", "plt.axvline(x=b1,linestyle='dashed')\n", "plt.legend() # 凡例\n", "plt.ylabel('Kernel Density')\n", "plt.title('Consistency: N={0}'.format(N))\n", "pass" ] }, { "cell_type": "markdown", "metadata": { "hidden": true }, "source": [ "上のコードには同じような行が複数あり,単純な間違いの可能性が高くなる。。`for`ループを使うとよりコンパクトに書くことが可能となり,間違いも少なくなる。" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "hidden": true }, "outputs": [], "source": [ "# OLS推定量のリスト\n", "b1hat_list = [data_consistency['30'],data_consistency['100'],data_consistency['250'],data_consistency['500']]\n", "\n", "# 色のリスト\n", "color_list = ['k', 'g', 'b', 'r']\n", "\n", "# ラベルのリスト\n", "label_list = [30, 100, 250, 500]\n", "\n", "# 横軸\n", "xx=np.linspace(0.0,1.0,100)\n", "\n", "# 下の説明(1)を参照 \n", "for (b, c, l) in zip(b1hat_list, color_list, label_list):\n", " kde_model=gaussian_kde(b)\n", " plt.plot(xx, kde_model(xx), c, label='n={}'.format(l)) # 下の説明(2)\n", "\n", "plt.axvline(x=b1,linestyle='dashed')\n", "plt.ylabel('Kernel Density')\n", "plt.title('Consistency: N={}'.format(N)) # 下の説明(2)\n", "plt.legend()\n", "pass" ] }, { "cell_type": "markdown", "metadata": { "hidden": true }, "source": [ "<コードの説明>\n", "> * (1) `zip()`はループによく使われる便利な関数である。以下の単純な`for`ループ\n", "> ```\n", "> for i in range(5):\n", "> print('Hi')\n", "> ```\n", "> にはループ・インデックスが`i`の1種類しかない。しかし複数のループ・インデックスを同時に使えると便利な場合があり,その際使うのが`zip()`である。使い方は,`zip()`の中に複数のリスト(例えば,`b1hat_list`, `color_list`, `label_list`)を入れ,`in`の前にタプルとして同じ順番にループ・インデックスを並べる。\n", "> * (2) `'n={}'.format(l)`について。文字列は`''`で挟むが,その中に定義した変数の値を書きたい場合がある。直接書いても構わないが,値が変更される度に書き直すのは面倒である。`'<文字列>{}'.format(<変数>)`を使うと変数の値が変わっても自動的に変更される。使い方は,`''`の入れたい箇所に`{}`を入れ,その後に`.format(<変数>)`を書く。もちろん`f-string`を使い次のように書いても同じ結果となる。\n", " ```\n", " f'n={l}'\n", " ```" ] }, { "cell_type": "markdown", "metadata": { "hidden": true }, "source": [ "---\n", "図から$n$が増加すると分布の分散が小さくなるのが視覚的に確認できる。これが一致性である。`n`の増加により推定量$\\hat{\\beta}_1$の精度が増すことが理解できる。また4つの分布は真の値$b_1=0.5$を中心に左右対象であることもわかる。即ち,推定量$\\hat{\\beta}_1$は不偏性も満たしていることがわかる。" ] }, { "cell_type": "markdown", "metadata": { "heading_collapsed": true }, "source": [ "## 標本の大きさによる違い" ] }, { "cell_type": "markdown", "metadata": { "hidden": true }, "source": [ "### 小標本特性" ] }, { "cell_type": "markdown", "metadata": { "hidden": true }, "source": [ "標本の大きさが $n>k+1$ であるかぎりどのような値(小さくても大きくても)であっても\n", "1. 仮定1〜4(GM仮定)の下でOLS推定量の**不偏性**は成立する。\n", "2. 仮定1〜5(CLM仮定)の下でOLS推定量の**B.L.U.E**も成立する。\n", "3. 仮定1〜6の下で,説明変数の値を所与とすると,OLS推定量$\\hat{\\beta}_j$は正規分布に従う。これにより$t$検定と$F$検定は**有効**となる。\n", "\n", "小標本特性3が成立する上で特に重要なのは仮定6(誤差項の正規性)である。標本の大きさ($n$)が小さくても(もちろん,大きくても),仮定6によりOLS推定量は正規分布に従い$t$検定と$F$検定は有効である。換言すると,仮定6が成立しなければ$t$検定と$F$検定は無効になってしまう。そこで重要な役割を果たすのが「大標本特性」といわれるものである。" ] }, { "cell_type": "markdown", "metadata": { "hidden": true }, "source": [ "### 大標本特性" ] }, { "cell_type": "markdown", "metadata": { "hidden": true }, "source": [ "仮定6が満たされなくとも(仮定1〜5のもとで)標本の大きさが十分に大きい場合($n\\rightarrow\\infty$),OLS推定量 $\\hat{\\beta}_j$は正規分布に従う。\n", "\n", "この特性により標本数が十分に大きい場合,$t$値と$F$値の分布はそれぞれ$t$分布と$F$分布で**近似**できる。この意味で$t$検定と$F$検定は有効となる。ではどれだけ$n$が大きければ大標本特性のもとで$t$検定と$F$検定が有効となるのであろう。残念なことに決まったルールはない。**30**とも言われるが,全てのケースでそうとは言いがたい。" ] }, { "cell_type": "markdown", "metadata": { "heading_collapsed": true }, "source": [ "## シミュレーション:大標本特性と$t$値の分布" ] }, { "cell_type": "markdown", "metadata": { "heading_collapsed": true, "hidden": true }, "source": [ "### 説明" ] }, { "cell_type": "markdown", "metadata": { "hidden": true }, "source": [ "大標本特性を確認するためにシミュレーションをおこなうが、ここでは$t$値の分布を考える。\n", "\n", "\n", "<シミュレーションの内容>\n", "\n", "* 単回帰分析を考える。\n", "\n", " $$ y=\\beta_0 + \\beta_1 x + u$$\n", " \n", "* 2つのケースに分ける。\n", " 1. $u$が正規性を満たしている場合\n", " 2. $u$が正規性を満たしていない場合\n", "* それぞれのケースで標本の大きさ$n=5,10,30,100$の4つのケースを考える(即ち,計8回パターン)。\n", "* 1パターンにつき$N$回の推定をおこない,次の統計量を$N$個生成する。\n", " \n", " $$\n", " q_1=\\frac{\\hat{\\beta}_1-\\beta_1}{\\text{se}\\left(\\hat{\\beta}_1\\right)}\n", " $$\n", "\n", " $u$が正規性を満たしている場合,$q_1$は$t_{n-2}$に従って分布する。\n", "* $q_1$の分布と対応する$t_{n-2}$分布を比べる。" ] }, { "cell_type": "markdown", "metadata": { "hidden": true }, "source": [ "---\n", "シミュレーションの目的は次の2つの点を確認することである。\n", "\n", "**(小標本特性)**\n", "仮定6が成り立つ場合,$q_1$の値は自由度$n-2$の$t$分布に従う。\n", "\n", "**(大標本特性)**\n", "仮定6が成り立たない場合,$n$が小さいと$q_1$値は$t$分布に従わないが,$n$が十分に大きいと自由度$n-2$の$t$分布に従う。" ] }, { "cell_type": "markdown", "metadata": { "heading_collapsed": true, "hidden": true }, "source": [ "### 誤差項の正規性が満たされる場合" ] }, { "cell_type": "markdown", "metadata": { "hidden": true }, "source": [ "母集団のパラメータは一致性のシミュレーションと同じ値を使う。" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "hidden": true }, "outputs": [], "source": [ "b0 = 1 # 定数項\n", "b1 = 0.5 # 説明変数の係数" ] }, { "cell_type": "markdown", "metadata": { "hidden": true }, "source": [ "誤差項は標準正規分布に従うと仮定する。`u_sd`は誤差項の標準偏差。" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "hidden": true }, "outputs": [], "source": [ "u_sd = 1" ] }, { "cell_type": "markdown", "metadata": { "hidden": true }, "source": [ "標本数(シミュレーションの回数)は次の値とする。" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "hidden": true }, "outputs": [], "source": [ "N = 100_000" ] }, { "cell_type": "markdown", "metadata": { "hidden": true }, "source": [ "シミュレーションの関数を作成する。\n", "\n", "(注意)\n", "* 小標本特性3に「説明変数の値を所与とすると」とあり,これを捉えるために以下のコードでは`for`ループの外に`x`を生成するコードを置く。こうすることにより,$x$を一度ランダム抽出し固定し,`N`回のシミュレーションに使用する。$x$は固定されているが,$u$と$y$は毎回変化することになる。\n", "* シミュレーションの高速化のために`numpy`の関数を使い「手計算」で$q_1$の分布を計算する。" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "hidden": true }, "outputs": [], "source": [ "@njit # 関数の高速化\n", "def sim_norm(n): # n=標本の大きさ\n", " \n", " q1_arr = np.zeros(N) # q1を入れる空array\n", " \n", " x = np.random.normal(loc=4, scale=1, size=n) # N回の推定に同じ説明変数を使う\n", "\n", " for j in range(N): # N 回のループ\n", " \n", " u = np.random.normal(loc=0, scale=u_sd, size=n) # 誤差項\n", " y = b0 + b1 * x + u # 説明変数\n", " \n", " var_cov = np.cov(y,x) # 分散共分散行列\n", " cov_yx = var_cov[0,1] # 共分散\n", " var_y = var_cov[0,0] # yの分散\n", " var_x = var_cov[1,1] # xの分散 \n", " \n", " b1hat = cov_yx / var_x # b1の推定値\n", " b0hat = np.mean(y)-b1hat*np.mean(x) #b0の推定値\n", " yhat = b0hat + b1hat*x # yの予測値\n", " uhat = y - yhat # 残差\n", " \n", " rss = np.sum(uhat**2) # 残差平方和\n", " sigma2 = rss/(n-2) # 回帰の残差(不偏)分散 \n", " ser = np.sqrt(sigma2) # 回帰の標準誤差\n", " \n", " b1se = ser/np.sqrt(n*np.var(x)) # b1の標準誤差\n", " \n", " q1 = (b1hat - b1)/b1se # q1の値\n", " \n", " q1_arr[j] = q1 # t値をarrayに追加\n", " \n", " return q1_arr # 返り値の設定" ] }, { "cell_type": "markdown", "metadata": { "hidden": true }, "source": [ "$n=5,\\;10,\\;30,\\;100$のシミュレーション" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "hidden": true }, "outputs": [], "source": [ "q1_norm = {} # 空の辞書の作成\n", "\n", "for i in [5,10,30,100]:\n", " q1_norm[str(i)] = sim_norm(i)" ] }, { "cell_type": "markdown", "metadata": { "hidden": true }, "source": [ "`plot`するための関数を用意する。" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "hidden": true }, "outputs": [], "source": [ "def plot_dist(n, q1_arr): # n=標本の大きさ,q1_arr=q1値のarray\n", " \n", " xx=np.linspace(-3,3,num=100) # 図を作成するために横軸の値を設定\n", "\n", " kde_model=gaussian_kde(q1_arr) # カーネル密度推定を使いt値の分布を推定\n", " \n", " t_dist = t.pdf(xx,df=n-2) # 同じ自由度のt分布\n", " \n", " plt.plot(xx, kde_model(xx), 'g-') # t値の分布プロット\n", " plt.plot(xx, t_dist,'b:') # t分布\n", " plt.ylabel('Kernel Density') # 縦軸のラベル\n", " plt.title('n = {0}'.format(n)) # タイトル" ] }, { "cell_type": "markdown", "metadata": { "hidden": true }, "source": [ "4つの図を並べて表示してみよう。\n", "* 実線:$q_1$値の分布(カーネル密度推定)\n", "* 点線:自由度`n-2`の$t$分布" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "hidden": true }, "outputs": [], "source": [ "plt.figure(figsize=(10, 8))\n", "\n", "# n = 5\n", "plt.subplot(221)\n", "plot_dist(5, q1_norm['5'])\n", "\n", "# n = 10\n", "plt.subplot(222)\n", "plot_dist(10, q1_norm['10'])\n", "\n", "# n = 30\n", "plt.subplot(223)\n", "plot_dist(30, q1_norm['30'])\n", "\n", "# n = 100\n", "plt.subplot(224)\n", "plot_dist(100, q1_norm['100'])\n", "pass" ] }, { "cell_type": "markdown", "metadata": { "hidden": true }, "source": [ "(結果:小標本特性)\n", "\n", "実線と点線は殆ど同じ。即ち,$q_1$は$t_{n-2}$分布に従っている。" ] }, { "cell_type": "markdown", "metadata": { "heading_collapsed": true, "hidden": true }, "source": [ "### 誤差項の正規性が満たされない場合" ] }, { "cell_type": "markdown", "metadata": { "hidden": true }, "source": [ "上のシミュレーションと違う点は,$u$は次の分布関数から抽出されると仮定する点である。\n", "\n", "$$\n", "u=\\frac{v-1}{\\sqrt{2}},\\qquad v\\sim\\chi^2(1)\n", "$$\n", "\n", "これは一致性のシミュレーションで使った分布関数と同じである。これにより仮定6が満たされない。\n", "\n", "この仮定を導入するために,上で定義した`sim_norm()`関数の代わりに`sim_non_normal()`を定義する。`sim_normal()`と異なるのは次の一行だけである。\n", "```\n", "u = (np.random.chisquare(1, size=n) - 1) / np.sqrt(2)\n", "\n", "```" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "hidden": true }, "outputs": [], "source": [ "@njit # 関数の高速化\n", "def sim_non_norm(n): # n=標本の大きさ\n", " \n", " q1_arr = np.zeros(N) # q1を入れる空array\n", " \n", " x = np.random.normal(loc=4, scale=1, size=n) # N回の推定に同じ説明変数を使う\n", "\n", " for j in range(N): # N 回のループ\n", " \n", " ###### 非正規分布の誤差項(次の行だけが異なる)######\n", " u = (np.random.chisquare(1, size=n) - 1) / np.sqrt(2)\n", " \n", " y = b0 + b1 * x + u # 説明変数\n", " \n", " var_cov = np.cov(y,x) # 分散共分散行列\n", " cov_yx = var_cov[0,1] # 共分散\n", " var_y = var_cov[0,0] # yの分散\n", " var_x = var_cov[1,1] # xの分散 \n", " \n", " b1hat = cov_yx / var_x # b1の推定値\n", " b0hat = np.mean(y)-b1hat*np.mean(x) #b0の推定値\n", " yhat = b0hat + b1hat*x # yの予測値\n", " uhat = y - yhat # 残差\n", " \n", " rss = np.sum(uhat**2) # 残差平方和\n", " sigma2 = rss/(n-2) # 回帰の残差(不偏)分散 \n", " ser = np.sqrt(sigma2) # 回帰の標準誤差\n", " \n", " b1se = ser/np.sqrt(n*np.var(x)) # b1の標準誤差\n", " \n", " q1 = (b1hat - b1)/b1se # q1の値\n", " \n", " q1_arr[j] = q1 # t値をarrayに追加\n", " \n", " return q1_arr # 返り値の設定" ] }, { "cell_type": "markdown", "metadata": { "hidden": true }, "source": [ "$n=5,\\;10,\\;30,\\;100$のシミュレーション" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "hidden": true }, "outputs": [], "source": [ "q1_non_norm = {} # 空の辞書の作成\n", "\n", "for i in [5,10,30,100]:\n", " q1_non_norm[str(i)] = sim_non_norm(i)" ] }, { "cell_type": "markdown", "metadata": { "hidden": true }, "source": [ "4つの図を並べて表示してみる。\n", "* 実線:$q_1$の分布(カーネル密度推定)\n", "* 点線:自由度$n-2$の$t$分布" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "hidden": true }, "outputs": [], "source": [ "plt.figure(figsize=(10, 8))\n", "\n", "# n = 5\n", "plt.subplot(221)\n", "plot_dist(5, q1_non_norm['5'])\n", "\n", "# n = 10\n", "plt.subplot(222)\n", "plot_dist(10, q1_non_norm['10'])\n", "\n", "# n = 30\n", "plt.subplot(223)\n", "plot_dist(30, q1_non_norm['30'])\n", "\n", "# n = 100\n", "plt.subplot(224)\n", "plot_dist(100, q1_non_norm['100'])\n", "pass" ] }, { "cell_type": "markdown", "metadata": { "hidden": true }, "source": [ "(結果:大標本特性)\n", "\n", "標本の大きさが小さい場合($n=5,10$),$q_1$の分布は$t_{n-2}$分布から乖離している。一方で,標本の大きさが大きくなるにつれて$q_1$の分布は$t_{n-2}$分布に近づいていくのが確認できる。これが「大標本特性」である。標本の大きさが十分に大きい場合,$t$検定は有効であることが確認できる。同じことが$F$検定にも当てはまる。\n", "\n", "このシミュレーションの結果次第では,$n=30$でも概ね$t_{n-2}$分布に近くなる場合がある。しかし同じ結果がどの場合にも当てはまるわけではなく,シミュレーションの設定が変わると異なる結果になることを覚えておこう。" ] }, { "cell_type": "markdown", "metadata": { "heading_collapsed": true }, "source": [ "## 正規性の確認" ] }, { "cell_type": "markdown", "metadata": { "hidden": true }, "source": [ "### qqプロット" ] }, { "cell_type": "markdown", "metadata": { "hidden": true }, "source": [ "`qq`プロットの`q`は`quantile`(分位数)のこと。横軸に正規分布の理論値を,縦軸にはデータの値を並べる。データが正規分布に従っている場合,データは45度線付近に分布することになる。\n", "\n", "以下では`statsmodels`の`qqplot`を使って説明する。(`lmdiag`パッケージでもよい)" ] }, { "cell_type": "markdown", "metadata": { "hidden": true }, "source": [ "**正規分布**から生成したデータのケース。\n", "* 赤い線は45度線\n", "* オプション`line='45'`:45度線を指定\n", "* オプション`fit=True`:データの平均と標準偏差を使って標準化する" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "hidden": true }, "outputs": [], "source": [ "data_norm = np.random.normal(scale=5, size=500)\n", "qqplot(data_norm, line='45', fit=True)\n", "pass" ] }, { "cell_type": "markdown", "metadata": { "hidden": true }, "source": [ "**一様分布**から生成したデータの場合。45度線から乖離しているのが分かる。" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "hidden": true }, "outputs": [], "source": [ "data_uniform = np.random.uniform(size=500)\n", "qqplot(data_uniform, line='45', fit=True)\n", "pass" ] }, { "cell_type": "markdown", "metadata": { "hidden": true }, "source": [ "`wooldridge`パッケージにある`wage1`のデータを使ってみる。\n", "\n", "誤差項は`res_wage`の属性である`.resid`から取得できるので,それを`qqplot()`に使う。" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "hidden": true }, "outputs": [], "source": [ "wage1 = wooldridge.data('wage1')\n", "formula_wage = 'wage ~ educ + exper+ tenure'\n", "res_wage = smf.ols(formula_wage, data=wage1).fit()\n", "qqplot(res_wage.resid, line='45',fit=True)\n", "pass" ] }, { "cell_type": "markdown", "metadata": { "hidden": true }, "source": [ "被説明変数の`wage`に対数を取ると、こちらの方が当てはまりが良いことが分かる。" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "hidden": true }, "outputs": [], "source": [ "wage1 = wooldridge.data('wage1')\n", "formula_wage_log = 'np.log(wage) ~ educ + exper+ tenure'\n", "res_wage_log = smf.ols(formula_wage_log, data=wage1).fit()\n", "qqplot(res_wage_log.resid, line='45',fit=True)\n", "pass" ] }, { "cell_type": "markdown", "metadata": { "hidden": true }, "source": [ "### Jarque-Bera検定" ] }, { "cell_type": "markdown", "metadata": { "hidden": true }, "source": [ "分布の正規性の確認によく使われる検定を紹介する。\n", "\n", "$\\text{H}_0$:正規分布である\n", "\n", "$\\text{H}_A$:$\\text{H}_0$は成立しない\n", "\n", "正規性の判断には分布の以下の特徴に基づいている。\n", "* 歪度(わいど;Skewness):分布の左右の偏り\n", "* 尖度(せんど;Kurtosis):分布の「頂上」のとがり具合\n", "\n", "`statsmodels`のサブパッケージの一部に含まれている。" ] }, { "cell_type": "markdown", "metadata": { "hidden": true }, "source": [ "上で使った`data_norm`で試してみよう。" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "hidden": true }, "outputs": [], "source": [ "jarque_bera(data_norm)" ] }, { "cell_type": "markdown", "metadata": { "hidden": true }, "source": [ "返り値\n", "\n", "1. JB検定統計量\n", "2. JBの$p$値\n", "3. 歪度の推定値(正規分布の場合は0)\n", "4. 尖度の推定値(正規分布の場合には3になるように設定されている)\n", "\n", "この例では$p$値が高いため$\\text{H}_0$は棄却できない。" ] }, { "cell_type": "markdown", "metadata": { "hidden": true }, "source": [ "---\n", "次に`data_uniform`を試してみよう。\n", "\n", "$p$値は非常に小さいため,1%有意水準でも$\\text{H}_0$を棄却できる。" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "hidden": true }, "outputs": [], "source": [ "jarque_bera(data_uniform)" ] }, { "cell_type": "markdown", "metadata": { "hidden": true }, "source": [ "---\n", "上で行った2つの回帰分析の結果を検定してみよう。" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "hidden": true }, "outputs": [], "source": [ "jarque_bera(res_wage.resid)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "hidden": true }, "outputs": [], "source": [ "jarque_bera(res_wage_log.resid)" ] }, { "cell_type": "markdown", "metadata": { "hidden": true }, "source": [ "---\n", "JB検定の結果は,回帰分析の結果の`summary()`に含まれている。\n", "* `Jarque-Bera (JB)`:JB検定統計量\n", "* `Prob(JB)`:JBの$p$値" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "hidden": true }, "outputs": [], "source": [ "print(res_wage_log.summary().tables[2])" ] }, { "cell_type": "markdown", "metadata": { "heading_collapsed": true, "hidden": true }, "source": [ "### Omnibus検定" ] }, { "cell_type": "markdown", "metadata": { "hidden": true }, "source": [ "正規性を確認するもう1つの検定を紹介する。\n", "\n", "$\\text{H}_0$:正規分布である\n", "\n", "$\\text{H}_A$:$\\text{H}_0$は成立しない\n", "\n", "BJ検定と同じように,正規性の判断には歪度(わいど;Skewness)と尖度(せんど;Kurtosis)に基づいている。\n", "\n", "---\n", "`statsmodels`のサブパッケージの一部に含まれている。" ] }, { "cell_type": "markdown", "metadata": { "hidden": true }, "source": [ "`data_norm`を使って試してみる。\n", "\n", "<返り値>\n", "* テスト統計量\n", "* $p$値" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "hidden": true }, "outputs": [], "source": [ "omni_normtest(data_norm)" ] }, { "cell_type": "markdown", "metadata": { "hidden": true }, "source": [ "$p$値は高いため,10%有意水準でも$\\text{H}_0$を棄却できない。" ] }, { "cell_type": "markdown", "metadata": { "hidden": true }, "source": [ "---\n", "次に`data_uniform`を試してみよう。\n", "\n", "$p$値は非常に小さいため,1%有意水準でも$\\text{H}_0$を棄却できる。" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "hidden": true }, "outputs": [], "source": [ "omni_normtest(data_uniform)" ] }, { "cell_type": "markdown", "metadata": { "hidden": true }, "source": [ "---\n", "上で行った2つの回帰分析の結果を検定してみよう。" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "hidden": true }, "outputs": [], "source": [ "omni_normtest(res_wage.resid)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "hidden": true }, "outputs": [], "source": [ "omni_normtest(res_wage_log.resid)" ] }, { "cell_type": "markdown", "metadata": { "hidden": true }, "source": [ "---\n", "Omnibus検定の結果は,回帰分析の結果の`summary()`に含まれている。\n", "* `Omnibus`:検定統計量\n", "* `Prob(Omnibus)`:$p$値" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "hidden": true }, "outputs": [], "source": [ "print(res_wage_log.summary().tables[2])" ] }, { "cell_type": "markdown", "metadata": { "heading_collapsed": true }, "source": [ "## $LM$検定" ] }, { "cell_type": "markdown", "metadata": { "hidden": true }, "source": [ "大標本の場合,$F$検定の代わりに$LM$検定も使える。ここでは`crime1`のデータを使って$LM$検定について説明する。`wooldridge`パッケージの`crime1`データを使おう。" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "hidden": true }, "outputs": [], "source": [ "crime1 = wooldridge.data('crime1')\n", "wooldridge.data('crime1', description=True)" ] }, { "cell_type": "markdown", "metadata": { "hidden": true }, "source": [ "`crime1`は1972年に初めて逮捕された成人で構成されたデータセットである。このデータを使い刑罰の犯罪に対する抑止力を検証する。\n", "\n", "被説明変数:\n", "* `narr86`:1986年に逮捕された回数\n", "\n", "説明変数\n", "* `pcnv`:1986年以前に逮捕され起訴につながった比率(逮捕を所与とし,起訴される確率もしくは期待を表している)\n", "* `ptime86`:1986年での服役期間(単位:月)(投獄による不自由さのコスト)\n", "* `qemp86`:雇用(単位:四半期)\n", "* `avgsen`:直近の懲役期間の平均(単位:月)\n", "* `tottime`:18歳以降の服役期間(単位:月)" ] }, { "cell_type": "markdown", "metadata": { "hidden": true }, "source": [ "制約がない場合の推定" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "hidden": true }, "outputs": [], "source": [ "form_0 = 'narr86 ~ pcnv + ptime86 + qemp86 + avgsen + tottime'\n", "res_0 = smf.ols(form_0, data=crime1).fit()\n", "res_0.params" ] }, { "cell_type": "markdown", "metadata": { "hidden": true }, "source": [ "仮説\n", "\n", "`avgsen = tottime = 0`" ] }, { "cell_type": "markdown", "metadata": { "hidden": true }, "source": [ "仮説が正しい(制約がある)場合の推定" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "hidden": true }, "outputs": [], "source": [ "form_1 = 'narr86 ~ pcnv + ptime86 + qemp86'\n", "res_1 = smf.ols(form_1, data=crime1).fit()\n", "res_1.params" ] }, { "cell_type": "markdown", "metadata": { "hidden": true }, "source": [ "---\n", "$\\text{H}_0$: `avgsen = tottime = 0`\n", "\n", "$\\text{H}_A$: $\\text{H}_0$は成立しない\n", "\n", "---\n", "`res_0`のメソッド`.compare_lm_test()`を使うと簡単に計算結果を表示できる。引き数は,仮説が正しい場合のOLS推定の結果`res_1`を使う。" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "hidden": true }, "outputs": [], "source": [ "res_0.compare_lm_test(res_1._results)" ] }, { "cell_type": "markdown", "metadata": { "hidden": true }, "source": [ "返り値(左から)\n", "* $LM$検定統計値\n", "* $p$値\n", "* 制限の数\n", "\n", "有意水準5%で帰無仮説は棄却できない。" ] } ], "metadata": { "kernel_info": { "name": "python3" }, "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.11.9" }, "nteract": { "version": "0.15.0" }, "toc": { "base_numbering": 1, "nav_menu": {}, "number_sections": true, "sideBar": true, "skip_h1_title": false, "title_cell": "Table of Contents", "title_sidebar": "Contents", "toc_cell": false, "toc_position": {}, "toc_section_display": true, "toc_window_display": false } }, "nbformat": 4, "nbformat_minor": 4 }