「統計解析のはなし―データに語らせるテクニック」8章

大村平「統計解析のはなし―データに語らせるテクニック」日科技連 の読後行間補充メモ

同書籍は、印刷数表と手計算による統計解析を紹介した良書。

本稿では、同書籍の設例について、プログラムによる演算処理をした場合の例を示す。

コードと出力例は、Python[Google Colaboratory]




8章 複雑さをばらす法 ~多変量解析のはなし~ 


p231 主要な因子を選び出す 表8.2

検定:因子が評価を左右する影響力を有するか。
因子分析:評価に影響を及ぼす主要な因子を選びだす。
import pandas as pd

# 基礎データ
A = [3,1,2,3,1]
B = [2,3,1,2,2]
C = [3,2,2,3,1]
D = [2,3,1,2,3]

DF = pd.DataFrame([A,B,C,D]).T
index_info = ['甲','乙','丙','丁','戊']
col_info = ['A子','B子','C子','D子']
DF.index =index_info
DF.columns = col_info
DF # 表8.2
	A子	B子	C子	D子
甲	3	2	3	2
乙	1	3	2	3
丙	2	1	2	1
丁	3	2	3	2
戊	1	2	1	3


p233 表8.4 相関係数の一覧表

# コードは上記の続き
import math

# 関数(2つのリストの相関係数)
def correlate(list1, list2):
  s_1 = pd.Series(list1)
  s_2 = pd.Series(list2)
  res = round(s_1.corr(s_2),2)
  return res

# 関数(DataFrameの n行と m行の相関係数)
def corre(n,m):
  list1 = DF.iloc[:,n]
  list2 = DF.iloc[:,m]
  return correlate(list1,list2) # 相関係数の関数

# 全行と全行の相関係数の一覧
line_total = []
for n in range(4): #各行
  n_line = []
  for m in range(4): #各行
    if n == m: # 同一人どおしの比較はスキップ
      n_line.append(float('nan'))
    else:
      n_line.append(corre(m,n)) #ある行と全行の相関係数
  line_total.append(n_line) #全行と全行

# DFに整理
DF84 = pd.DataFrame(line_total,index= col_info)
DF84.columns= col_info

# 関数 セル情報に該当する行・列の情報
def row_col(dataframe,value): # データフレーム、セル情報
  for row in range(dataframe.shape[0]): # 行数
    for column in range(dataframe.shape[1]): # 列数
      if dataframe.iloc[row][column] == value: # セル情報と一致する場合
        return [dataframe.index[row], dataframe.columns[column]] # 該当セルの行と列の番号を取得
      else:
        break

# 相関係数の最大・最小値の情報
maxis = DF84.max().max() # 最大値
print(f'相関係数の最大値:{maxis}は、{row_col(DF84, maxis)[1]}と{row_col(DF84, maxis)[0]}')
minis = DF84.min().min() # 最小値
print(f'相関係数の最小値:{minis}は、{row_col(DF84, minis)[1]}と{row_col(DF84, minis)[0]}')

DF84 # 表8.4
相関係数の最大値:0.9は、A子とC子
相関係数の最小値:-0.6は、A子とD子


p234 表8.5 主要な因子を選び出す

正の相関あり →特徴が共通の因子に着目

負の相関あり →特徴が反する因子に着目

import pandas as pd

# 基礎データ
Height = ['tall','short','short','tall']
Shape = ['fat','lean','lean','fat']
Chest = ['big','big','big','flat']
Waist = ['wide','narrow','narrow','wide']
Color = ['white','black','black','white']
Face = ['horizon','vertical','horizon','vertical']
Hair = ['wave','straight','straight','wave']
Voice = ['husky','chimes','husky','chimes']

DFB = pd.DataFrame([Height,Shape,Chest,Waist,Color,Face,Hair,Voice])
index_info = ['Height','Shape','Chest','Waist','Color','Face','Hair','Voice']
col_info = ['A','B','C','D']
DFB.index =index_info
DFB.columns = col_info

# 因子分析
DFB = DFB.assign(AC="", BD="", AD="")
for item in range(len(DFB)):
  if DFB['A'][item] == DFB['C'][item]:
    DFB['AC'][item] = '◎'
  if DFB['B'][item] == DFB['D'][item]:
    DFB['BD'][item] = '〇'
  if not DFB['A'][item] == DFB['D'][item]:
    DFB['AD'][item] = '△'
DFB # 表8.5


p235 因子の候補が無い場合→ベクトルの使用

import pandas as pd
import math

# 基礎データ
TT = [1,-1,1,-1,1,-1,-1,1,1,-1]
SW = [1,-1,-1,-1,1,-1,1,1,1,-1]

# DF化
DF = pd.DataFrame([TT,SW]).T
index_info = ['甲','乙','丙','丁','戊','己','庚','辛','壬','癸']
col_info = ['卓球','相撲']
DF.index =index_info
DF.columns = col_info
print(f'{DF}\n')

# ベクトル角
res = DF['卓球'].corr(DF['相撲']) # 相関係数
pidegrees = math.acos(res) # ベクトルの内角
degrees = math.degrees(pidegrees) # ラジアン角度 →度数
print(f'相関係数:{round(res,1)}\nベクトルの内角:{round(degrees,1)}度')
   卓球  相撲
甲   1   1
乙  -1  -1
丙   1  -1
丁  -1  -1
戊   1   1
己  -1  -1
庚  -1   1
辛   1   1
壬   1   1
癸  -1  -1

相関係数:0.6
ベクトルの内角:53.1度


p243~244 因子の候補が無い場合 図8.5(因子ベクトル)

import numpy as np
import matplotlib.pyplot as plt
from matplotlib import patches

remainder_angle = (90 - degrees)/2
print(f'余角:{round(remainder_angle,2)}度')

# ベクトルPの角度
P_degrees = remainder_angle + degrees
print(f'ベクトルPの角度:{round(P_degrees,2)}度')
P_pi = math.radians(P_degrees) # 度数 →ラジアン角度

# ベクトルSの角度
S_degrees = remainder_angle
print(f'ベクトルSの角度:{round(S_degrees,2)}度')
S_pi = math.radians(S_degrees)

# ベクトル
origin = np.array([0, 0]) # 原点
vecP = np.array([math.cos(P_pi), math.sin(P_pi)]) # P終点
vecS = np.array([math.cos(S_pi), math.sin(S_pi)]) # S終点
vector = [(origin, vecP, 'blue'), # ベクトルP
          (origin, vecS, 'green')] # ベクトルS

# グラフの大きさ
fig ,ax= plt.subplots(figsize=(5,5))

# ベクトル描画
for tail, head, color in vector:
    ax.quiver(*tail,*head, color=color, units='xy', scale=1)
ax.text(vecP[0]*1.05,vecP[1]*1.05, "$\overrightarrow{P}$", color = "blue", size = 13) # Pの座標表示
ax.text(vecS[0]*1.05,vecS[1]*1.05, "$\overrightarrow{S}$", color = "green", size = 13) # Sの座標表示

# 円弧
patchP = patches.Arc(xy=(0, 0), width=0.9, height=0.9, color="red", theta1 =S_degrees, theta2 =P_degrees)
patchS = patches.Arc(xy=(0, 0), width=1, height=1, color="red", theta1 =0, theta2 =S_degrees)
patchY = patches.Arc(xy=(0, 0), width=1, height=1, color="red", theta1 =P_degrees, theta2 = 90)
ax.add_patch(patchP)
ax.add_patch(patchS)
ax.add_patch(patchY)

# 円弧の角度表示
loc_PSx = math.cos((P_pi+S_pi)/2)/2 # PSベクトルの中間位置
loc_PSy = math.sin((P_pi+S_pi)/2)/2
loc_Sx = math.cos(S_pi/2)/2 # Sベクトルとx軸の中間位置
loc_Sy = math.sin(S_pi/2)/2
loc_Px = math.sin(S_pi/2)/2/2 # Pベクトルとy軸の中間位置(位置補正あり)
loc_Py = math.cos(S_pi/2)/2*1.1
ax.text(loc_PSx, loc_PSy, f'{round(degrees,1)}'"$^{\circ}$", color = "red", size = 11)
ax.text(loc_Sx, loc_Sy, f'{round(remainder_angle,1)}'"$^{\circ}$", color = "red", size = 11)
ax.text(loc_Px, loc_Py, f'{round(remainder_angle,1)}'"$^{\circ}$", color = "red", size = 11)

# 表題、軸ラベル
ax.set_title('Factor Vector', fontsize =11)
ax.set_xlabel('$\overrightarrow{x}$', fontsize =12) # x軸ラベル
ax.set_ylabel('$\overrightarrow{y}$', fontsize =12) # y軸ラベル
ax.set_xlim(0, 1.2) # x軸の範囲
ax.set_ylim(0, 1.2) # y軸の範囲
plt.show()
余角:18.43度
ベクトルPの角度:71.57度
ベクトルSの角度:18.43度


p247 主な成分を見つけ出す 表8.9

import pandas as pd
import math

# 設例のDF化
A = [5,4,3,2,1]
B = [5,2,4,1,3]
index_info = ['A子','B子']
columns_info = ['馬','犬','豚','バッタ','ウニ']
DF = pd.DataFrame([A,B],index=index_info, columns=columns_info )

# 関数(2つのリストの相関係数)
def correlate(list1, list2):
  s_1 = pd.Series(list1)
  s_2 = pd.Series(list2)
  res = round(s_1.corr(s_2),2)
  return res

# 相関係数
corre = correlate(A,B)
print(f'相関係数:{corre}')

# ベクトル角度
acos = math.acos(corre) #逆余弦(ラジアン)
d_degree = math.degrees(acos) #ラジアン→度数表記
print(f'ベクトル角度:{round(d_degree,2)}度')

DF
相関係数:0.5
ベクトル角度:60.0度

	馬	犬	豚	バッタ	ウニ
A子	5	4	3	2		1
B子	5	2	4	1		3


p251 主成分に分解 図8.7

from matplotlib import pyplot as plt
import japanize_matplotlib

fig = plt.figure(figsize=(5, 5)) # 図版の大きさ
ax = fig.add_subplot(111) # グラフ1つ
ax.set_aspect("equal") # 直交座標

# 各点をプロット
plt.scatter(A,B,20, color ="red", marker="D") # 各リスト、点の大きさ、色、種類

# 描画範囲
xlower, xupper = -2, 6 
ylower, yupper = -2, 6
plt.xlim(xlower, xupper)
plt.ylim(ylower, yupper)

# 点名称をプロット
for col in range(len(columns_info)):
  ax.text(DF.iloc[0][col],DF.iloc[1][col],DF.columns[col])

# 座標軸
plt.hlines(0, -3, 6, color ='black') # x軸
plt.vlines(0, -3, 6, color ='black') # y軸
plt.plot([xlower, xupper], [ylower, yupper], color="lightblue", linewidth=5, label="第1主成分軸")
plt.plot([xlower, 2], [2, ylower], color="lightgreen", linewidth=5, label="第2主成分軸")

# 関数 垂線の足の座標(一般)
def Perpendicular_Legs(a,b,c,x,y): # 点(x,y) から ax+by+c=0 へ垂線 
  return [(x*b**2-a*b*y-a*c)/(a**2+b**2),(y*a**2-a*b*x-b*c)/(a**2+b**2)]

# 関数 垂線の足の座標(第2主成分軸用)
def PLEG(x,y): # 点(x,y) から ax+by+c=0 へ垂線
  a, b, c = 1,1,0
  return Perpendicular_Legs(a,b,c,x,y)

# 各点から第2主成分軸への垂線  
for i in range(5):
  x_value = DF.iloc[0][i]
  y_value = DF.iloc[1][i]
  plt.plot([PLEG(x_value,y_value)[0],DF.iloc[0][i]],
   [PLEG(x_value,y_value)[1],DF.iloc[1][i]],
           color='red', linestyle="--") # 各点と垂線足とを破線連結

#グラフの軸名称
plt.xlabel('Score_by_A')
plt.ylabel('Score_by_B')
plt.legend()
plt.grid(True)