この文書はブラウザ上で稼働するPython環境であるJupyter Notebookを利用して作成しています。
Windowsでは以下の実行のためのPython環境はAnacondaをインストールすると良いです。Python環境に加え、numpy、scipy、matplotlibなどのライブラリや、このノートブックに実行に必要なJupyter Notebookがインストールされるので、インストーラをダブルクリックし、指示に従いインストールを行った後、Jupyter Notebookのアイコンで起動してください。
import numpy as np
import scipy as sp
import scipy.stats
import pandas as pd
%matplotlib inline
import matplotlib.pyplot as plt
plt.style.use('ggplot')
データは以下のディレクトリからダウンロードしてください。以下の読み込み時にはファイルパスは適宜、自分でダウンロードした場所に変更してください。
https://art.ist.hokudai.ac.jp/~takigawa/course/da2016/pdf/dataset.csv
data = pd.read_csv('/Users/takigawa/Documents/artist/course/da2016/pdf/dataset.csv', header=None)
データの中身は以下のようになっています。最初の行がヘッダではないので読み込み時に header=Noneが必要なことに注意してください。
data.columns = ['番号', '新生時の体重', '母親の体重', '母親の年齢', '懐妊期間(日)', '喫煙習慣の有無']
data
データの最初のみを見るには head() を利用します。
data.head()
データの方は pandas のデータフレームになっているので、これをnumpy配列にしておきます。
type(data)
x = data.as_matrix()
type(x)
ここでは目的変数の値を格納するベクトル y および説明行列を格納する行列 X を作成します。
まず、目的変数はxの1列目をそのまま取り出すだけです。
y = np.array(x[:, 1])
つづいて、説明変数行列を作成するので、読み込んだデータの行数、列数を取り出します。yを含む行と、最初のid行の2行分は説明変数ではないため、説明変数の数pは2を引いておきます。
n, p = x.shape
p -= 2
[n, p]
ここで一度numpy配列であるxの中身を確認しておきます。
x
1列目(index=0)はid行、2列目(index=1)は目的変数の値なので、index=2から列をp列選択して、つまり、index=2からindex=p+2まで必要部分を取り出します。
z = x[:, 2:(p+2)]
pd.DataFrame(z).head()
さらに定数項のための1ベクトルを最初の列に加えた行列を作成します。
X = np.column_stack((np.ones(n), z))
pd.DataFrame(X).head()
行列公式
を用いて偏回帰係数を求める。
beta = np.dot(np.dot(np.linalg.inv(np.dot(X.T, X)), X.T), y)
pd.DataFrame([beta],
columns = ['定数項', '変数1', '変数2', '変数3', '変数4'],
index = ['偏回帰係数'])
上記の係数を説明行列Xにかけて、yの予測値を計算します。
y_pred = np.dot(X, beta)
yの予測値をx軸に、yの真値をy軸にとって散布図をプロットしてみます。
plt.scatter(y_pred, y)
plt.plot([2400, 4000], [2400, 4000])
plt.ylim(2400, 4000)
plt.xlim(2400, 4000)
plt.ylabel('groundtruth')
plt.xlabel('prediction')
まずはyの要素の平均値を求めておきます。
y_bar = np.mean(y)
次に、平方和(Sum of Squares, SS)を三種類計算します。
全変動(SST)は以下の式で定義されます。
sst = np.sum([ (y[i] - y_bar)**2 for i in range(len(y)) ])
残差変動(SSE)は以下の式で定義されます。
sse = np.sum([ (y[i] - y_pred[i])**2 for i in range(len(y)) ])
回帰変動(SSR)は以下の式で定義されます。
ssr = np.sum([ (y_pred[i] - y_bar)**2 for i in range(len(y)) ])
pd.DataFrame([[y_bar, sst, sse, ssr]],
columns = ['$\overline y$', '全変動', '残差変動', '回帰変動'],
index = ['value'])
残差変動を自由度で割ると誤差分散が求められます。その平方根が標準偏差になります。
sig2 = sse/(n-p-1)
回帰変動と全変動の比は「決定係数」という量になります。その平方根が重相関係数になります。
r2 = ssr/sst
また下記のように自由度で割ったものから計算した決定係数を自由度調整済み決定係数と呼びます。
r2_star = 1-(sse/(n-p-1))/(sst/(n-1))
pd.DataFrame([[sig2, np.sqrt(sig2), r2, r2_star, np.sqrt(r2)]],
columns = ['誤差分散', '標準誤差', '決定係数', '(自由調整済)', '重相関'],
index = ['value'])
決定係数の値は「1-残差変動/全変動」と等しくなります。
1-sse/sst
また重相関係数の値は、yの真値とyの予測値の相関係数と等しくなります。
np.corrcoef(y, y_pred)[0, 1]
この相関係数を定義から計算すると以下のようになります。
u = y_pred - np.mean(y_pred)
v = y - np.mean(y)
np.dot(u, v)/(np.linalg.norm(u) * np.linalg.norm(v))
行列公式を用いて、
偏回帰係数の推定量の分散共分散行列を計算します。
S = sig2 * np.linalg.inv(np.dot(X.T, X))
pd.DataFrame(S)
この行列の対角要素をとってきたものが各々の偏回帰係数の推定量の標準誤差になります。また、各々の偏回帰係数の推定値を標準誤差で割ったものがt比になります。
err = np.sqrt(S.diagonal())
t_stat = beta / err
pd.DataFrame([err, t_stat],
columns = ['定数項', '変数1', '変数2', '変数3', '変数4'],
index = ['標準誤差', 't比'])
上記手続きで計算したt比は、係数=0の仮説の元では自由度n-p-1のt分布に従うので、この事実を利用して、t検定によるp値、および、各々の偏回帰係数の95%信頼区間を計算します。両側検定するとすると、片側で裾確率 P(t>|X|) を求めて2倍したものがp値になります。
pval = 2 * sp.stats.t.sf(np.abs(t_stat), df = n-p-1)
t分布において両側の裾確率 P(|X|>alpha) がちょうど0.05になる点alphaを計算します。両側の和が0.05なので、片側では各々0.05/2になる点にあたります。
alpha = sp.stats.t.ppf(0.05/2, df = n-p-1)
このalphaを用いると以下の形で簡単に信頼区間(CI, Confidence Interval)が計算できます。
ci_lower = beta - np.abs(alpha) * err
ci_upper = beta + np.abs(alpha) * err
結果は以下のようになります。
pd.DataFrame([pval, ci_lower, ci_upper],
columns = ['定数項', '変数1', '変数2', '変数3', '変数4'],
index = ['p値', '95%CI下限', '95%CI上限'])
回帰分析の結果は以下のようになります。
pd.DataFrame([[np.sqrt(r2), r2, r2_star, np.sqrt(sig2), n, p]],
columns = ['重相関', '決定係数', '調整済', '標準誤差', '例数', '説明変数'],
index = ['value'])
pd.DataFrame([beta, err, t_stat, pval, ci_lower, ci_upper],
columns = ['定数項', '変数1', '変数2', '変数3', '変数4'],
index = ['偏回帰係数', '標準誤差', 't比', 'p値', '95%CI下限', '95%CI上限'])