「統計解析のはなし―データに語らせるテクニック」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)