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

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

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

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

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




9章 なんでも数字で表す法 ~数量化のはなし~ 


p262 5段階評価 表9.1

  • 等間隔目盛りで評価対象を測定する線形の評価。
  • 平均値をまたいだ \(1\sigma\) の区間に約38パーセントの確率分布。
import numpy as np
from scipy.stats import norm
import pandas as pd
import matplotlib.pyplot as plt

p_inf = float('inf') # 無限大

# 設定値
mean = 0 # 平均
sigma = 1 # 標準偏差

# 領域の上限
up1 = -sigma *3/2
up2 = -sigma/2
up3 = sigma/2
up4 = sigma *3/2
up5 = p_inf
up_list = [-p_inf, up1, up2, up3, up4, up5] # リスト化

# 領域の確率
area_prob = []
for up in range(len(up_list)-1):
  prob = norm.cdf(up_list[up+1], mean)-norm.cdf(up_list[up], mean)
  area_prob.append(round(prob*100,1))

# DF化
## index生成
subject = "{}点クラス"
point_list = ['1','2','3','4','5']
index_info = []
for point in point_list:
    class_name = subject.format(point) # 差込で語句生成
    index_info.append(class_name)
## DF生成
DFw = pd.DataFrame([area_prob])
DF = DFw.T
DF.index =[index_info]
DF.columns =['確率 [%]']

# グラフのデータ
X_list = np.linspace(-3, 2.9, 60) # -3から2.9まで60個のリスト
Y1 = norm.pdf(X_list,mean,sigma) # 確率密度関数のデータ
Y2 = np.linspace(0,0,60) # 0から0まで60個のリスト(y=0 のデータ)

# グラフの描画
plt.plot(X_list, Y1) # 正規分布
plt.plot(X_list, Y2, color = 'black') # x軸
plt.vlines(0,0,0.5, color = 'black') # y軸
plt.xticks([up_list[1],up_list[2],up_list[3],up_list[4]],
 [r'$-\frac{3}{2}\sigma$',r'$-\frac{1}{2}\sigma$',r'$\frac{1}{2}\sigma$', r'$\frac{3}{2}\sigma$' ]) # x軸目盛
plt.yticks([])
plt.title('5 stage evaluation') # 表題
plt.ylim(0,)

# 塗りつぶし
color_list = ['lightgrey','lightblue','lightgreen','orange']
for num in range(len(color_list)):
  plt.fill_between(X_list, Y1, Y2, where=(X_list>=up_list[num+1]),fc= color_list[num])

# 表示
print(DF)
plt.show()
             確率 [%]
1点クラス     6.7
2点クラス    24.2
3点クラス    38.3
4点クラス    24.2
5点クラス     6.7



p267 ウェイト付け(1次元に圧縮) 表9.2

互いに独立な3つの次元の変量(年齢、扶養家族の有無、勤務年数)により形成される3次元空間内の位置情報を1次元の評価(得点)に圧縮する。 

(作業工程)
  1. 2次元(例:年齢・扶養家族)の組合せにつき優劣を設定し、点数を割り振る。[表9.2の各上段]
  2. 同組合せを構成している各次元内での項目(例:年齢であれば、若・老)に点数を配賦する。[表9.2の各下段]
  3. 上記工程を3種(3次元のうち2次元を選び出す組合せの数)実施し、各次元内での項目ごとに合計点数を集計する(例:年齢=老であれば、4点)
  4. 評価対象(例:A曹長)ごとに、各次元(例:年齢)の該当項目(例:年齢=老)に該当する点数(例:4点)を与えて、評価対象ごとに点数を集計する(例:A曹長であれば15点)。[表9.3]
  5. 同評価対象の集計点数の多寡により、評価対象の優劣を判定する(1次元判定)。

import pandas as pd

# 考慮要素
Age = ['若','老']
Depend = ['有','無']
Services = ['長', '短']
elements = [Age, Depend, Services] # リスト化

profile = {'年齢': Age,
          '扶養家族': Depend,
          '勤務年数': Services} # 辞書

# ------------------------------------------------------表9.2 を作るための関数
def score_cal(i,j):

  # 考慮要素を組み合わせた軸要素を作成
  comblist = []
  for iele in elements[i]:
    for jele in elements[j]:
      comblist.append(iele +'・'+ jele)

  # 空のDF作成
  DF92part = pd.DataFrame(index =comblist, columns =comblist)

  # 斜線右上を設定
  DF92part.iloc[0][1:4] = 1
  if i==1 and j==2:
    DF92part.iloc[1][2] = 1 # (i,j)=(1,2)のときは、1
  else:
    DF92part.iloc[1][2] = -1 # (i,j)=(0,1) or (0,2) のときは、-1
  DF92part.iloc[1][3] = 1
  DF92part.iloc[2][3] = 1

  # 斜線右上の値を反転して左下に入力
  for col in range(0,DF92part.shape[1]): # 列数だけ繰り返す
    for row in range(0,DF92part.shape[0]): # 行数だけ繰り返す
      DF92part.iloc[row][col] = -DF92part.iloc[col][row]

  # 表示設定
  DF92part = DF92part.replace(1, '〇')
  DF92part = DF92part.replace(-1, '×')

  # 〇の個数合計の列を追加
  DF_bool = (DF92part == '〇') # 〇判定
  bool_sum = DF_bool.sum(axis =1) # 列ごとのTrue 数を集計
  bool_col = pd.DataFrame([bool_sum], index = ['点数']).T # 〇個数の列
  DF92part = pd.concat([DF92part,bool_col], axis=1)

  # 関数 列名の特定文字に点数を与える
  def collect(str):
    nstr = 0
    for row in range(4):
      if str in DF92part.index[row]:
        nstr += DF92part.iloc[row][4]
    return nstr

  # 集計
  ele_i0 = collect(elements[i][0]) # 考慮要素内の有利特性の点数
  ele_i1 = collect(elements[i][1]) # 考慮要素内の有利特性の点数
  ele_j0 = collect(elements[j][0])
  ele_j1 = collect(elements[j][1])
  ele_DF = pd.DataFrame([ele_i0, ele_i1, ele_j0, ele_j1]
                        , columns=[f'{list(profile.keys())[i]} & {list(profile.keys())[j]}の点数']
                        , index=[elements[i][0], elements[i][1], elements[j][0],elements[j][1]])

  # 戻り値
  return DF92part, ele_DF # 一覧表DF、考慮要素内の各特性の点数DF

# -------------------------------- score_cal関数への入力と出力表示のための関数
def DF_maker(ignore):
  # 関数 score_cal関数に入力する(i,j)の組の選別
  BL = [0,1,2]
  BL.remove(ignore)#  (0,1), (0,2), (1,2)

  # 演算と表示
  print(f'→ {list(profile.keys())[ignore]}を無視\n') #  (i,j)=(0,1)
  display(score_cal(BL[0],BL[1])[0]) # 一覧表DF
  display(score_cal(BL[0],BL[1])[1]) # 考慮要素内の各特性の点数DF
# --------------------------------------------------------------------------

# 実行
ignore = int(input(f'年齢=0, 扶養家族=1, 勤続年数=2 のいずれかを入力: ', ))
DF_maker(ignore)


年齢=0, 扶養家族=1, 勤続年数=2 のいずれかを入力: 2
→ 勤務年数を無視

		若・有	若・無	老・有	老・無	点数
若・有	NaN		〇		〇		〇		3
若・無	×		NaN		×		〇		1
老・有	×		〇		NaN		〇		2
老・無	×		×		×		NaN		0


年齢 & 扶養家族の点数
若	4
老	2
有	5
無	1
年齢=0, 扶養家族=1, 勤続年数=2 のいずれかを入力: 1
→ 扶養家族を無視

		若・長	若・短	老・長	老・短	点数
若・長	NaN		〇		〇		〇		3
若・短	×		NaN		×		〇		1
老・長	×		〇		NaN		〇		2
老・短	×		×		×		NaN		0


年齢 & 勤務年数の点数
若	4
老	2
長	5
短	1
年齢=0, 扶養家族=1, 勤続年数=2 のいずれかを入力: 0
→ 年齢を無視

		有・長	有・短	無・長	無・短	点数
有・長	NaN		〇		〇		〇		3
有・短	×		NaN		〇		〇		2
無・長	×		×		NaN		〇		1
無・短	×		×		×		NaN		0


扶養家族 & 勤務年数の点数
有	5
無	1
長	4
短	2




p268 3つの表[表9.2]から点数を集計

# コードは上記の続き
Weight_DF = pd.concat([score_cal(0,1)[1],score_cal(0,2)[1],score_cal(1,2)[1]], axis=1)
Weight_DF['合計'] = Weight_DF.sum(axis=1, skipna=True)
Weight_DF



p268 各人の得点を求める 表9.3

# コードは上記の続き
import numpy as np
DF93 = pd.DataFrame([Weight_DF.index.values.tolist()
                     ,Weight_DF['合計'].values.tolist()
                     ,['','レ','','レ','レ','']
                     ,['レ','','レ','','','レ']
                     ,['','レ','レ','','','レ']]
                    ,index=['カテゴリ','ウェイト','A曹長','B二等兵','C軍曹'])
DF93_bool = (DF93 == 'レ') # レ判定
scores_list = []
for person in range(2,DF93.shape[0]):
  score = 0
  for score_col in range(DF93.shape[1]):
    cell = DF93_bool.iloc[person][score_col]
    if cell == True:
      score += DF93.iloc[1][score_col] #該当行のウェイト数値を加算
  scores_list.append(score)
bool93_col = pd.DataFrame([scores_list], columns =DF93.index[2:5]).T
DF93 = pd.concat([DF93,bool93_col], axis=1)
DF93.columns = ['年齢','年齢','扶養家族','扶養家族','勤務年数','勤務年数','得点']
DF93 = DF93.fillna('')
DF93
  
  




p269 基準が数値で与えられたら [数量化 Ⅰ 類] 表9.4

  • 外的基準が数値で付与。←→外的基準が分類で付与(数量化Ⅱ類)
  • 2次元の情報を1次元の数値に数量化。
(工程)
  1. 各次元(例:学科)の項目(例:学科であれば優・並)ごとに重みを変数(例:学科=優であれば、\(x_1\))で設定 [表9.5]
  2. 評価対象(例:甲)ごとに予測値(例:\(x_1+y_1+c\)) を設定 [表9.5]
  3. 予測値と外的数値基準(例:甲であれば5点)とが最も適合するように、最小二乗法で、重み変数を定める。(二乗和をした式[式9.3]の各偏微分式[式9.4]が0になるように連立方程式を解く) 
  4. 求めた重み変数(\(x_1,x_2,y_1,y_2\))を用いて、評価対象ごとの予想得点を求める(1次元化)
import pandas as pd
index_info =['甲','乙','丙','丁','戊']
DF94 = pd.DataFrame([['優','優',5]
                   ,['優','優',3]
                   ,['優','並',1]
                   ,['並','優',4]
                   ,['並','優',2]]
                  , index = index_info
                  , columns =['学科試験','面接試験','入社後の実績'])
DF94
	学科試験	面接試験	入社後の実績
甲	優		優		5
乙	優		優		3
丙	優		並		1
丁	並		優		4
戊	並		優		2



p269 表9.5 ウェイトを求める手がかり

# マルチ・インデックス(初期使用)
mult_index = pd.MultiIndex.from_tuples(
    [("学科", "優",'x1'), ("学科",  "並",'x2')
    ,("面接","優",'y1'), ("面接", "並",'y2')]
    ,names=["Item", "Category",'Weight'])

# マルチ・コラム(DF転置後に使用)
mult_column = pd.MultiIndex.from_tuples(
    [("学科", "優",'x1'), ("学科",  "並",'x2')
    ,("面接","優",'y1'), ("面接", "並",'y2'),
     ('作業用','',''),('実績d','','')]
    ,names=["Item", "Category",'Weight'])

# 空のDFを作成
DF95b = pd.DataFrame(
    {'甲':['','','','']
     ,'乙':['','','','']
     ,'丙':['','','','']
     ,'丁':['','','','']
     ,'戊':['','','','']}
    ,index = mult_index) # 初期マルチ・インデックス
DF95 = DF95b.T # 転置

# 学科の優・劣にレ点
for person in range(DF95.shape[0]): # 人数分繰り返し
  if DF94.iloc[person][0]=='優': # DF94 の情報をチェック
    DF95.iloc[person,(0,0)] = 'レ' # 学科の優にレ点
  else:
    DF95.iloc[person,(1,1)] = 'レ' # 学科の並にレ点

# 面接の優・劣にレ点
for person in range(DF95.shape[0]):
  if DF94.iloc[person][1]=='優':
    DF95.iloc[person,(2,2)] = 'レ' # 面接の優にレ点
  else:
    DF95.iloc[person,(3,3)] = 'レ' # 面接の並にレ点

# レ点セルについて、ウェイトを集計
DF95_bool = (DF95 == 'レ') # レ点判定の論理値取得
scores_list = []
for person in range(DF95.shape[0]):
  score = []
  for score_col in range(DF95.shape[1]):
    cell = DF95_bool.iloc[person][score_col]
    if cell == True: # レ点あり(True判定)の場合
      score.append(list(DF95.columns)[score_col][2]) #該当行のウェイトを加算
  scores_list.append(score)

# DFの仮組み
bool93_col = pd.DataFrame([scores_list], columns =DF95.index[0:5]).T
DF95 = pd.concat([DF95,bool93_col], axis=1)
DF95 = pd.concat([DF95,DF94['入社後の実績']], axis=1)
DF95.columns = mult_column # 転置後マルチ・コラム

# 予測値e列DFの生成
explist = []
for item in range(DF95.shape[0]):
  equation = DF95['作業用'][item][0] +' + ' +DF95['作業用'][item][1]+' + c' # 数式表現化
  explist.append(equation)
expdata = pd.DataFrame(explist)

mult_column2 = pd.MultiIndex.from_tuples(
    [('予測値e','計算式','')]
    ,names=["Item", "Category",'Weight']) # 予測値e 列用のコラム
expdata.index = index_info
expdata.columns = mult_column2

# DFの結合と整除
DF95 = pd.concat([DF95,expdata],axis=1) # 予測値e 列DFを結合
DF95.drop(DF95.columns[[4]], axis=1) # 作業用列を削除
DF95 = DF95.loc[:, ['学科','面接','予測値e', '実績d']] # 列の順序を整理
DF95





p271 数式の計算(DFから情報を取得)

import sympy as sy

# シンボル文字を指定
x1 = sy.Symbol("x1",real=True)
x2 = sy.Symbol("x2",real=True)
y1 = sy.Symbol("y1",real=True)
y2 = sy.Symbol("y2",real=True)

c_value = str(DF95['実績d'].mean())

expected_list = []
for expected in range(5):
  c_update = DF95['予測値e']['計算式'][expected].replace('c', c_value) # cを平均値3に変更
  expected_list.append(c_update)

# 式9.2~式9.3
equ_93 = 0
for exp in range(len(expected_list)):
  seed = (DF95['実績d'][exp]- sy.sympify(expected_list[exp]))**2 # (実績-予測式)の2乗
  equ_93 += seed.expand() # 展開計算

print(f'式9.3:')
display(equ_93) # 式9.3





p271 数式の計算(式の展開、偏微分)

import sympy as sy

# シンボル文字を指定
x1 = sy.Symbol("x1",real=True)
x2 = sy.Symbol("x2",real=True)
y1 = sy.Symbol("y1",real=True)
y2 = sy.Symbol("y2",real=True)
c = DF95['実績d'].mean()

# DF95['予測値e'][計算式][0]~[4]
e_Kou = x1 + y1 + c
e_Otsu = x1 + y1 + c
e_Hei = x1 + y2 + c
e_Tei = x2 + y1 + c
e_Bo = 	x2 + y1 + c
expected_list = [e_Kou, e_Otsu, e_Hei, e_Tei, e_Bo]

# 式9.2~式9.3
equ_93 = 0
for exp in range(len(expected_list)):
  seed = (DF95['実績d'][exp]- expected_list[exp])**2 # (実績-予測式)の2乗
  equ_93 += seed.expand() # 展開計算
print(f'式9.3:')
display(equ_93) # 式9.3

# 式9.4
print(f'\n式9.4:')
for equ_94 in (x1, x2, y1, y2):
  display(equ_93.diff(equ_94)) # 式9.3 を各シンボル文字で偏微分





p271 数式の計算(連立方程式の解法)

  • 連立方程式を行列形式の方程式にする。
  • 変数の係数を取得:SymPy の coeff 機能を使用
  • 方程式の解法:NumPy の solve 機能を利用
# 式9.5~9.6
from numpy.linalg import solve

# 連立方程式を行列で解く
## 左辺の係数
left = [] # 行列方程式の左辺の係数を格納するリスト
for diffs in equ_93_diffs:
  left_list = [] # 各微分方程式の係数を格納するリスト
  for variable in (x1,x2,y1,y2):
    left_list.append(float(diffs.coeff(variable))) # 各変数に対応する係数を抽出
  left.append(left_list)
## 右辺の定数項
right = [] # 行列方程式の右辺(係数)を格納するリスト
for diffs in equ_93_diffs:
  right.append(float(-diffs.coeff(x1,0).coeff(x2,0).coeff(y1,0).coeff(y2,0))) # 定数項を抽出
## 解法を実行
answer = solve(left, right)

# 文字数と解とをリスト化
answers_list = []
tick = 0
for equ_96 in (x1, x2, y1, y2):
  answers_list.append((equ_96,round(answer[tick],2)))
  tick +=1

# 結果を表示
for ans in range(len(answers_list)):
  print(answers_list[ans])
(x1, 0.0)
(x2, -1.0)
(y1, 1.0)
(y2, -2.0)



p272~273 数式の計算(解の代入)

  • 計算式に変数を代入して計算:NumPy の subs 機能を使用
substitution = answers_list # 代入リスト(文字数と解のリスト)
expected_numbers = []
for expected in expected_list: # 予測値eの計算式
  expected_numbers.append(expected.subs(substitution)) # 計算式に解を代入して計算

# 整数値リストに変換
DF_EN = []
for item in expected_numbers:
  DF_EN.append(float(item))

# DF化
en_DF = pd.DataFrame([DF_EN]).T
en_DF.index = index_info
mult_column3 = pd.MultiIndex.from_tuples(
    [('予測値e','数値','')]
    ,names=["Item", "Category",'Weight']) # 予測値e 列用のコラム
en_DF.index = index_info
en_DF.columns = mult_column3

# DFの結合と整除
DF95 = pd.concat([DF95, en_DF],axis=1) # 予測値eの計算式列DFを結合
DF95 = DF95.loc[:, ['学科','面接','予測値e', '実績d']] # 列の順序を整理
DF95




p273 基準が分類で与えられたら(数量化 Ⅱ 類)

外的判断基準が、数値ではなく、分類(例:合格/不合格)である場合に、因子(例:学科成績、面接成績)の加重度合いを求める。
import pandas as pd
import numpy as np

allcolumns = ['','合否']
item =['学科','面接']
category = ['優','並']
weight = ['x1','x2','y1','y2']
person_list = ['ウェイト','甲','乙','丙','丁','戊','己']
result = [weight
          ,['レ','','レ','']
          ,['レ','','','レ']
          ,['レ','','','レ']
          ,['','レ','レ','']
          ,['','レ','レ','']
          ,['','レ','','レ']]

# 階層付きDF化
mult_column = pd.MultiIndex.from_product([item, category]) # 横軸の階層化
DF = pd.DataFrame(result, index =person_list, columns= mult_column)
mult_column2 = pd.MultiIndex.from_product([['合否'], ['']])
result = pd.DataFrame(['','〇','〇','×','〇','×','×'], index =person_list, columns = mult_column2)
DF = pd.concat([DF,result], axis=1)
DF



p275 得点を因子のウェイトで数式表現する

因子のウェイト:  \(x_1,x_2,y_1,y_2\) 

# 演算対象をDF本体部分に限定
DFs = DF.iloc[1:len(person_list),0:len(category)*2]

# レ点セルについて、ウェイトを集計し得点列を形成
DFs_bool = (DFs == 'レ') # レ点判定の論理値取得
scores_list = ['']
for person in range(DFs.shape[0]):
  score = ''
  for score_col in range(DFs.shape[1]):
    cell = DFs_bool.iloc[person][score_col]
    if cell == True: # レ点あり(True判定)の場合
      score += '+' + DF.iloc[0][score_col] #該当行のウェイト文字列を追記
  score = score.lstrip('+') # 表記調整
  scores_list.append(score)

# 得点列を既存DFに統合
mult_column3 = pd.MultiIndex.from_product([['得点'], ['']])
scores_set = pd.DataFrame(scores_list, index =person_list, columns = mult_column3)
DF2 = pd.concat([DF,scores_set], axis=1)
DF2



p275 ウェイト付けの例

\(x_1=3\)

\(x_2=0\)

\(y_1=2\)

\(y_2=1\)


# ウェイトを代入後の数値に置換
DF_num = DF.replace({'x1':3, 'x2':0, 'y1':2, 'y2':1})

# 演算対象をDF本体部分に限定
DFsn = DF_num.iloc[1:len(person_list),0:len(category)*2]
# DFsn = DF.iloc[1:len(person_list),0:len(category)*2]

# レ点セルについて、ウェイトを集計し得点列を形成
DFsn_bool = (DFsn == 'レ') # レ点判定の論理値取得

# 得点を計算、集計、DF化
scores_list = ['']
for person in range(DFsn.shape[0]):
  score = 0
  for score_col in range(DFsn.shape[1]):
    cell = DFsn_bool.iloc[person][score_col]
    if cell == True: # レ点あり(True判定)の場合
      score += DF_num.iloc[0][score_col] #該当行のウェイト文字列を追記
  scores_list.append(score)
# 得点列を既存DFに統合
mult_column3 = pd.MultiIndex.from_product([['得点'], ['']])
scores_set = pd.DataFrame(scores_list, index =person_list, columns = mult_column3)
DF3 = pd.concat([DF_num,scores_set], axis=1)
display(DF3)

# 各グループの平均値
# 合格者
Passed = DF3[DF3['合否']=='〇'] # 合格者リスト
Pass_m = Passed['得点'].mean() # 合格者の平均点数
print(f'合格者の平均点数:{round(Pass_m,2)}')
# 不合格者
Failure = DF3[DF3['合否']=='×'] # 不格者リスト
Failure_m =Failure['得点'].mean() # 不合格者の点数平均
print(f'不合格者の平均点数:{round(Failure_m,2)}')

# 合格者平均と不合格者平均の2要素からなる集合の分散
PF_m_list = [Pass_m, Failure_m] # リスト化
import statistics
var_2 = statistics.pvariance(PF_m_list) # 分散
print(f'合格者平均と不合格者平均の2要素からなる集合の分散:{round(var_2,2)}')

# 全体の分散
var_all = statistics.pvariance(DF3['得点'][1:]) # 全体の分散
print(f'全体の分散:{var_all}')

# 相関比
print(f'相関比:{round(var_2/var_all,2)}')





p275 相関比が最大となるようなウェイト付け ~相関比を数式化する~

(作業工程)

  1. 全体集合の分散を求める。
  2. 合格者平均と不合格者平均の2要素からなる集合の分散を求める。
  3. 相関比(1と2の比率)を数式化する。
  4. 相関比の極値ひいては最大値を求めるため、相関比をウェイト変数で偏微分する。
  5. 各偏微分の結果が0になるようなウェイト変数の値を求める。
import sympy as sy

# シンボル文字を指定
x1 = sy.Symbol("x1",real=True)
x2 = sy.Symbol("x2",real=True)
y1 = sy.Symbol("y1",real=True)
y2 = sy.Symbol("y2",real=True)
u = sy.Symbol("u",real=True)
v = sy.Symbol("v",real=True)

KOU = x1 + y1
OTS = x1 + y2
HEI = x1 + y2
TEI =	x2 + y1
BO	=	x2 + y1
KI  = x2 + y2

all_list = [KOU, OTS, HEI, TEI, BO, KI]
pass_list = [KOU, OTS, TEI]
failure_list = [HEI, BO, KI]

# 全平均
all_average = np.mean(all_list)
print('全平均')
display(all_average)

# 全体の分散
numerator = 0
for element in all_list:
  numerator += (element - all_average)**2
denominator = len(all_list)
all_variance = numerator/denominator
print('\n全体の分散')
display(all_variance)

# vの式
v_num = (all_variance*12).expand()
print('\n全体の分散 =v/12 と置くときの v')
display(v_num)

# 合格者平均と不合格者平均の2要素からなる集合の分散
# リスト化
list_pandf = [np.mean(pass_list), np.mean(failure_list)]
# 分散
numerator_pandf = 0
for element_pf in list_pandf:
  numerator_pandf += (element_pf - all_average)**2
denominator_pf = len(list_pandf)
pf_variance = numerator_pandf/denominator_pf
print('\n合格者平均と不合格者平均の2要素からなる集合の分散')
display(pf_variance)

# uの式
u_num = (pf_variance*36).expand()
print('\n合格者平均と不合格者平均の2要素からなる集合の分散 =u/36 と置くときの u')
display(u_num)

# 相関比
print('\n相関比')
cr_uv_num = (u_num/v_num)/(36/12)
cr_uv = cr_uv_num.subs({(v_num,v),(u_num,u)})
display(cr_uv)





p277 相関比が最大となるようなウェイト付け ~相関比の式の偏微分~

相関比

\(r = \cfrac {1} {3} \cfrac{u}{v}\)

が極値ひいては最大となるような  \(x_1,x_2,y_1,y_2\)  の条件を見つける。

→ 相関比を \(x_1,x_2,y_1,y_2\) で偏微分したうえで、それら4式が0となる条件を見つける。

→ 微分の商の公式:

\(\left (\dfrac {u} {v}\right)'=\dfrac {u'v-uv'} {v^2}\)

で、右辺の分子部分 \(u'v-uv'\)が0になる条件を見つける。

equ911_diffs = [] # 偏微分した各式の分子を格納するリスト
equ_num = 1
for derivative in (x1,x2,y1,y2): # 各ウェイトで微分
  rdx_nume = u_num.diff(derivative)*v - v_num.diff(derivative)*u # 分数関数の微分公式の分子
  equ911_diffs.append(rdx_nume)
  print(f'({equ_num})式の左辺:')
  equ_num += 1
  display(rdx_nume)
  print(f'\n')





p277 相関比が最大となるようなウェイト付け ~相関比微分式の因数分解~

上式(1)と(2)は同値(右辺は0であるためマイナスをかけると同じ)。→上式(1)のみを検討のため、因数分解。

上式(3)と(4)は同値(右辺は0であるためマイナスをかけると同じ)。→上式(3)のみを検討のため、因数分解。

# (1)、(3)式に u, vを代入
subst_01 = equ911_diffs[0].subs({(v,v_num),(u,u_num)}).expand()
subst_03 = equ911_diffs[2].subs({(v,v_num),(u,u_num)}).expand()

# 因数分解
equ911f01 = sy.factor(subst_01)
equ911f03 = sy.factor(subst_03)

# 表示
print('(1)式:')
display(equ911f01)
print('\n')
print('(3)式:')
display(equ911f03)



因数分解をした(1)式 \(=0\) が成立するためには、

\(y_1-y_2=0\)

\(x_1-x_2-y_1+y_2=0\)

\(x_1-x_2+y_1-y_2=0\)

のいずれかが成立することが必要。

もっとも、本設例では、成績の重みづけは等しくない(差があるように成績付ける)ので、

\(y_1\neq y_2\)

である。よって、上記3式のうち1番上の式は成立しない。このため、因数分解をした(1)式 \(=0\) が成立するためには、以下のいずれかが成立する必要がある。

\(x_1-x_2=y_1-y_2\neq0\)

\(x_1-x_2=-(y_1-y_2)\neq0\)

そして、本設例では、優秀 \(>\) 並みとなるように点数をつけるので、以下の不等式が成立する。

\(x_1>x_2\)

\(y_1>y_2\)

よって、上記2式のうち、下の式は成立しない(左辺がプラス、右辺がマイナスとなるため)。結局、本設例に合致する条件は、上記1式のみとなる。そして、上記1式は、以下の3式が共に成立することを意味する。

\(x_1-x_2=y_1-y_2\)

\(x_1\neq x_2\)

\(y_1\neq y_2\)

この条件は、式(3)の因数分解の左式を0とさせる(中項が0になる)。



p277 求めたウェイトを基に相関比を確認

# ウェイトを代入後の数値に置換
DF_num = DF.replace({'x1':1, 'x2':0, 'y1':1, 'y2':0})

# 演算対象をDF本体部分に限定
DFsn = DF_num.iloc[1:len(person_list),0:len(category)*2]
# DFsn = DF.iloc[1:len(person_list),0:len(category)*2]

# レ点セルについて、ウェイトを集計し得点列を形成
DFsn_bool = (DFsn == 'レ') # レ点判定の論理値取得

# 得点を計算、集計、DF化
scores_list = ['']
for person in range(DFsn.shape[0]):
  score = 0
  for score_col in range(DFsn.shape[1]):
    cell = DFsn_bool.iloc[person][score_col]
    if cell == True: # レ点あり(True判定)の場合
      score += DF_num.iloc[0][score_col] #該当行のウェイト文字列を追記
  scores_list.append(score)
# 得点列を既存DFに統合
mult_column3 = pd.MultiIndex.from_product([['得点'], ['']])
scores_set = pd.DataFrame(scores_list, index =person_list, columns = mult_column3)
DF3 = pd.concat([DF_num,scores_set], axis=1)
display(DF3)

# 各グループの平均値
# 合格者
Passed = DF3[DF3['合否']=='〇'] # 合格者リスト
Pass_m = Passed['得点'].mean() # 合格者の平均点数
print(f'合格者の平均点数:{round(Pass_m,2)}')
# 不合格者
Failure = DF3[DF3['合否']=='×'] # 不格者リスト
Failure_m =Failure['得点'].mean() # 不合格者の点数平均
print(f'不合格者の平均点数:{round(Failure_m,2)}')

# 合格者平均と不合格者平均の2要素からなる集合の分散
PF_m_list = [Pass_m, Failure_m] # リスト化
import statistics
var_2 = statistics.pvariance(PF_m_list) # 分散
print(f'合格者平均と不合格者平均の2要素からなる集合の分散:{round(var_2,2)}')

# 全体の分散
var_all = statistics.pvariance(DF3['得点'][1:]) # 全体の分散
print(f'全体の分散:{round(var_all,2)}')

# 相関比
print(f'相関比:{round(var_2/var_all,2)}')





p278 基準がなくとも ~数量化 Ⅲ 類~ 表9.7


import pandas as pd
person_list = ['A','B','C','D','E']
flower_list = ['Tulip','Morning glory','Horsetail','Rose']
DF = pd.DataFrame([['レ','','','レ']
                   ,['レ','','レ','']
                   ,['レ','レ','','']
                   ,['','','','レ']
                   ,['','レ','レ','']]
                  ,index = person_list
                  ,columns = flower_list)





p278 表9.7 行と列を配置換え

rea_DF = DF.reindex(index=['D','A','C','B','E'], columns=['Horsetail','Morning glory','Tulip','Rose'])
rea_DF



p279 相関係数

num_DF = rea_DF.copy()
num_DF.index = [4,3,2,1,0]
num_DF.columns = range(num_DF.shape[1])
display(num_DF)

x_list = []
y_list = []

for index in num_DF.index: # 行要素を繰返し
  for column in num_DF.columns: # 列要素を繰り返し
    if num_DF.loc[index][column] == 'レ': # レ点があるセル
      x_list.append(column) # コラム名を格納
      y_list.append(index) # インデックス名を格納
x_series = pd.Series(x_list)
y_series = pd.Series(y_list)
print(f'\n相関係数:{round(x_series.corr(y_series),3)}') # 相関係数