Up

2つの独立なデータの平均値の分析

t分布に基づくベイズ分析

 

正規分布の観点から見ると外れ値と見做される値も、t分布のように裾の厚い分布においては他の値と同様に扱われる(Martin, 2018)。

t分布は、次式で与えられる(Gelman et al., 2014)。

 

 

は、自由度(degrees of freedom, df)と呼ばれることもあるパラメータである。

パラメータの幾つかの値に対するt分布と正規分布のグラフを図1に示す。

図1

 

パラメータの値が小さいとき裾の厚い分布であるが、の値が大きくなると正規分布に近付いている。

確率分布としてt分布を用いるベイズ分析のスクリプトを、リストリストに分けて用意した。スクリプトファイルなどは圧縮ファイルtest_t_distri_files.zipとしてまとめた。

リスト1のスクリプトは、Windows上では直接実行できない。Windowsで実行するときは、WindowsWSLWindows Subsystem for Linux)をインストールして、WSL上にインストールされたUbuntu上でリスト1のスクリプトを実行する。Ubuntu上での実行は、miniconda上に用意したPython3.12仮想環境で行った。すなわち、リスト1のスクリプトは、Python3.12/miniconda/Ubuntu/WSL/Windows 10で実行した。Minicondaの使用は、Anacondaと同様である。このウェブサイトを参照されたい。

リスト1のスクリプトでMCMCサンプリングを行った後、リスト2のスクリプトでMCMCサンプルの分析を行った。リスト2のスクリプトは直接Windows上で実行した。WSL/Windowsでは描画ソフトが使えないようである(2025.02)。

 

リスト1のスクリプトを実行すると、入力データファイル名の設定が求められる。

 

(py312) yasuharu@PSY-PM-PC/mnt/*****/test_t_distri_files$ python ba_t_distri_means.py

Input data file(*.csv) =

 

入力データファイルは、図2の形式のCSVファイルとして用意する。

図2 入力データファイル例sampleN100.csv

 

 

1行目に変数名を書き、2行目からデータ値を書く。1列目はデータの識別用で、プログラムでは用いられない。2列目と3列目に分析データ値を書くが、データ数が異なるときは少ない方のデータのセルは空になる(図2)。

次の例では、図2のファイル名が設定されている。

 

(py312) yasuharu@PSY-PM-PC:/mnt/*****/test_t_distri_files$ python ba_t_distri_means.py

Input data file(*.csv) = sampleN100.csv

 

ファイル名を設定すると、データが読み込まれて計算が始まる。

MCMCサンプリングが終了すると、MCMCサンプルがファイルに保存されて、スクリプトの実行終了となる。

 

fit.pkl was saved.

(py312) yasuharu@PSY-PM-PC:/mnt/*****/ba_t_distri/test_t_distri_files$

 

 

リスト1のスクリプトの実行後、リストのスクリプトを実行すると、MCMCサンプルのファイルを読み込んで分析が行われる。

まず、パラメータmu1mu2の事後分布のグラフが表示される(図3)。

図3

 

分布の代表値は中央値である(Gelman et al., 2021)。

図3のWindowを閉じると、パラメータの差mu1-mu2の事後分布のグラフが表示される(図4)。

図4

 

タイトル部にmu1>mu2の事後確率が1.000であること、mu1<mu2の事後確率が0.000であることが表示されている。ほぼ確実にmu1の方がmu2より大きいと考えられる。

図4のWindowを閉じると、パラメータsgmの事後分布が表示される。

図5

 

図5のWindowを閉じると、パラメータnuの事後分布のグラフが表示される(図6)。

図6

 

KDEによる推定分布では負の領域にも分布が正の値をとっている領域があるが、ヒストグラムではnuの分布は正の領域に限られていることが分かる。

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

図7

 

データのヒストグラムと、事後分布の中央値をパラメータとするt分布のグラフが描かれている。

 

 

 

リスト1 ベイズ分析のMCMCサンプリングを行うスクリプト(ba_t_distri_means.py

 

import numpy as np

import pandas as pd

import stan

import pickle

 

inf_nm = input('Input data file(*.csv) = ')

dframe = pd.read_csv(inf_nm)                #  データの読み込み

 

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

    pickle.dump(dframe, f)                  #   読み込みデータの保存

 

df_keys = dframe.keys()

x_name = df_keys[1]

y_name = df_keys[2]

print(x_name, y_name)

x = dframe[x_name].values

x = x[np.isnan(x)==False]       #  空セルデータの削除

print('X =\n', x)

y = dframe[y_name].values

y = y[np.isnan(y)==False]       #  空セルデータの削除

print('Y =\n', y)

 

mean1 = np.mean(x)

sd1 = np.std(x)

print('mean1 =', mean1, '   sd1 =', sd1)

mean2 = np.mean(y)

sd2 = np.std(y)

print('mean2 =', mean2, '   sd2 =', sd2)

 

min1 = np.min(x)

max1 = np.max(x)

min2 = np.min(y)

max2 = np.max(y)

print(min1,'  ', max1)

print(min2,'  ', max2)

 

stan_code = """

    data {

        int n1;

        array[n1] real x;

        int n2;

        array[n2] real y;

        real min1;

        real max1;

        real min2;

        real max2;

    }

    parameters {

        real<lower=0, upper=1> vmu1;

        real<lower=0, upper=1> vsgm1;

        real<lower=0, upper=1> vmu2;

        real<lower=0, upper=1> vsgm2;

        real<lower=0, upper=1> vnu1;

        real<lower=0, upper=1> vnu2;

    }

    transformed parameters {

        real mu1;

        real<lower=0> sgm1;

        real mu2;

        real<lower=0> sgm2;

        real nu1;

        real nu2;

        /*

               Parameters rescaled

        */

        mu1 = min1 + (max1 - min1) * vmu1;

        sgm1 = 0.0001 + (max1 - min1) * vsgm1 / 2.0;

        mu2 = min2 + (max2 - min2) * vmu2;

        sgm2 = 0.0001 + (max2 - min2) * vsgm2 / 2.0;

        nu1 = 0.0001 + vnu1*100;

        nu2 = 0.0001 + vnu2*100;

    }

    model {

        vmu1 ~ beta(1, 1);

        vsgm1 ~ beta(1, 1);

        vmu2 ~ beta(1, 1);

        vsgm2 ~ beta(1, 1);

        vnu1 ~ beta(1, 1);

        vnu2 ~ beta(1, 1);

        x ~ student_t(nu1, mu1, sgm1);

        y ~ student_t(nu2, mu2, sgm2);

    }

"""

 

Data = {'n1':len(x), 'x':x, 'min1':min1, 'max1':max1,

        'n2':len(y), 'y':y, 'min2':min2, 'max2':max2}

 

sm = stan.build(stan_code, data=Data)

fit = sm.sample()

 

pd_fit_frame = fit.to_frame()

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

    pickle.dump(pd_fit_frame,f)    #   MCMC samples saved

print('fit.pkl was saved.')

 

 

 

 

リスト2 MCMCサンプルの分析スクリプト(draw_t_distri_means.py

 

import numpy as np

import pandas as pd

import pickle

import matplotlib.pyplot as plt

import seaborn as sb

import scipy.stats as ss

 

"""

          保存されたデータの読み込み

"""

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

    dframe = pickle.load(f)                 

 

df_keys = dframe.keys()

x_name = df_keys[1]

y_name = df_keys[2]

print(x_name, y_name)

x = dframe[x_name].values

x = x[np.isnan(x)==False]       #  空セルデータの削除

print('X =\n', x)

y = dframe[y_name].values

y = y[np.isnan(y)==False]       #  空セルデータの削除

print('Y =\n', y)

 

mean1 = np.mean(x)

sd1 = np.std(x)

print('mean1 =', mean1, '   sd1 =', sd1)

mean2 = np.mean(y)

sd2 = np.std(y)

print('mean2 =', mean2, '   sd2 =', sd2)

 

#max_v = np.max(x) if np.max(x) > np.max(y) else np.max(y);

 

 

min1 = np.min(x)

max1 = np.max(x)

min2 = np.min(y)

max2 = np.max(y)

print(min1,'  ', max1)

print(min2,'  ', max2)

 

 

"""

         MCMCサンプルの読み込み

"""

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

    pd_fit_frame = pickle.load(f)

"""

      事後分布の中央値を点推定値とする

"""

mu1 = np.median(pd_fit_frame['mu1'])

sgm1 = np.median(pd_fit_frame['sgm1'])

nu1 = np.median(pd_fit_frame['nu1'])

mu2 = np.median(pd_fit_frame['mu2'])

sgm2 = np.median(pd_fit_frame['sgm2'])

nu2 = np.median(pd_fit_frame['nu2'])

"""

               パラメータmu1mu2の事後分布

"""

plt.title('Posterior distributions' +f'\nmu1={mu1:.2f},  mu2={mu2:.2f}')

sb.kdeplot(pd_fit_frame['mu1'], label=x_name)

sb.kdeplot(pd_fit_frame['mu2'], label=y_name)

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

plt.legend()

plt.show()

"""

           パラメータmu1mu2の差の事後分布と事後確率

"""

diff_mu1_mu2 = pd_fit_frame['mu1'] - pd_fit_frame['mu2']

diff_mu_med = np.median(diff_mu1_mu2)

pos_idx = diff_mu1_mu2 > 0

neg_idx = diff_mu1_mu2 < 0

prop_pos = np.mean(pos_idx)     #   P(mu1-mu2>0)

prop_neg = np.mean(neg_idx)     #   P(mu1-mu2<0)

sb.kdeplot(diff_mu1_mu2)

plt.xlabel('mu1-mu2', fontsize=14)

plt.title(f'P(mu1>mu2)={prop_pos:.5f}, P(mu1<mu2)={prop_neg:.5f}')

plt.tight_layout()

plt.show()

"""

          パラメータsgm1sgm2の事後分布

"""

plt.title('Posterior distributions'

          f'\nsgm1={sgm1:.1f},  sgm2={sgm2:.2f}')

sb.kdeplot(pd_fit_frame['sgm1'], label=x_name)

sb.kdeplot(pd_fit_frame['sgm2'], label=y_name)

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

plt.legend()

plt.show()

"""

         パラメータnu1nu2の事後分布

"""

plt.title('Posterior distributions' +

          f'\nnu1={nu1:.2f},  nu2={nu2:.2f}')

plt.hist(pd_fit_frame['nu1'], color='g', alpha=0.5,

         density=True, label=x_name)

plt.hist(pd_fit_frame['nu2'], color='orange', alpha=0.5,

         density=True, label=y_name)

sb.kdeplot(pd_fit_frame['nu1'], color='g', label=x_name)

sb.kdeplot(pd_fit_frame['nu2'], color='orange', label=y_name)

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

plt.legend()

plt.show()

"""

          データのヒストグラムとt分布のグラフ

"""

plt.hist(x, density=True, color='b', alpha=0.5, label=x_name)

plt.hist(y, density=True, color='orange', alpha=0.5, label=y_name)

"""

         グラフの横軸の範囲

"""

min_xy = min1 if min1 < min2 else min2

max_xy = max1 if max1 > max2 else max2

coord_xy = np.linspace(min_xy, max_xy, 1000)

density1 = ss.t.pdf(coord_xy, nu1, loc=mu1, scale=sgm1)   #  t分布

density2 = ss.t.pdf(coord_xy, nu2, loc=mu2, scale=sgm2)   #  t分布

plt.plot(coord_xy, density1, c='g', label=x_name)

plt.plot(coord_xy, density2, c='r', label=y_name)

plt.legend()

plt.show()

 

 

 

ベイズ分析の基本的考え方の説明を岡本(2019pp.169-177岡本(2014pp.227-232で行っている。

 

 

参考文献

Gelman, A., Carlin, J. B., Stern, H. S., Dunson, D. B., Vehtari, A., & Rubin, D. B. (2014). Bayesian data analysis, 3rd. Ed. CRC Press.

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

Martin, O. (2018). Bayesian analysis with Python, 2nd Ed. Packt

岡本安晴(2014)「心理学データ分析と測定」勁草書房

岡本安晴(2019)「いまさら聞けないPythonでデータ分析」丸善出版

 

 

Home