Home

PyStan 3による単回帰分析

 

拙著<岡本安晴「いまさら聞けないPythonでデータ分析」丸善出版>の第11章で扱ったPyStan 2による単回帰分析StanスクリプトをPyStan 3で実行した。Stanスクリプトは変更なしで用いることが出来たが、PythonスクリプトはPyStan 3用に書き換えた。書き換えたスクリプトを、リスト1に、Stanスクリプト(拙著、リスト11.1)をリスト2に示す。スクリプトファイルは、圧縮ファイルfiles_reg.zipにまとめた。

リスト1では、PyStan 3によるビルドにおいてStanスクリプトがファイルから読み込んだ文字列で与えられている。すなわち

 

with open('Regression.stan', 'r') as f:

    sm = stan.build(f.read(), data = Data)

 

である。PyStan 3では、Stanスクリプトは文字列として関数stan.build()の第1パラメータとして設定する。また、データも関数stan.build()のパラメータとして設定する。

なお、PyStan 3のインストールは、AnacondaPython 3.10仮想環境では

 

pip install pystan

 

であるが、Pythonスクリプトにおけるimportでは

 

import stan

 

である。

 

 

リスト1 PyStan 3によるMCMCサンプリングを行うスクリプト(Regression_PS3.py

 

 

import stan

import pickle

import numpy as np

import arviz as az

 

RawData = [ ['City',     'Temperature', 'Latitude'],

            ['Sapporo',     15,             43.1],

            ['Sendai',      20,             38.3],

            ['Niigata',     17,             37.9],

            ['Kanazawa',    18,             36.6],

            ['Tokyo',       24,             35.7],

            ['Osaka',       22,             34.7],

            ['Fukuoka',     22,             33.6],

            ['Kochi',       24,             33.6],

            ['Kagoshima',   23,             31.6],

            ['Naha',        26,             26.2]

]

 

ID = []

Y = []

X = []

N = len(RawData) - 1

for i in range(N):

    ID.append(RawData[i + 1][0])

    Y.append(RawData[i + 1][1])

    X.append(RawData[i + 1][2])

 

for v in zip(ID, Y, X):

    print(v)

 

Data = {'N': N, 'Y': Y, 'X': X}

with open('Regression.stan', 'r') as f:

    sm = stan.build(f.read(), data = Data)

 

fit = sm.sample()

 

print('fit =\n', fit)

infrnc_d = az.from_pystan(posterior = fit, posterior_model = sm )

print('\nSummary =\n', az.summary(infrnc_d))

 

with open('infrnc_d.pkl', 'wb') as f:

    pickle.dump(infrnc_d, f)

 

d_frame = fit.to_frame()

print(d_frame.keys())

with open('d_frame.pkl', 'wb') as f:

    pickle.dump(d_frame, f)

 

 

リスト1では、次のコード

with open('Regression.stan', 'r') as f:

    sm = stan.build(f.read(), data = Data)

 

によりStanスクリプトのビルドを行い、MCMCサンプリングは次のコードで行っている。

 

fit = sm.sample()

 

MCMCサンプリングの結果は、コード

 

infrnc_d = az.from_pystan(posterior = fit, posterior_model = sm )

print('\nSummary =\n', az.summary(infrnc_d))

 

with open('infrnc_d.pkl', 'wb') as f:

    pickle.dump(infrnc_d, f)

 

により、FitオブジェクトをInferenceDataオブジェクトにまとめ、pickleでファイルに保存して、次のリスト3で用いる。

また、InferenceDataオブジェクトに纏められたものから関数az.summary()によってMCMCサンプリングの統計量を求めている。

 

次のコードでは、MCMCサンプリングの結果をPandas DataFrameオブジェクトにまとめて、pickleで保存している。Stanスクリプトにおけるパラメータ名とDataFrameでのキーとの対応は、print文の出力で確認できる。

 

d_frame = fit.to_frame()

print(d_frame.keys())

with open('d_frame.pkl', 'wb') as f:

    pickle.dump(d_frame, f)

 

 

リスト2 Stanスクリプト(Regression.stan

 

 

 

data{

    int N;

    real Y[N];

    real X[N];

    }

parameters{

    real b0;

    real b1;

    real<lower = 0> sgm;

}

transformed parameters{

    real mu[N];

    for (i in 1:N)

        mu[i] = b0 + b1 * X[i];

}

model{

    for (i in 1:N)

        Y[i] ~ normal(mu[i], sgm);

}

 

 

リスト1の実行後、リスト3を実行する。ただし、Windowsの場合は、リスト1は直接Windows上では実行できない。WindowsWSLWindows Subsystem for Linux)を用意して、その上にインストールされたUbuntu上でPyStan3を用いる。詳しくはこのウェブサイトを参照されたい。PyStan 3は、直接Windows上で実行できないので、リスト1とリスト3に分けたが、Windows以外では分ける必要はない。

 

 

リスト3 リスト1のスクリプトに続いて実行するスクリプト(DrawRegression_PS3.py

 

import pickle

import matplotlib.pyplot as plt

import scipy.stats as scst

import numpy as np

import arviz as az

 

RawData = [ ['City',     'Temperature', 'Latitude'],

            ['Sapporo',     15,             43.1],

            ['Sendai',      20,             38.3],

            ['Niigata',     17,             37.9],

            ['Kanazawa',    18,             36.6],

            ['Tokyo',       24,             35.7],

            ['Osaka',       22,             34.7],

            ['Fukuoka',     22,             33.6],

            ['Kochi',       24,             33.6],

            ['Kagoshima',   23,             31.6],

            ['Naha',        26,             26.2]

]

 

ID = []

Y = []

X = []

N = len(RawData) - 1

for i in range(N):

    ID.append(RawData[i + 1][0])

    Y.append(RawData[i + 1][1])

    X.append(RawData[i + 1][2])

 

for v in zip(ID, Y, X):

    print(v)

 

with open('infrnc_d.pkl', 'rb') as f:

    infrnc_d = pickle.load(f)

print('Summary...\n', az.summary(infrnc_d))

 

with open('d_frame.pkl', 'rb') as f:

    d_frame = pickle.load(f)

 

print('Keys...\n', d_frame.keys())

 

plt.hist(d_frame['b0'])

plt.title('MCMC sample of b0', fontsize = 20)

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

plt.savefig('Figb0.png')

plt.show()

 

plt.hist(d_frame['b1'])

plt.title('MCMC sample of b1', fontsize = 20)

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

plt.savefig('Figb1.png')

plt.show()

 

plt.hist(d_frame['sgm'])

plt.title('MCMC sample of sgm', fontsize = 20)

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

plt.savefig('Figsgm.png')

plt.show()

 

r = scst.pearsonr(d_frame['b0'], d_frame['b1'])[0]

print('r = ', r)

plt.xlabel('b0')

plt.ylabel('b1')

plt.title('r = {0:.3f}'.format(r), fontsize = 24)

plt.plot(d_frame['b0'], d_frame['b1'], 'b.')

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

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

plt.savefig('Figb0b1.png')

plt.show()

 

k_names = [f'mu.{i}' for i in range(1,11)]

print(k_names)

MedPreds = []

for i in range(10):

    MedPreds.append(np.percentile(d_frame[k_names[i]], 50))

 

plt.xlabel('Data')

plt.ylabel('Prediction')

plt.plot(Y, MedPreds, 'bo')

for nm, y, pred in zip(ID, Y, MedPreds):

    plt.text(y + 0.1, pred + 0.1, nm)

plt.plot([14, 28], [14, 28], 'b-')

plt.savefig('FigPrdctn.png')

plt.show()

 

 

リスト3は、拙著のリスト11.2の後半に対応する処理を行うものである。リスト3を実行すると、まず図1のグラフが表示される。

図1

 

これは、次のコード

 

with open('d_frame.pkl', 'rb') as f:

    d_frame = pickle.load(f)

 

で、DataFrameのデータを読み込んで、次のコードで描画したものである。

 

plt.hist(d_frame['b0'])

plt.title('MCMC sample of b0', fontsize = 20)

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

plt.savefig('Figb0.png')

plt.show()

 

図1のフォームを閉じると、図2のグラフが表示される。

図2

 

図2のフォームを閉じると図3のグラフが表示される。

図3

 

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

図4

 

図4は、以下のコードで描画されている。

 

r = scst.pearsonr(d_frame['b0'], d_frame['b1'])[0]

print('r = ', r)

plt.xlabel('b0')

plt.ylabel('b1')

plt.title('r = {0:.3f}'.format(r), fontsize = 24)

plt.plot(d_frame['b0'], d_frame['b1'], 'b.')

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

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

plt.savefig('Figb0b1.png')

plt.show()

 

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

図5

 

横軸がデータ値、縦軸が予測値である。

図5のフォームを閉じると、スクリプトの実行終了である。

 

 

Home