Up

単回帰分析

2025.11CmdStanPy対応に改訂

 

 

StanPythonインターフェースCmdStanPy対応とした。CmdStanPyの簡単な説明を、このウェブサイトに用意した。

 

下図のデータdata.csvの単回帰分析を行う。

 

図1

 

すなわち、データにおいての関係を次式のモデル(1)で表すことを考える。

 

 

ここで、は誤差(残差)を表し、平均が0の正規分布に従うものとする。

 

 

は、平均0,分散の正規分布を表し、記号は、左辺の確率変数が右辺の分布に従うことを表す。

単回帰モデルによるベイズ分析を行うためのStanスクリプトをリスト1に示す。

 

リスト1 単回帰分析のStanスクリプト(simple_regression.stan

 

data {

    int N;

    array[N] real X;

    array[N] real Y;

}

parameters {

    real a;

    real b;

    real<lower = 0> sgm;

}

model {

    a ~ normal(0, 100);

    b ~ normal(0, 100);

    sgm ~ exponential(0.001);

    for (i in 1:N)

        Y[i] ~ normal(a + b*X[i], sgm);

}      

 

 

スクリプトファイルおよびデータファイルは、圧縮ファイルfilesSRegression.zipにまとめている。ダウンロード・展開すれば自由に利用できる。

Stanについては、岡本安晴(2019で説明している。

 

リスト1のStanスクリプトファイルを使って、図1のデータを分析するPythonスクリプトをリスト2に示す。

 

 

リスト2 単回帰分析のPythonスクリプト例(SimpleRegression.py

 

 

from cmdstanpy import CmdStanModel

import numpy as np

import pandas as pd

import matplotlib.pyplot as plt

import seaborn as sb

import arviz as az

 

df = pd.read_csv('data.csv')

data = df.values

X = data.T[1]

Y = data.T[2]

for x, y in zip(X, Y):

    print(x, y)

   

Data = {'N':len(X), 'X':X, 'Y':Y}

model = CmdStanModel(stan_file='simple_regression.stan')

fit = model.sample(data=Data)

 

print(fit.summary())

 

inf_data = az.from_cmdstanpy(fit)      #   Arviz DataFrame型に変換

az.plot_trace(inf_data)                #   トレースプロット

plt.tight_layout()

plt.show()

 

fit = fit.draws_pd()                   #   Pandas DataFrame型に変換

 

a_hat = np.median(fit['a'])

b_hat = np.median(fit['b'])

sb.kdeplot(fit['a'])

plt.title('a: median = {0:.2f}'.format(a_hat), fontsize = 18)

plt.savefig('Fig_a.png')

plt.show()

 

sb.kdeplot(fit['b'])

plt.title('b: median = {0:.2f}'.format(b_hat), fontsize = 18)

plt.savefig('Fig_b.png')

plt.show()

 

plt.plot(fit['b'], fit['a'], 'o')

plt.xlabel('b', fontsize = 20)

plt.ylabel('a', fontsize = 20)

plt.savefig('Fig_scatter_ab.png')

plt.show()

 

plt.plot(X, Y, 'o', label = 'Data')

plt.plot([min(X), max(X)], [a_hat + b_hat * min(X), a_hat + b_hat * max(X)],

        label = 'Regression Line')

plt.xlabel('X', fontsize = 16)

plt.ylabel('Y', fontsize = 16)

plt.legend(fontsize = 14)

plt.savefig('FigSRegress.png')

plt.show()

 

 

 

リスト1のStanスクリプトファイル(simple_regression.stan)、リスト2のPythonスクリプトファイル(SimpleRegression.py)、入力データファイル(data.csv)を同じフォルダに置く。そのフォルダにカレントディレクトリ(フォルダ)を移して、CmdStanPyのインストールされている環境で、次のコマンドを実行する。CmdStanPyの簡単な解説は、このウェブサイトに用意した。

 

(stan) *****/filesSRegression$ python SimpleRegression.py

 

上のコマンドを実行すると、Stanスクリプトファイルが読み込まれてコンパイルが始まる。コンパイルは多少時間が掛かる。コンパイルが終了すると、MCMCサンプリングが始まり、終了するとトレース図が表示される(図2)。

 

図2

 

左側に事後分布のグラフが描かれ、右側にサンプリングのトレースが描かれている。

図2のWindowを閉じると、図3のグラフが表示される。

 

図3 aの事後分布

 

パラメータaの事後分布のグラフである。

図3のWindowを閉じると、図4のグラフが表示される。

 

図4 bの事後分布

 

パラメータbの事後分布のグラフである。

図4のWindowを閉じると、図5のグラフが表示される。

 

図5 (b,a)の散布図

 

パラメータabMCMCサンプル(ba)の散布図である。強い相関が表れている。パラメータ間の相関を避ける方法として、中心化がある。

図5のWindowを閉じると、図6のグラフが表示される。

 

図6 回帰直線

 

データの散布図と回帰直線が描かれている。回帰直線の傾きが小さくなると、y切片の値が大きなるという図5で表されていることが納得できる。

図6のWindowを閉じると、スクリプトの実行終了である。

 

傾きby切片aの相関が無いようにする方法として、中心化がある。次に、中心化について説明する。

 

 

 

中心化(Centering

 

モデル(1)の定数a,、独立変数の平均値における値を示すようにするために、独立変数の中心化を行う。

 

 

この中心化されたZを独立変数として回帰分析を行うPythonスクリプトをリスト3に示す。

 

 

リスト3 中心化された独立変数による単回帰分析(SRegression_centered.py

 

 

from cmdstanpy import CmdStanModel

import numpy as np

import pandas as pd

import matplotlib.pyplot as plt

import seaborn as sb

import arviz as az

 

df = pd.read_csv('data.csv')

data = df.values

X = data.T[1]

Y = data.T[2]

for x, y in zip(X, Y):

    print(x, y)

 

Z = X - np.mean(X)

plt.plot(Z, Y, 'o')

plt.xlabel('Z', fontsize = 14)

plt.ylabel('Y', fontsize = 14)

plt.show()

 

Data = {'N':len(Z), 'X':Z, 'Y':Y}

 

model = CmdStanModel(stan_file='simple_regression.stan')

fit = model.sample(data=Data)

 

print(fit.summary())

 

inf_data = az.from_cmdstanpy(fit)      #   Arviz DataFrame型に変換

az.plot_trace(inf_data)                #   トレースプロット

plt.tight_layout()

plt.show()

 

fit = fit.draws_pd()                   #   Pandas DataFrame型に変換

 

a_hat = np.median(fit['a'])

b_hat = np.median(fit['b'])

sb.kdeplot(fit['a'])

plt.title('a: median = {0:.2f}'.format(a_hat))

plt.savefig('FigZ_a.png')

plt.show()

 

sb.kdeplot(fit['b'])

plt.title('b: median = {0:.2f}'.format(b_hat))

plt.savefig('FigZ_b.png')

plt.show()

 

plt.plot(fit['b'], fit['a'], 'o')

plt.xlabel('b', fontsize = 16)

plt.ylabel('a', fontsize = 16)

plt.savefig('FigZ_ab.png')

plt.show()

 

plt.plot(Z, Y, 'o')

plt.plot([min(Z), max(Z)], [a_hat + b_hat * min(Z), a_hat + b_hat * max(Z)],

        label = 'Regression Line')

plt.plot(0, a_hat, 's', c = 'r',

         label = (r'(0, a_hat)')  + ('\na_hat={0:.2f}'.format(a_hat)))

plt.xlabel(r'Z = X - $\bar{X}$', fontsize = 16)

plt.ylabel('Y', fontsize = 16)

plt.legend(fontsize = 14)

plt.savefig('FigZ_regress.png')

plt.show()

 

 

 

リスト3のPythonスクリプトファイル(SRegression_centered.y)、リスト1のStanスクリプトファイル(simple_regression.stan)、入力データファイル(data.csv:図1)を同じフォルダに置く。そのフォルダに、カレントディレクトリ(フォルダ)を移し、CmdStanPyのインストールされた環境において、次のコマンドを実行する。CmdStanPyの簡単な説明を、このウェブサイトに用意した。

 

(stan) *****/filesSRegression$ python SRegression_centered.py

 

上のコマンドを実行すると、データが読み込まれて図7の散布図が表示される。

 

図7 中心化されたデータ

 

横軸に、中心化された独立変数Zがとられている。

図7のWindowを閉じると、図8のグラフが表示される。

 

図8 トレース図

 

左側に、パラメータの事後サンプルのグラフ、右側にサンプリングのトレースが描かれている。

図8のWindowを閉じると、図9のグラフが表示される。

 

図9 aの事後分布

 

パラメータaの事後分布のグラフである。点推定値として中央値が示されている。Gelmanら(2021は、点推定値として、平均値より安定した中央値を勧めている。独立変数が中心化されているので、パラメータaは元のデータXの平均値における回帰直線の値である。

図9のWindowを閉じると、図10のグラフが表示される。

 

10 bの事後分布

 

中心化を行ってもスケールは変更していないので、パラメータbの値は中心化を行っていない図4とサンプリングによるランダムな変動を除いて同じである。

10Windowを閉じると、図11のグラフが表示される。

 

11 (b,a)の散布図

 

パラメータabの散布図である。中心化の効果としてパラメータの相関が消えている。

11Windowを閉じると、図12のグラフが表示される。

 

12 回帰直線

 

データの散布図と回帰直線である。回帰直線上の赤い小正方形は、回帰直線上の点を表している。独立変数の平均値における回帰直線の値は、である。

12Windowを閉じると、スクリプトの実行終了である。

 

 

 

参考文献

Gelman, A., Hill, J., & Vehtari, A. (2021). Regression and other stories. Cambridge University

 

 

 

 

 

Up