Up

腕組みの性差の2項分布による階層的ロジスティック回帰モデルのベイズ分析

 

クロス表のベイズ分析が提案されている(Kruscke, 2011岡本、2019)。クロス表の度数分布に対してポアッソン分布が適用されているが、Spiegelhalter (2019)のデータ(Table 10.2)のようなクロス表の場合は階層的2項分が適当であると考えられる。Spiegelhalterのデータでは、男女別に腕組みが右腕が上か左腕が上かの度数が示されている。これは受講生におけるデータであるので、自然発生的度数と考えるよりは、男女の度数を2項分布で表し、さらに男女別にどちらの腕が上であるかの度数を2項分布で表すことが考えられる。このとき、「男女別」および「右腕が上/左腕が上」の2つの主効果と、「男女別」と「右腕が上/左腕が上」の交互作用を設定したロジスティック回帰モデルによる階層的2項分布モデルを以下のように考える。

一般的に考えるために、男女の要因をAA1:女性、A2:男性)、腕組みをした時のどちらの腕が上であったかを要因BB1:左が上、B2:右が上)で表すと、クロス表は表1の形に書くことができる。

 

 

Table 1.  Observed counts of levels of Factor A by levels of Factor B.

 

 

Factor B

 

 

 

B1

B2

Sum

Factor A

A1

A2

Sum

 

 

Spiegelhalter (2019)のデータ(Table 10.2)における上記Table 1の値は、

 

5,   9

7,   23

 

である。

 

要因Aについて、2項分布を適用して

 

 

と置く。

 

要因Aの各水準において、要因Bについて2項分布を適用して

 

 

と置く。要因Bの主効果はで表され、は交互作用である。

 

ロジスティック関数の逆関数は、図1のようである。

 

図1

 

 

上記モデルによるTable 1のベイズ分析を行うスクリプトを、リスト1およびリスト2に示す。リスト1とリスト2に分割したのは、Windows上では、PyStan 3を用いるリスト1のスクリプトは、WindowsにインストールしたUbuntu上で実行され、リスト1の実行により得られたMCMCサンプルの描画などはWindows上でリスト2のスクリプトにより行われるからである。スクリプトファイルは、まとめて圧縮ファイルhibinfiles.zipとした。

リスト1を実行すると、(D1)と(D2)に示されるデータに対してMCMCサンプリングが行われ、サンプルがファイルに保存される。リスト2を実行すると、保存されたMCMCサンプルが読み込まれて、まず要因Aのパラメータaの事後分布が表示される(図2)。

 

図2

 

事後分布は負の領域にシフトしており、でパラメータaが正である事後確率は0.1%と小さい。統計学的検定の慣例を参考にすれば、パラメータは負であると考えてよさそうである。すなわち、女性の比率は0.5以下と解釈できる。

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

 

図3

 

要因Bの主効果のパラメータbの事後分布である。事後分布は負の方向にシフトしているが、であるので、統計学的検定の慣例を参考にすれば、bは負であるとは断言できないが、bが負である事後確率は0.863とかなり高い。女性において左腕が上という傾向があると解釈できる。

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

 

図4

 

交互作用のパラメータcの事後分布である。であり、原点0の左右にほぼ同じ程度に分布している。交互作用は無いと解釈できる。すなわち、左腕が上であるという傾向は、男女間で同じと解釈できる。

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

 

図5

 

女性における左腕が上のパラメータbと、男性における左腕が上のパラメータb+cの事後分布のグラフである。2つの事後分布はほぼ重なっている。

 

 

 

引用参考文献

 

Kruschke, J. K. (2011) Doing Bayesian data analysis. Elsevier.

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

Spiegelhalter, D. (2019) The art of statistics. Penguin Random House

 

 

 

リスト1 MCMCサンプリング: Python3.12/Miniconda/Ubuntu24/WSL/Windows10で実行した。

 

import stan

import arviz as az

import pickle

 

"""

       D. Spiegelhalter (2019)

       The art of statistics: Learnig from data.

       Penguin Random House

       Table 10.2

"""

        #   Left arm    Right arm

data =  [  [   5,          9  ],      #  Female

           [  17,         23  ]   ]   #  Male

 

 

stan_code = """

        data {

            array[2, 2] int F;

        }

        transformed data {

            int row1_sum;

            int row2_sum;

            int total;

            row1_sum = F[1][1] + F[1][2];

            row2_sum = F[2][1] + F[2][2];

            total = sum(F[1]) + sum(F[2]);

        }

        parameters {

            real a;

            real b;

            real c;

        }

        model {

            a ~ normal(0.0, 10.0);

            b ~ normal(0.0, 10.0);

            c ~ normal(0.0, 10.0);

            row1_sum ~ binomial(total, inv_logit(a));

            F[1][1] ~ binomial(row1_sum, inv_logit(b));

            F[2][1] ~ binomial(row2_sum, inv_logit(b + c));

        }

"""

Data = {'F':data}

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

fit = sm.sample()

print(fit)

"""

        MCMCサンプルの保存

"""

df_fit = fit.to_frame()

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

    pickle.dump(df_fit, f)

"""

      MCMCサンプルの統計量

"""

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

smmry = az.summary(inf_data)

print(smmry)

 

 

 

リスト2 MCMCサンプルの描画: Python3.13/Windows10で実行した。

 

import arviz as az

import pickle

import matplotlib.pyplot as plt

import seaborn as sb

import numpy as np

"""

         MCMCサンプルの読み込み

"""

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

    df_fit = pickle.load(f)

 

p_a = np.mean(df_fit['a'] > 0)

plt.title(f'Prob(a > 0) = {p_a:.3f}', fontsize=16)

sb.kdeplot(df_fit['a'])

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

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

plt.show()

 

p_b = np.mean(df_fit['b'] > 0)

plt.title(f'Prob(b > 0) = {p_b:.3f}', fontsize= 16)

sb.kdeplot(df_fit['b'])

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

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

plt.show()

 

p_c = np.mean(df_fit['c'] > 0)

plt.title(f'Prob(c > 0) = {p_c:.3f}', fontsize=16)

sb.kdeplot(df_fit['c'])

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

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

plt.show()

 

sb.kdeplot(df_fit['b'], label='b')

sb.kdeplot(df_fit['b'] + df_fit['c'], label='b+c')

plt.xlabel('')

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

plt.legend(fontsize=18)

plt.show()

 

 

 

 

Home