Googleクラスルーム
GeoGebraGeoGebra Classroom

判別する〜集団の平均点の垂直2等分線

0.問題

<相関比> 2つの集団がまざって分布しているとき、どこに境界線をひくのかという問題が判別問題です。 分散は平均との差の2乗の平均ですが、個数で割る前の2乗和を変動といいます。 平均からの距離の2乗を集団で合計したものです。 集団Pと集団Nのそれぞれの平均をmp, mn,とします。全体の平均をmとします。 全体変動(全変動)はT=Σ(x-m)2、 集団内の変動の和(群内変動)はI=Σ(xp-mp)2+Σ(xnーmn)2 集団の平均どうしの変動和(群間変動)はJ=Σ(m − mp)2+Σ(m - mn)2 T=I+J(全変動=群内変動+群間変動)となります。 TにしめるJの値が大きいと、それぞれの集団のかたまりが強いことになります。 2集団の分離がよりはっきりしています。 相関比=群間変動の比率=J/Tまたは、J/Iの値が判別の強さを表しているともいえます。 <課題1> 男子10人女子10人の身長x、体重yのデータから、男女を判別するx1とx2の関係式をさがす。
番号12345678910
x151.1155.9159.4154.6162.9158.3171.7160.8153.4161.2
y43.746.249.556.350.963.559.851.758.346.8
性FFFFFFFFFF
番号11121314151617181920
x184.9181.3171.4168.6162.3179.9179.5173.4167.9177.9
y75.578.966.261.055.780.666.161.261.377.2
性MMMMMMMMMM

1.課題1

男子群p、女子群nを判別する直線w1x1+w2x2=bをw={{w1},{w2}},x={{x1},{x2}}と列ベクトルをとると、 法線ベクトルへの正射影の長さがbとなるベクトル方程式wt x=bとなるね。 そのwとbを求めたい。 x=(x1,x2)のデータが原点を通り法線ベクトルを方向ベクトルとする「直線上L」に射影される。 男子xpの平均ベクトルはmp,女子xnの平均ベクトルはmn. 平均ベクトルの直線L上の射影はw' mp, w' mnとなる。'は随伴行列(共役複素数の転置)。 群間J=(w' mp - w' mn)2=(w' (mp- mn))(w'( mp -mn))'=w' (mp-mn)(mp-mn)' w = w' B wとおける。 群内I=Σ(w' mp-w' xp)2+Σ(w' mn- w' xn)2=w' (Σ(mp-xp)(mp-xp)' + Σ(mn-xn)(mn-xn)' ) w=w' W wとおく。 J/I=J(w)= w' B w / w' W w が最大になるwを求めるにはJ(w)のwでの偏微分=0から、 結局、B w =λ W wとなるwとλを求めればよいね。 Wの逆行列をかけると、inv(W)B w =λ w という固有方程式ができる。 だから、行列inv(W)Bの固有値をα、固有ベクトルをwとすると、.......(途中略)。w=inv(W)(mp-mn). wは2つの群の平均mp、mnの中心を2等分する直線の傾き、つまり、平均の差のベクトルに直交して、 中点を通ればよいのです。正射影の長さbは直線L上での中点だから、b=1/2(w'(mp+mn)) これで判別直線wt x=bと決まるね。
<julia> using LinearAlgebra using Statistics x11=[184.9,181.3,171.4,168.6,162.3,179.9,179.5,173.4,167.9,177.9] x12=[151.1,155.9,159.4,154.6,162.9,158.3,171.7,160.8,153.4,161.2] x21=[75.5,78.9,66.2,61.0,55.7,80.6,66.1,61.2,61.3,77.2] x22=[43.7,46.2,49.5,56.3,50.9,63.5,59.8,51.7,58.3,46.8] m1=[mean(x11) mean(x21)] m2=[mean(x12) mean(x22)] x1=[[x1 x2] for (x1,x2) in zip(x11,x21)] x2=[[x1 x2] for (x1,x2) in zip(x12,x22)] mx1=[transpose([m1[1]-p m1[2]-q]) for (p,q) in x1] mx2=[transpose([m2[1]-p m2[2]-q]) for (p,q) in x2] s1=sum([[x[1]^2 x[1]*x[2] ;x[1]*x[2] x[2]^2] for x in mx1]) s2=sum([[x[1]^2 x[1]*x[2] ;x[1]*x[2] x[2]^2] for x in mx2]) W=s1+s2 invW=inv(W) w = normalize(invW *(m1-m2)') mid=(m1+m2) *0.5 #w[1]x1+w[2]x2-b=0 slope= -w[1]/w[2] y_intersect=b/w[2] sl=floor(-w[1]*10/w[2])/10 yi= mid[2]+ mid[1]*(-sl) print("sl=",sl,",yi=",yi) #======================================================== sl=-3.1,yi=577.662
<julia> #LDA線形判別分析(Linear Discriminant Analysis)のモジュールを使う。 #Xp,Xnを行列で渡しやすくするためDataFramesモジュールを使う。 w'x+b=0を解くので、bの符号は上記と反対になるので注意。 using LinearAlgebra using DataFrames using MultivariateStats xp=[184.9,181.3,171.4,168.6,162.3,179.9,179.5,173.4,167.9,177.9] yp=[75.5,78.9,66.2,61.0,55.7,80.6,66.1,61.2,61.3,77.2] xn=[151.1,155.9,159.4,154.6,162.9,158.3,171.7,160.8,153.4,161.2] yn=[43.7,46.2,49.5,56.3,50.9,63.5,59.8,51.7,58.3,46.8] p=DataFrame(datax=xp,datay=yp) n=DataFrame(datax=xn,datay=yn) #データをデータフレームから取り出して行列として渡すためにfloat.(Matrix()')でくるむ。 Xp = float.(Matrix(p)'); Xn = float.(Matrix(n)'); f = MultivariateStats.fit(LinearDiscriminant, Xp, Xn); w=f.w b=f.b sl=-w[1]/w[2] yi=-b/w[2] print("sl=",sl,",yi=",yi) #======================================================== sl=-3.022262684795434,yi=564.6938610775744
# PyPlotを使って視覚化する。 using PyPlot xp=[184.9,181.3,171.4,168.6,162.3,179.9,179.5,173.4,167.9,177.9] yp=[75.5,78.9,66.2,61.0,55.7,80.6,66.1,61.2,61.3,77.2] xn=[151.1,155.9,159.4,154.6,162.9,158.3,171.7,160.8,153.4,161.2] yn=[43.7,46.2,49.5,56.3,50.9,63.5,59.8,51.7,58.3,46.8] # 男子の点 plt.scatter(xp,yp) m = ["m","m","m","m","m","m","m","m","m","m"] for (i, txt) in enumerate(m) plt.annotate(txt, (xp[i], yp[i])) end # 女子の点 plt.scatter(xn,yn) f = ["f","f","f","f","f","f","f","f","f","f"] for (i, txt) in enumerate(f) plt.annotate(txt, (xn[i], yn[i])) end # 判別直線 xs=150:185 #+はブロードキャストのため.+にする。 ys=(xs.*sl).+yi plt.plot(xs,ys)
Image
<Python> import numpy as np from statistics import mean from numpy.linalg import inv,norm xp=np.array([184.9, 181.3, 171.4, 168.6, 162.3, 179.9, 179.5, 173.4 ,167.9 ,177.9]) yp=np.array([75.5,78.9, 66.2, 61.0, 55.7, 80.6, 66.1 , 61.2, 61.3, 77.2 ]) xn=np.array([151.1, 155.9, 159.4, 154.6, 162.9,158.3,171.7,160.8,153.4,161.2]) yn=np.array([43.7, 46.2, 49.5, 56.3, 50.9, 63.5, 59.8, 51.7, 58.3, 46.8]) mp=np.array([mean(xp) ,mean(yp)]) mn=np.array([mean(xn) ,mean(yn)]) xp=[np.array([x1 ,x2]) for (x1,x2) in zip(xp,yp)] xn=[np.array([x1 ,x2]) for (x1,x2) in zip(xn,yn)] mxp=[np.array([mp[0]-p, mp[1]-q]) for (p,q) in xp] mxn=[np.array([mn[0]-p, mn[1]-q]) for (p,q) in xn] sp=sum([np.array([[x[0]**2 ,x[0]*x[1]] ,[x[0]*x[1], x[1]**2]]) for x in mxp]) sn=sum([np.array([[x[0]**2 ,x[0]*x[1]] ,[x[0]*x[1], x[1]**2]]) for x in mxn]) W=sp+sn invW=inv(W) w = invW @(mp-mn) w = w/norm(w) mid=(mp+mn) *0.5 #w[1]x1+w[2]x2-b=0 slope= -w[1]/w[2] y_intersect=b/w[2] sl=int(-w[0]*10/w[1])/10 yi= mid[1]+ mid[0]*(-sl) print("sl=",sl,",yi=",yi) #======================================================== sl= -3.0 ,yi= 560.98

判別直線の視覚化