PyStan 3による2項分布の分析
拙著<岡本安晴「いまさら聞けないPythonでデータ分析」丸善出版>の第10章で扱ったPyStan 2による2項分布の分析StanスクリプトをPyStan 3で実行した。Stanスクリプトは変更なしで用いることが出来たが、PythonスクリプトはPyStan 3用に書き換えた。書き換えたスクリプトを、リスト1とリスト2に示す。スクリプトファイルは、圧縮ファイルfiles_bin.zipにまとめた。
リスト1は、Stanスクリプトが文字列で与えられている場合である。Stanスクリプトを別ファイルとして用意する例は、本ウェブサイトの後半に示す。
リスト1 PyStan 3によるMCMCサンプリングを行うスクリプト(BinStr.py)
import stan
import arviz as az
import pickle
Stan_Model = """
data{
int N;
int k;
}
parameters{
simplex[2] theta;
}
model{
k ~ binomial(N, theta[1]);
}
"""
Data = {'N': 10, 'k': 7}
sm = stan.build(Stan_Model, data =
Data)
fit = sm.sample()
with open('v_theta.pkl', 'wb') as f:
pickle.dump(fit['theta'], f)
dframe = fit.to_frame()
print('Keys...\n', dframe.keys())
with open('dframe.pkl', 'wb') as f:
pickle.dump(dframe, f)
inference_data =
az.from_pystan(posterior = fit, posterior_model = sm)
with open('inf_data.pkl', 'wb') as f:
pickle.dump(inference_data,
f)
print(az.summary(inference_data))
リスト2 リスト1に続いて実行するスクリプト(DrawBinStr.py)
import arviz as az
import matplotlib.pyplot as plt
import seaborn as sb
import pickle
with open('dframe.pkl', 'rb') as f:
dframe = pickle.load(f)
print('keys...\n', dframe.keys())
with open('inf_data.pkl', 'rb') as f:
inference_data =
pickle.load(f)
summary = az.summary(inference_data)
print('Summary:\n', summary)
az.plot_trace(inference_data)
plt.tight_layout()
plt.savefig('FigTrace.png')
plt.show()
print(dframe)
sb.kdeplot(dframe['theta.1'])
plt.title("KDEPlot of
dframe['theta.1]", fontsize = 16)
plt.savefig('Fig1.png')
plt.show()
with open('v_theta.pkl', 'rb') as f:
v_theta = pickle.load(f)
print(v_theta[:5])
sb.kdeplot(v_theta[0])
plt.title('KDEPlot of v_theta')
plt.savefig('Fig2.png')
plt.show()
リスト1を実行してからリスト2を実行する。ただし、Windowsの場合は、リスト1は直接Windows上では実行できない。WindowsにWSL(Windows Subsystem for Linux)を用意して、その上にインストールされたUbuntu上でPyStan3を用いる。詳しくはこのウェブサイトを参照されたい。PyStan 3は、直接Windows上で実行できないので、リスト1とリスト2に分けたが、Windows以外では分ける必要はない。
リスト1では、PyStan 3は次のコードで実行されている。
sm = stan.build(Stan_Model, data =
Data)
fit = sm.sample()
ここで、PyStan 3は次のimport文
import stan
でimportされていることに注意。
PyStan 3では、まず、関数buildによってStanスクリプトのコンパイルとデータを設定したビルドを行い、Modelオブジェクトを作成する。
次に、Modelオブジェクトの関数sample()の呼び出してMCMCサンプリングを行い、結果をFitオブジェクトとして返す。
パラメータthetaの事後分布のサンプル値fit['theta']を次のコードでファイルに保存している。
ith open('v_theta.pkl', 'wb') as f:
pickle.dump(fit['theta'], f)
Fitオブジェクトの関数to_frame()は、Fitオブジェクトの内容をPandas DataFrameにまとめるものである。
dframe = fit.to_frame()
DataFrame dframeのキーは、パラメータ名に対応していて、リスト1の場合、次のコード
print('keys...\n', dframe.keys())
を実行すると、以下のように出力を得る。
Keys...
Index(['lp__', 'accept_stat__',
'stepsize__', 'treedepth__', 'n_leapfrog__',
'divergent__', 'energy__', 'theta.1', 'theta.2'],
dtype='object',
name='parameters')
次のパラメータ
simplex[2] theta;
は、キーとして
'theta.1', 'theta.2'
で表されていることが分かる。
このDataFrameオブジェクトdframeをpickleによりファイルに保存して、次のリスト2で用いる。
次のコード
inference_data =
az.from_pystan(posterior = fit, posterior_model = sm)
は、FitオブジェクトをInferenceDataオブジェクトにまとめるものであり、これもpickleでファイルに保存して、次のリスト2で用いる。
リスト2のスクリプトは、直接Windows上で実行できる。リスト1とリスト2のスクリプトファイルは、同じフォルダ内に置いておくと、pickleで扱うファイルも同じフォルダ内で扱うことが出来て便利である。
リスト1の実行後、リスト2を実行すると図1のグラフが表示される。
図1
これは、次のコード
with open('inf_data.pkl', 'rb') as f:
inference_data =
pickle.load(f)
で読み込んだものを、次のコード
az.plot_trace(inference_data)
で描画したものである。
図1のフォームを閉じると、図2のグラフが表示される。
図2
これは、次のコード
with open('dframe.pkl', 'rb') as f:
dframe = pickle.load(f)
で読み込んだものを、次のコード
sb.kdeplot(dframe['theta.1'])
で描画したものである。
図2のフォームを閉じると、図3のグラフ表示される。
図3
これは、次のコード
with open('v_theta.pkl', 'rb') as f:
v_theta = pickle.load(f)
で読み込んだものを、次のコード
sb.kdeplot(v_theta[0])
で描画したものである。
図3のフォームを閉じると、プログラムの実行終了である。
StanスクリプトをPythonスクリプトファイルとは別のファイルとして用意する場合
PyStan 3のビルドでは、Stanコードは文字列で与えられるが、Stanコードをファイルとして用意している場合は、そのスクリプトファイルを読み込んで、文字列としてbuildの引数とすればよい。リスト3では、リスト4のStanコードのスクリプトファイルを読み込んで、ビルドを行っている。スクリプトファイルは、圧縮ファイルfiles_bin.zipにまとめているので自由にダウンロード展開して利用されたい。
Stanスクリプトがファイルとして用意されているときは、ファイルを読み込んで文字列としてbuildの第1パラメータに指定すればよい。すなわち、
with open('Bin.stan', 'r') as f:
sm = stan.build(f.read(),
data = Data)
である。
後は、リスト1の場合と同様であるが、リスト1ではパラメータthetaが
simplex[2] theta;
と、simplex型であるが、リスト4では
real<lower = 0.0, upper =
1.0> theta;
と、real型である。
これに伴い、リスト3の実行によるMCMCサンプリングの結果の分析スクリプト(リスト5)に変更がある。
また、Windowsでは、直接リスト3を実行することはできないので、WSL上のUbuntuで実行することはリスト1の場合と同じである。
リスト5は、直接Windows上で実行できる。
リスト3 PyStan 3のビルドにおいて、Stanコードをファイルから読み込む場合(BinFile.py)
import stan
import arviz as az
import pickle
Data = {'N': 10, 'k': 7}
with open('Bin.stan', 'r') as f:
sm = stan.build(f.read(),
data = Data)
fit = sm.sample()
with open('v_theta.pkl', 'wb') as f:
pickle.dump(fit['theta'], f)
print('fit...\n', fit['theta'])
dframe = fit.to_frame()
print('Keys...\n', dframe.keys())
with open('dframe.pkl', 'wb') as f:
pickle.dump(dframe, f)
inference_data =
az.from_pystan(posterior = fit, posterior_model = sm)
with open('inf_data.pkl', 'wb') as f:
pickle.dump(inference_data,
f)
print(az.summary(inference_data))
リスト4のStanスクリプトは、リスト1の場合とパラメータthetaの宣言を少し変えてみた。
data{
int N;
int k;
}
parameters{
real<lower = 0.0, upper =
1.0> theta;
}
model{
k ~ binomial(N, theta);
}
リスト3のMCMCサンプリングのスクリプトの実行後、サンプリングの分析スクリプト(リスト5)を実行する。
Windowsでは、リスト3のスクリプトは、Anaconda/Ubuntu/WSL/Windowsに用意したPython 3.10仮想環境において実行したが、リスト5は直接Windows上で実行できる。
リスト5 リスト3によるMCMCサンプリングの結果の処理(DrawBinFile.py)
import arviz as az
import matplotlib.pyplot as plt
import seaborn as sb
import pickle
with open('dframe.pkl', 'rb') as f:
dframe = pickle.load(f)
print('Keys...\n', dframe.keys())
with open('inf_data.pkl', 'rb') as f:
inference_data =
pickle.load(f)
summary = az.summary(inference_data)
print('Summary:\n', summary)
az.plot_trace(inference_data)
plt.tight_layout()
plt.show()
print(dframe)
sb.kdeplot(dframe['theta'])
plt.title("KDEPlot
dframe['theta']", fontsize = 16)
plt.savefig('Fig1.png')
plt.show()
with open('v_theta.pkl', 'rb') as f:
v_theta = pickle.load(f)
print(v_theta)
sb.kdeplot(v_theta[0])
plt.title("KDEPlot
v_theta[0]", fontsize = 16)
plt.savefig('Fig2.png')
plt.show()
リスト3のコード
with open('v_theta.pkl', 'wb') as f:
pickle.dump(fit['theta'], f)
で保存したデータは、次のコード
print('fit...\n', fit['theta'])
の出力
fit...
[[0.73891671 0.44881966 0.68348178 ...
0.75439311 0.77459035 0.65838827]]
に示されるように、(1,サンプルサイズ)型の配列になっているので、リスト5におけるthetaのサンプルの描画コードも、それに合わせて
sb.kdeplot(v_theta[0])
となっている。
リスト5を実行すると、まず図4のグラフが表示される。
図4
図1のグラフと異なるのは、図1ではsimplex型のパラメータtheta[1]とtheta[2]の2つのパラメータの分布が描かれているが、図4ではreal型のパラメータthetaの分布が描かれているからである。