スタートページ> JavaScript> 他言語> Python 目次> ←主成分分析 →クラスター分析
下記の青線の部分をGoogle Colaboratryの「コード」部分にコピーアンドペースト(ペーストは Cntl+V)して実行すれば、右図の画像が表示されます。
特性 c0,c1,c2,… をもつ多数のデータ群 A,B,C,… を、あらかじめグループ群(正群、負群)に区分しておき、それに統計的処理をするとにより、
Z = b0*c0 + b1*c1 + b2*c2 +…
の b0.b1.b2,… を決定します。
そして、新データが与えられたとき、Zを計算して、Z>0ならば正群、Z<0ならば負群に属しると判別します。
このような統計的方法を判別分析といい、Zを判別関数といいます。判別関数を算出する方法はいくつかありますが、代表的なものにフィッシャーの判別関数があります。
ここでは、各行に判別値(group)として1と-1を与え、c0~c5 の値から判別関数Zを作成します。 numpy.linalg や sklearn などのライブラリが用いれれていますが、その説明は「主成分分析」(pca.txt) を参照してください。

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
# ================================ 入力データ
df = pd.DataFrame([
['A',-1, 3, 4, 4, 5, 4, 4],
['B',-1, 6, 6, 7, 8, 7, 7],
['C', 1, 6, 5, 7, 5, 5, 6],
['D',-1, 6, 7, 5, 4, 6, 5],
['E', 1, 5, 7, 6, 5, 5, 5],
['F',-1, 4, 5, 5, 5, 6, 6],
['G', 1, 6, 6, 7, 6, 4, 4],
['H',-1, 5, 5, 4, 5, 5, 6],
['I', 1, 6, 6, 6, 7, 7, 6],
['J', 1, 6, 5, 6, 6, 5, 5],
['K',-1, 5, 4, 4, 5, 5, 5],
['L', 1, 5, 5, 6, 5, 4, 5],
['M', 1, 6, 6, 5, 5, 6, 5],
['N', 1, 5, 5, 4, 4, 5, 3],
['O',-1, 5, 6, 4, 5, 6, 6],
['P', 1, 6, 6, 6, 4, 4, 5],
['Q',-1, 4, 4, 3, 6, 5, 6],
['R', 1, 6, 6, 7, 4, 5, 5],
['S',-1, 5, 3, 4, 3, 5, 4],
['T', 1, 4, 6, 6, 3, 5, 4]],
columns = ['name','group','c0','c1','c2','c3','c4','c5'])
# =========== グループ別の行列に変換
# group の正負により、正群x1と負群x2に分割、計算に用いる列だけを取り込み
# ndarray に変換
x = df[['c0','c1','c2','c3','c4','c5']].values
x1 = df[df.group > 0].loc[:,['c0','c1','c2','c3','c4','c5']].to_numpy() # 正群
x2 = df[df.group < 0].loc[:,['c0','c1','c2','c3','c4','c5']].to_numpy() # 負群
# =========== 諸元の算出
n1 = len(x1) # x1 の要素数
n2 = len(x2) #
m1 = np.mean(x1, axis=0) # x1 の平均
m2 = np.mean(x2, axis=0) #
m = (m1*n1+m2*n1)/(n1+n2) # 全体の平均
#
# ========================= 判別関数の計算
#
import numpy.linalg as LA
sw = ((x1-m1).T @ (x1-m1)) + ((x2-m2).T @ (x2-m2))
sinv = np.linalg.inv(sw)
w = -sinv @ (m2 - m1)
print('重み:w = ', w)
print('平均:m = ', m)
print('判別関数:Z = w[0]*(c0-m[0]) + w[1]*(c1-m[1]) + w[2]*(c2-m[2]) + …; >0なら正群 <0なら負群')
# ============= 元配列に判別関数をあてはめる
df['Z'] = np.dot(x-m, w)
df['err'] = ' '
df.loc[(df.group * df.Z < 0), 'err'] = '×'
print(df)
# ============= 判別関数の算出としてはこれまでです
# 以降は図の作成のためです。
#
# 主成分分析
from sklearn.decomposition import FactorAnalysis as FA
corr = df.corr() # 共分散行列
eig, eigv = np.linalg.eig(corr) # eig 固有値
fa = FA(n_components=2, max_iter=500)
fa.fit(x)
df['f1'] = fa.fit_transform(x)[:,0] # 第1因子の値
df['f2'] = fa.fit_transform(x)[:,1] # 第2因子の値
# 散布図作成
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
df.loc[df['group'] > 0, 'color'] = 'b'
df.loc[df['group'] < 0, 'color'] = 'r'
ax.scatter(df.f1, df.f2, color=df.color)
for i, txt in enumerate(df.name):
ax.annotate(txt, (df.f1[i], df.f2[i]), size=14)
ax.grid()
ax.plot([-3,3],[0,0], color='k', linewidth=1)
ax.plot([0,0],[-2,2.5], color='k', linewidth=1)
fig.show()
判別関数の決定
重み:w = [ 0.060 0.045 0.102 0.015 -0.050 -0.118 ]
平均:m = [ 5.677 5.838 5.744 5.511 5.744 5.644 ]
判別関数:Z = w[0]*(c0-m[0]) + w[1]*(c1-m[1]) + w[2]*(c2-m[2]) + …;
>0なら正群 <0なら負群
元データへの当てはめ
name group c0 c1 c2 c3 c4 c5 Z err
0 A -1 3 4 4 5 4 4 -0.146
1 B -1 6 6 7 8 7 7 -0.031
2 C 1 6 5 7 5 5 6 0.097
3 D -1 6 7 5 4 6 5 0.036 ×
4 E 1 5 7 6 5 5 5 0.144
5 F -1 4 5 5 5 6 6 -0.277
6 G 1 6 6 7 6 4 4 0.445
7 H -1 5 5 4 5 5 6 -0.269
8 I 1 6 6 6 7 7 6 -0.030 ×
9 J 1 6 5 6 6 5 5 0.129
10 K -1 5 4 4 5 5 5 -0.195
11 L 1 5 5 6 5 4 5 0.104
12 M 1 6 6 5 5 6 5 0.006
13 N 1 5 5 4 4 5 3 0.071
14 O -1 5 6 4 5 6 6 -0.274
15 P 1 6 6 6 4 4 5 0.194
16 Q -1 4 4 3 6 5 6 -0.461
17 R 1 6 6 7 4 5 5 0.246
18 S -1 5 3 4 3 5 4 -0.152
19 T 1 4 6 6 3 5 4 0.127
・group と Z が異なる符号ならば、元データがこの判別関数に合致しなかったことを示します。
20件中、合致しなかったのはDとIの2件なので、この判別関数は適切だといえます。
・Zの絶対値が大ならば、明らかにその群に属するといえます。
0に近いものは、「どちらかといえばその群になるが、他の群である可能性は否定できない」といえます。
・この関係を可視化するとグラフになります。