Home

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上では実行できない。WindowsWSLWindows 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オブジェクトdframepickleによりファイルに保存して、次のリスト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の引数とすればよい。リストでは、リスト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型であるが、リストでは

 

    real<lower = 0.0, upper = 1.0> theta;

 

と、real型である。

これに伴い、リストの実行によるMCMCサンプリングの結果の分析スクリプト(リスト)に変更がある。

また、Windowsでは、直接リスト3を実行することはできないので、WSL上のUbuntuで実行することはリスト1の場合と同じである。

リストは、直接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))

 

リストStanスクリプトは、リストの場合とパラメータthetaの宣言を少し変えてみた。

 

リスト4 Stanスクリプトファイル(Bin.stan

 

data{

    int N;

    int k;

}

parameters{

    real<lower = 0.0, upper = 1.0> theta;

}

model{

    k ~ binomial(N, theta);

}

 

リストMCMCサンプリングのスクリプトの実行後、サンプリングの分析スクリプト(リスト)を実行する。

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,サンプルサイズ)型の配列になっているので、リストにおけるthetaのサンプルの描画コードも、それに合わせて

 

sb.kdeplot(v_theta[0])

 

となっている。

リスト5を実行すると、まず図4のグラフが表示される。

図4

 

図1のグラフと異なるのは、図1ではsimplex型のパラメータtheta[1]theta[2]2つのパラメータの分布が描かれているが、図4ではreal型のパラメータthetaの分布が描かれているからである。

 

 

Home