多変量重回帰モデルのコントラスト検定
多変量重回帰モデルのコントラスト検定をgeneral fundamental least-squares theorem(Timm, 1975, p. 191)に基づいて行う方法(岡本、2014、第3.4節)のためのスクリプトを、Pythonで作成した。スクリプトをリスト1に示す。スクリプトファイルなどは、圧縮ファイルmvmregfiles.zipにまとめた。ダウンロード・展開して自由に利用されたい。
リスト1のスクリプトをコマンド
PS ・・・\mvmregfiles> python
MVmreg.py
で実行すると、入力データファイル名を聞いてくるので、次のように入力する。
入力データファイル名(*.txt) = Data.txt
上のデータファイルData.txtは、拙著「心理学データ分析と測定」の表3.2.1のデータのファイルである。リスト2にファイルData.txtのリストを示す。
| 
   表3.2.1  | 
 |
| 
   A校の生徒の点数  | 
  
   B校の生徒の点数  | 
 
| 
   (41, 77) (35, 77) (74, 29) (51, 67) (58, 55) (50, 69) (61, 49) (67, 53) (60, 52) (51, 67) (44, 61) (53, 64) (62, 54) (84, 30) (61, 45) (77, 41) (67, 46) (68, 41) (53, 60) (39, 76)  | 
  
   (55, 64) (54, 77) (43, 87) (83, 47) (61, 63) (84, 42) (62, 62) (86, 27) (60, 63) (35, 89) (73, 50) (64, 48) (79, 47) (48, 79) (58, 74) (54, 66) (68, 54) (90, 29) (74, 43) (49, 69)  | 
 
入力データファイルData.txtは、以下の形式で用意されている。
/       Y1  Y2 
X1  X2
        2       2
       
41       
77        
1        
0
       
35       
77        
1        
0
       
74       
29        
1        
0
     ・
     ・
     ・
       
90       
29        
0        
1
       
74       
43        
0        
1
       
49       
69        
0        
1
/     データの終わり
/  コントラストC
        1
        1               -1
        
/  A
        1
        1
        0
        
/  コントラストC
        1
        1               -1
        
/  A
        1
        0
        1
        
/  コントラストC
        1
        1               -1
        
/  A
        1
        1
        1
        
/   終わり
        -1
        
データファイルは、スラッシュ / で始まる行によって区切られたテキストファイルである。スラッシュ / の右側は、無視されるのでコメントを書くことができるが、日本語を用いるときには注意が必要である。入力データファイルは、次のコードで読み込んでいる。
with open(infilenm, 'r', encoding =
'utf-8') as fin:  #   日本語はUTF-8で設定
    raw_data = fin.readlines()
データ作成時に、日本語コードが「encoding = 'utf-8'」の設定に合わないときは、読み込み時にエラーとなる。使用した日本語コードにencodingの設定を合わせるか、日本語を用いないでデータファイルを作成する必要がある。
最初のスラッシュ / で始まる行の次の行に、従属変数の数pと独立変数の数qを空白文字で区切って並べる。
次の行から、1行に1ケースのデータを、従属変数、独立変数と並べる。i番目のケースのデータが、従属変数Y(i,1)、・・・、Y(i,p)、独立変数X(i,1)、・・・、X(i,q)であれば、1行に
Y(i,1) Y(i,2) ・・・ Y(i,p) X(i,1) X(i,2) ・・・ X(i,q)
と書く。すべてのデータを書き終わったら、スラッシュ / で始まる行を置いてデータの終わりであることを示す。
次のスラッシュ / で始まる行の次の行には、コントラスト行列Cの行数を書き、続く行から1行に1つずつコントラストを書いていく。
Cの値を書き終わったら、また、スラッシュ / で始まる行を置いて、その次の行にはコントラスト行列Aの列数を書き、続く行からAの値を行単位で書いていく。行列Aの値を読み込むと、そのCとAの組み合わせに対して検定が行われる。
このCとAの組を書くことを必要な回数繰り返す。CとAによる制約条件については、拙著「心理学データ分析と測定」における次式
![]()
の説明を参照されたい。プログラムでは
![]()
の場合を扱っている。
Cの行数として負の値を書くと、入力の終了となり、スクリプトの実行終了である。
端末画面には、次のメッセージが表示されている。
Results.txt
was saved.
出力ファイルResults.txtを開くと以下のようになっている。
入力データファイル = Data.txt
       
Y1       
Y2        X1        X2
      41.0      77.0       1.0       0.0
      35.0      77.0       1.0       0.0
      74.0      29.0       1.0       0.0
     ・
     ・
     ・
      90.0      29.0       0.0       1.0
      74.0      43.0       0.0       1.0
      49.0      69.0       0.0       1.0
C =
[[ 1. -1.]]
A =
[[1.]
 [0.]]
Lambda = 0.95204,   df1 = 1,  df2 = 1,  df3 = 38
F = 1.91414,   df1 = 1,  df2 = 38,  p = 0.17458
Chi^2 = 1.84292,   df = 1,   p = 0.17461
C =
[[ 1. -1.]]
A =
[[0.]
 [1.]]
Lambda = 0.98866,   df1 = 1,  df2 = 1,  df3 = 38
F = 0.43593,   df1 = 1,  df2 = 38,  p = 0.51307
Chi^2 = 0.42775,   df = 1,   p = 0.51310
C =
[[ 1. -1.]]
A =
[[1.]
 [1.]]
Lambda = 0.53633,   df1 = 1,  df2 = 1,  df3 = 38
F = 32.85175,   df1 = 1,  df2 = 38,  p = 0.00000
Chi^2 = 23.36263,   df = 1,   p = 0.00000
入力データの出力後、各コントラスト行列CとAの組に対する検定統計量が出力されている。
Lambda = に続く値は、
に従う統計量
の値である。
F の値は、コントラスト行列Aの列数u が1のときに出力される。
Chi-square の値は、バートレット近似(Bartlett’s approximation)の値である。
上の出力結果の意味は、拙著「心理学データ分析と測定」の「3.2 分析例2」を参照されたい。
Tim, N. H. (1975). Multivariate analysis with applications in education and psychology. Brooks/Cole publishing company.
リスト1 多変量重回帰モデルのコントラスト検定(MVmreg.py)
"""
        N.
H. Timm (1975).
         
Multivariate analysis with applications in
               
education and psychology.
            
Brooks/Cole publishing company
        岡本安晴(2014)心理学データ分析と測定.
          第3章 偏回帰係数を比べる:コントラスト
         勁草書房     
"""
import numpy as np
import scipy.stats as ss
import sys
infilenm = input('入力データファイル名(*.txt) = ')  
with open(infilenm, 'r', encoding =
'utf-8') as fin:  #   日本語はUTF-8で設定
    raw_data = fin.readlines()
fout = open('Results.txt', 'w')
fout.write(f'入力データファイル = {infilenm}\n')
pre_data = ''
for v in raw_data:
    pre_data += v.replace('\t',
' ').replace('\n', ',')
pre1_data = pre_data.split(',')
ck = 0
pos = 0
while True:
    if len(pre1_data[pos]) >
0:
        if
pre1_data[pos][0] == '/':
           
pos += 1
           
break
    pos += 1
p_q = np.array(pre1_data[pos].split(),
dtype='int')
p = p_q[0]                
#   従属変数の個数
q = p_q[1]                
#   独立変数の個数
print('p =',p, '  q =',q)
#
#          
データY,Xの読み込み
#
pos += 1
preX = []
while True:
    if pre1_data[pos][0] == '/':
        pos
+= 1
       
break
    v =
np.array(pre1_data[pos].split(), dtype='float')
    print(v)
    preX.append(v)
    pos += 1
preX = np.array(preX)
#print('preX =\n', preX)
Y = preX[:,0:p]
X = preX[:,p:p+q]
r = np.linalg.matrix_rank(X)
N = len(X)
if p > N-r:
    print(f'\nthat p <= N-r
should hold.\np = {p},  N =
{N},  r = {r}\n')
    sys.exit()
fout.write('\n\n')
for i in range(p):
    vs = f'Y{i+1}'
    fout.write(f'{vs:>10s}')
for i in range(q):
    vs = f'X{i+1}'
    fout.write(f'{vs:>10s}')
fout.write('\n')
for i in range(N):
    for j in range(p):
        vs =
f'{Y[i][j]}'
       
fout.write(f'{vs:>10s}')
    for j in range(q):
        vs =
f'{X[i][j]}'
       
fout.write(f'{vs:>10s}')
    fout.write('\n')
    
ginvXX = np.linalg.pinv(X.T @ X)
H = ginvXX @ X.T @ X
Bhat = ginvXX @ X.T @ Y
print('Bhat =\n', Bhat)
#
#           
コントラストの検定
#
while True:
    while True:
        if
len(pre1_data[pos]) > 0:
              
if pre1_data[pos][0] == '/':
                  
pos += 1
                  
break
        pos
+= 1
    g = int(pre1_data[pos])
    if g < 1:                
#   検定の終了
       
break
    if g > r:
       
print(f'g(={g}) should be smaller or equal to f(={r})!\n')
       
continue
    pos += 1
    #
    #              
Cの読み込み
    #
    C = []
    for i in range(g):
       
C.append(pre1_data[pos].split())
        pos
+= 1
    C = np.array(C,
dtype='float')
    print('\n\nC =\n', C)
    fout.write('\n\n')
    fout.write('\nC =\n')
    fout.write(C.__str__())
    if np.trace((C - C@H) @ (C -
C@H).T) > 1.0e-9:
       
print('C is not estimable\nC =\n', C)
       
continue
    while True:
        if
len(pre1_data[pos]) > 0:
            
  if pre1_data[pos][0]
== '/':
                  
pos += 1
                  
break
        pos
+= 1
        
    u = int(pre1_data[pos])
    pos += 1
    if u > p:
       
print(f'\nu(={u}) should be smaller or eual to p(={p})\n')
    #
    #        Aの読み込み
    #
    A = []
    for i in range(p):
       
A.append(pre1_data[pos].split())
        pos
+= 1
    A = np.array(A,
dtype='float')
    print('A =\n', A)
    fout.write('\n\nA =\n')
    fout.write(A.__str__())
    #
    #      岡本安晴(2014)、pp.45-47.
    #
    preQea = (Y - X @ Bhat) @ A
    Qea = preQea.T @ preQea
    Qta = Qea + (C @ Bhat @ A).T
@ np.linalg.inv(C @ ginvXX @ C.T) @\
         
(C @ Bhat @ A)
    Lmbd = np.linalg.det(Qea) /
np.linalg.det(Qta)
    print('\nLambda =', Lmbd, '
df1 =', u, ' df2 =', g, ' df3 =', N-r)
    fout.write('\n\n')
    fout.write(f'Lambda =
{Lmbd:.5f},   df1 = {u},  df2 = {g},  df3 = {N-r}')
    if u == 1:
        F =
((1.0 - Lmbd)/g) / ((Lmbd/(N-r)))
       
print(f'\nF = {F:.5f},  
df1 = {g}, df2 = {N-r},', end ='')
        cdf
= ss.f.cdf(F, g, N-r)
       
print(f'     p
= {1-cdf:.5f}')
       
fout.write('\n\n')
       
fout.write(f'F = {F:.5f},  
df1 = {g},  df2 = {N-r},')
       
fout.write(f'  p =
{1-cdf:.5f}')
    chi2 = -((N-r) -0.5*(u-g+1))
* np.log(Lmbd)
 
  print(f'\nChi~2 =
{chi2:.5f},   df = {u*g},', end
= '')
    cdf = ss.chi2.cdf(chi2, u*g)
    print(f'    p = {1-cdf:.5f}')
    fout.write('\n\n')
    fout.write(f'Chi^2 =
{chi2:.5f},   df = {u*g},   p = {1-cdf:.5f}')
fout.close()
print('\nResults.txt was saved.')
/       Y1  Y2 
X1  X2
        2       2
       
41       
77        
1        
0
       
35       
77        
1        
0
       
74       
29        
1        
0
       
51       
67        
1        
0
       
58       
55        
1        
0
       
50       
69        
1        
0
       
61       
49        
1        
0
       
67       
53        
1        
0
       
60       
52        
1        
0
       
51       
67        
1        
0
       
44       
61        
1        
0
       
53       
64        
1        
0
       
62       
54        
1        
0
       
84       
30        
1        
0
        61       
45        
1        
0
       
77       
41        
1        
0
       
67       
46        
1        
0
       
68       
41        
1        
0
       
53       
60        
1        
0
       
39       
76        
1        
0
       
55        64        
0        
1
       
54       
77        
0        
1
       
43       
87        
0        
1
       
83       
47        
0        
1
       
61       
63        
0        
1
       
84       
42        
0        
1
       
62       
62        
0        
1
       
86       
27        
0        
1
       
60       
63        
0        
1
       
35       
89        
0        
1
       
73       
50        
0        
1
       
64       
48        
0        
1
       
79       
47        
0        
1
       
48       
79        
0        
1
       
58       
74        
0        
1
       
54       
66        
0        
1
       
68       
54        
0        
1
       
90       
29        
0        
1
       
74       
43        
0        
1
       
49       
69        
0        
1
/     データの終わり
/  コントラストC
        1
        1               -1
        
/  A
        1
        1
        0
        
/  コントラストC
        1
        1               -1
        
/  A
        1
        0
        1
        
/  コントラストC
        1
        1               -1
        
/  A
        1
        1
        1
        
/   終わり
        -1