Up

第7章 関係を表すパス図

共分散構造分析

 

パス図の分析法として共分散構造分析(analysis of covariance structures, general structural equation technique)と呼ばれている方法がある。<岡本安晴「心理学データ分析測定」勁草書房>の図7.1.4の例に対してサンプルスクリプトを作成してみた。図7.1.4を少し簡略化して図7.1.4(改)に示す。

7.1.4(改)

 

7.1.4(改)のモデルをリスト1に示すStanスクリプトで表してみた。

 

リスト1 図7.1.4(改)のStanスクリプト(ファイル名SubjModel.stan

data {

    int N;

    real Jpn[N];

    real Eng[N];

    real Hist[N];

    real Math[N];

    real Phys[N];

    real Chem[N];

}

parameters {

    real b1;

    real b2;

    real FG[N];

    real FL[N];

    real FS[N];

    real a[6];

}

transformed parameters {

    real f_Jpn[N];

    real f_Eng[N];

    real f_Hist[N];

    real f_Math[N];

    real f_Phys[N];

    real f_Chem[N];

    for (i in 1:N){

        f_Jpn[i] = a[1] * FL[i];

        f_Eng[i] = a[2] * FL[i];

        f_Hist[i] = a[3] * FL[i];

        f_Math[i] = a[4] * FS[i];

        f_Phys[i] = a[5] * FS[i];

        f_Chem[i] = a[6] * FS[i];

    }

}

model {

    b1 ~ uniform(0.0, 1.0);

    b2 ~ uniform(-1.0, 1.0);

    for (j in 1:6){

        a[j] ~ uniform(0.0, 1.0);

    }

    for (i in 1:N){

        FG[i] ~ normal(0, 1);

        FL[i] ~ normal(b1 * FG[i], sqrt(1 - b1 * b1));

        FS[i] ~ normal(b2 * FG[i], sqrt(1 - b2 * b2));

        Jpn[i] ~ normal(f_Jpn[i], sqrt(1 - a[1]*a[1]));

        Eng[i] ~ normal(f_Eng[i], sqrt(1 - a[2]*a[2]));

        Hist[i] ~ normal(f_Hist[i], sqrt(1 - a[3]*a[3]));

        Math[i] ~ normal(f_Math[i], sqrt(1 - a[4]*a[4]));

        Phys[i] ~ normal(f_Phys[i], sqrt(1 - a[5]*a[5]));

        Chem[i] ~ normal(f_Chem[i], sqrt(1 - a[6]*a[6]));

    }

}

 

リスト1のStanスクリプトを使って、図7.1.4(改)に対応するデータ(下図1、ファイル名Data.xlsx)を読み込み分析するPythonスクリプトを、ウェブサ後半に示したリスト2のように用意した。これらのファイルは圧縮ファイルfiles7.zipとして用意したので、ダウンロードすれば自由に利用できる。

図1 データData.xlsx

 

リスト2のスクリプトのデータ読み込み部は以下のようになっている。

 

flnm = input('Data File Name (*.xlsx) = ')

xlsx = pd.ExcelFile(flnm)                   #   入力ファイル名の設定

data = pd.read_excel(xlsx)                  #   読み込み

 

上のスクリプトを実行すると、入力データファイル名を聞いてくる(図2)。

図2

 

入力データファイル名(図2の場合は、図1のデータファイル名)を入力すると、設定された名前のExcelのデータファイルが読み込まれ、PandasDataFrame型のオブジェクトに格納されてdataによって指し示される。このDataFrame型のオブジェクトから、以下のスクリプトによってリスト1のStanスクリプト用のデータが用意される。

 

VarLabels = data.columns[1:].values;

print(VarLabels)

 

X = data.values[:, 1:]

X = ss.zscore(X)

X = X.T

Jpn = X[0]

Eng = X[1]

Hist = X[2]

Math = X[3]

Phys = X[4]

Chem = X[5]

N = len(Jpn)

 

データ値Xは標準得点化されている。Python用のStanPyStan)の使い方は、<岡本安晴「ないPythonデータ分析丸善出版>の第10章などを参照されたい。

Stanによるサンプリングが終了すると、まず、係数b1b2の事後分布が表示される(図3)。

図3

 

図3のウィンドウを閉じると、係数a1からa6までの事後分布が表示される(図4)。

図4

 

図4を閉じると、因子得点fJpnの事後分布の平均値とデータ値Jpnの標準化得点との散布図が表示される(図5)。グラフの上に表示されている数値は、a1の事後分布の平均値である。

図5

 

図5を閉じると、因子得点fEngの事後分布の平均値とデータ値Engの標準化得点との散布図が表示される(図6)。グラフの上部の数値はa2の事後分布の平均値である。

図6

 

以下同様に、ウィンドウを閉じると次の散布図が表示されていく。最後に、図7の散布図が表示される。

図7

 

図7のウィンドウを閉じると、プログラムの終了である(図8)。

図8

 

コンソール画面には、各係数の平均値が出力されている。

 

 

リスト2 リスト1のStanスクリプトによる分析を行うPythonスクリプト(圧縮ファイルfiles7.zipに含まれている)

import pandas as pd

import numpy as np

import matplotlib.pyplot as plt

import seaborn as sb

import scipy.stats as ss

import pystan

import pickle

 

 

flnm = input('Data File Name (*.xlsx) = ')

xlsx = pd.ExcelFile(flnm)                   #   入力ファイル名の設定

data = pd.read_excel(xlsx)                  #   読み込み

 

VarLabels = data.columns[1:].values;

print(VarLabels)

 

X = data.values[:, 1:]

X = ss.zscore(X)

X = X.T

Jpn = X[0]

Eng = X[1]

Hist = X[2]

Math = X[3]

Phys = X[4]

Chem = X[5]

N = len(Jpn)

 

Data = {'N': N, 'Jpn': Jpn, 'Eng': Eng, 'Hist': Hist, 'Math': Math, 'Phys': Phys, 'Chem': Chem}

 

def f_init():

    return dict(b1 = 0.9, b2 = 0.9, a = [0.9, 0.9, 0.9, 0.9, 0.9, 0.9])

 

 

sm = pystan.StanModel(file = 'SubjModel.stan')

fit = sm.sampling(data = Data, init = f_init, n_jobs = 1)

 

print(fit)

 

b1_smpls = fit['b1']

b2_smpls = fit['b2']

b1 = fit['b1'].mean()

b2 = fit['b2'].mean()

print('\nb1 = {0:.5}'.format(b1))

print('b2 = {0:.5}'.format(b2))

a = fit['a'].mean(axis = 0)

for i in range(6):

    print('a[{0:}] = {1:.5}'.format(i+1, a[i]))

 

sb.kdeplot(b1_smpls, label = 'b1')

sb.kdeplot(b2_smpls, label = 'b2')

plt.title('Means:\nb1 = {0:.3},  b2 = {1:.3}'.format(b1, b2), fontsize = 14)

plt.legend()

plt.show()

 

a_smpls = fit['a'].T

for i in range(6):

    sb.kdeplot(a_smpls[i], label = 'a{}'.format(i+1))

plt.legend()

plt.title(('Means: a1 = {0:.3},  a2 = {1:.3}\n' +

           'a3 = {2:.3},  a4 = {3:.3},  a5 = {4:.3},  a6 = {5:.3}').format(

               a[0], a[1], a[2], a[3], a[4], a[5]), fontsize = 14)

plt.show()

 

fJpn = fit['f_Jpn'].mean(axis = 0)

plt.plot(fJpn, Jpn, 'bo')

plt.ylabel('Jpn(std)')

plt.xlabel('fJpn')

plt.title('a1 = {0:.5}'.format(a[0]), fontsize = 14)

plt.show()

 

fEng = fit['f_Eng'].mean(axis = 0)

plt.plot(fEng, Eng, 'bo')

plt.xlabel('fEng')

plt.ylabel('Eng(std)')

plt.title('a2 = {0:.5}'.format(a[1]), fontsize = 14)

plt.show()

 

fHist = fit['f_Hist'].mean(axis = 0)

plt.plot(fHist, Hist, 'bo')

plt.xlabel('fHist')

plt.ylabel('Hist(std)')

plt.title('a3 = {0:.5}'.format(a[2]), fontsize = 14)

plt.show()

 

fMath = fit['f_Math'].mean(axis = 0)

plt.plot(fMath, Math, 'bo')

plt.xlabel('fMath')

plt.ylabel('Math(std)')

plt.title('a4 = {0:.5}'.format(a[3]), fontsize = 14)

plt.show()

 

fPhys = fit['f_Phys'].mean(axis = 0)

plt.plot(fPhys, Phys, 'bo')

plt.xlabel('fPhys')

plt.ylabel('Phys(std)')

plt.title('a5 = {0:.5}'.format(a[4]), fontsize = 14)

plt.show()

 

fChem = fit['f_Chem'].mean(axis = 0)

plt.plot(fChem, Chem, 'bo')

plt.xlabel('fChem')

plt.ylabel('Chem(std)')

plt.title('a6 = {0:.5}'.format(a[5]), fontsize = 14)

plt.show()

 

 

 

Up