スタートページ> JavaScript> 他言語> Python 目次> ←散布図 →主成分分析
keyword:基本統計量、共分散、回帰直線、相関係数、信頼区間、t分布、重相関
np.cov()、df.corr()
ライブラリ;scikit-learn(sklearn)、linear_model、linear_model.LinearRegression()
下記の青線の部分をGoogle Colaboratryの「コード」部分にコピーアンドペースト(ペーストは Cntl+V)して実行すれば、右図の画像が表示されます。
例1:回帰直線 y=a+bx や相関係数を計算するプロセスを示します。
import numpy as np import matplotlib.pyplot as plt # 入力データ x = np.array([180,170,162,171,160,165,148,153,170,154,160,166,177,152]) y = np.array([ 70, 55, 45, 60, 45, 55, 43, 50, 56, 46, 48, 47, 61, 35]) # ====================================== 基本統計量の計算 n = len(x) # 組数:n= 14 mx = x.mean() # 平均:mx=163.4 my = y.mean() # my=51.14 sxx = np.sum((x-mx)**2) # 偏差平方和:sxx=sum((x-mx)**2)=1203.43 syy = np.sum((y-my)**2) # syy=sum((y-my)**2)=1041.71 vx = sxx/n # 標本分散:vx=sxx/n=85.96 vy = syy/n # vy=syy/n=74.41 # ======================== 共分散 sxy sxy = np.sum((x-mx)*(y-my)) / n # 共分散A(標本分散による):sxy = np.sum((x-mx)*(y-my))/n=69.795 # ========= 注 np.cov による共分散 npcov = np.cov(x, y) # 共分散行列B(np.covによる):cov = np.cov(x, y)= # [[92.57142857 75.16483516] # [75.16483516 80.13186813]] cov = npcov[0,1] # 共分散Bのxy要素 cov = npcov[0,1]=75.16 # 共分散Bは不偏推定量 自由度(n-1)を用いているため共分散Aと異なる # cov*(n-1)/n cov=69.795 と修正すると共分散Aと一致する # ======================== 回帰直線 y = a + bx の計算 b = sxy / vx # b = sxy/vx= 0.812 a = my - b*mx # a = my-a*mx= -81.56 print('回帰直線:y =', a, ' + ', b, '*x') # y = -81.56 + 0.812*x # ======================= 相関係数 r r2 = (sxy/vx)*(sxy/vy) # 決定係数:r2=(sxy/vx)*(sxy/vy)=0.76 r = np.sqrt(r2) # 相関係数:r = ±sqrt(r2) =0.87 if b < 0: r = -r # 回帰直線が右上がりなら+、右下がりなら- # ====================================== 散布図の表示 fig = plt.figure() ax = fig.add_subplot(1,1,1) ax.scatter(x, y) ax.grid() # ===================================== 散布図に回帰直線を上書き xmin = x.min() xmax = x.max() ax.plot([xmin,xmax],[a+b*xmin,a+b*xmax], color='r', linewidth=4) # ================= 散布図に相関係数を上書き text = 'r = ' + str(round(r,2)) ax.text(170,35, text, size=14) fig.show()
線形回帰の分野では、scikit-learn (sklearn)拡張ライブラリを用いると、簡潔に多様な情報が得られます。
これを用いるには、次の宣言をします。
from sklearn import linear_model
slr = linear_model.LinearRegression()
sklearnでは、y = a0 + a1.x1 + a2.x2 + … のような多変数回帰を対象にしています。それで、入力データは、説明関数(y)は1次元ベクトル(行ベクトルでも列ベクトルでもよい)、説明変数(x)は二次元の列ベクトルになります。
x の入力記述を簡素化するために、右の xt のようにしたいときがあります。そのときは x = xt.T として転置します。
x1 x2 y = [100, 200, 300] x = [ [10, 11] xt = [ [10, 20, 30] x1 [20, 21] [11, 21, 31] ] x2 [30, 31] ]
import numpy as np import matplotlib.pyplot as plt # scikit-learnの利用宣言 from sklearn import linear_model slr = linear_model.LinearRegression() # ========================入力データ y = np.array([ 70, 55, 45, 60, 45, 55, 43, 50, 56, 46, 48, 47, 61, 35]) xt = np.array([[180,170,162,171,160,165,148,153,170,154,160,166,177,152]]) # sklearnでは、 x = xt.T # 転置行列 # array([[180], # [170], # ; # [152]]) # ======================== 回帰の計算 slr.fit(x, y) # これだけで回帰式や相関係数などが計算できます。 # 得られる諸元 説明は冗長になるので、この通りで取り出せると理解する b = slr.coef_[0] # 切片 slr.coef_ が [0.812] なのでスカラーにする a = slr.intercept_ # x の係数 r2 = slr.score(x, y) # 決定係数 相関係数の2乗 r = np.sqrt(r2) # 相関係数 print('a (切片, slr.intercept_) = ',a, ' + b (回帰係数. slr.coef_) = ', b, '*x') # a (切片, slr.intercept_) = -81.56 + b (回帰係数. slr.coef_) = 0.81*x print('r2 (決定係数, slr.score(x, y) ) = ', r2, ', r (信頼係数 r = sqrt(r2) ) = ', r) # r2 (決定係数, slr.score(x, y) ) = 0.76, r (信頼係数 r = sqrt(r2) ) = 0.87 # ======================== 散布図の作成 fig = plt.figure() ax = fig.add_subplot(1,1,1) ax.scatter(x, y) # xはxiでもよい ax.grid() # ============= 回帰直線の追加 ax.plot(x, slr.predict(x), color='r', linewidth=4) # slr.predict(x):x における回帰直線のyの値を戻す # x の各点での y = a + bx を計算し、各点 (x0,y0),(x1,y1),(x2,y2), をつないだ線になる # ================= 散布図に相関係数を上書き text = 'r = ' + str(round(r,2)) ax.text(170,35, text, size=14) fig.show()
a (切片, slr.intercept_) = -81.555 + b (回帰係数. slr.coef_) = 0.8119 *x
r2 (決定係数, slr.score(x, y) ) = 0.7616, r (信頼係数 r = sqrt(r2) ) = 0.8727
sklearnでの関数が列ベクトル形の行列を想定していることは、入力データを DataFrame(df) で与えることを前提としているように思われます。
しかし、内部処理ではほとんどが数値計算になるので、ndarray に変換することになります。
import numpy as np import pandas as pd # Pandasの利用宣言 import matplotlib.pyplot as plt from sklearn import linear_model slr = linear_model.LinearRegression() # ========================入力データ df = pd.DataFrame([[180, 70], [170, 55], [162, 45], [171, 60], [160, 45], [165, 55], [148, 43], [153, 50], [170, 56], [154, 46], [160, 48], [166, 47], [177, 61], [152, 35]], columns = ['x', 'y']) x = df[['x']].values # 列ベクトルにする y = df['y'].values # 行ベクトルでよい # ======================== 回帰の計算 slr.fit(x, y) # ============= 得られた諸元 b = slr.coef_[0] # slr.coef_ が [0.812] なのでスカラーにする a = slr.intercept_ r2 = slr.score(x, y) r = np.sqrt(r2) print('a (切片, slr.intercept_) = ',a, ' + b (回帰係数. slr.coef_) = ', b, '*x') # a (切片, slr.intercept_) = -81.56 + b (回帰係数. slr.coef_) = 0.81*x print('r2 (決定係数, slr.score(x, y) ) = ', r2, ', r (信頼係数 r = sqrt(r2) ) = ', r) # r2 (決定係数, slr.score(x, y) ) = 0.76, r (信頼係数 r = sqrt(r2) ) = 0.87 # ======================== 散布図の作成 df.plotによる記述もできます fig = plt.figure() ax = df.plot(x='x', y='y', kind = 'scatter', grid=True) # ============= 回帰直線の追加 ax.plot(x, slr.predict(x), color='r', linewidth=4) # ================= 散布図に相関係数を上書き text = 'r = ' + str(round(r,2)) ax.text(170,35, text, size=14) fig.show()
a (切片, slr.intercept_) = -81.555 + b (回帰係数. slr.coef_) = 0.8119 *x
r2 (決定係数, slr.score(x, y) ) = 0.7616, r (信頼係数 r = sqrt(r2) ) = 0.8727
import numpy as np import matplotlib.pyplot as plt # scikit-learnの利用宣言 from sklearn import linear_model slr = linear_model.LinearRegression() # ========================入力データ y = np.array([ 70, 55, 45, 60, 45, 55, 43, 50, 56, 46, 48, 47, 61, 35]) xt = np.array([[180,170,162,171,160,165,148,153,170,154,160,166,177,152]]) x = xt.T # ======================== 回帰の計算 slr.fit(x, y) # ============= 得られた諸元 b = slr.coef_[0] a = slr.intercept_ r2 = slr.score(x, y) r = np.sqrt(r2) if b < 0: r = -r print('a (切片, slr.intercept_) = ',a, ' + b (回帰係数. slr.coef_) = ', b, '*x') # a (切片, slr.intercept_) = -81.56 + b (回帰係数. slr.coef_) = 0.81*x print('r2 (決定係数, slr.score(x, y) ) = ', r2, ', r (信頼係数 r = sqrt(r2) ) = ', r) # r2 (決定係数, slr.score(x, y) ) = 0.76, r (信頼係数 r = sqrt(r2) ) = 0.87 # ====================================== ここからが追加部分 p2 = 0.95 # 信頼確率 95% (信頼区間の計算に用いる) # ======================== 基本統計量の計算 n = len(x) # 組数:n= 14 xmin = x.min() # x の最小値 xmax = x.max() # x の最大値 mx = x.mean() # 平均:mx=163.4 sdx = x.std() # x の標準偏差 # ======================== t分布の値とscipyライブラリ from scipy import stats # t.ppt(α,自由度) を使うために必要 p = (1+p2)/2 # p2=0.95は「入力データ」で指定、片側確率 (1+0.95)/2=0.975 t = stats.t.ppf(p, n-2) # t(0.975,12)=2.179 print('t(',p, ', ', n-2, ') = ',t) # ================= 誤差の統計量 yr = a + b*x # 各xに対応する回帰直線のyの値(xが列ベクトルなので) yr = yr[:,0] # 列ベクトルを行ベクトルに変換 ye = y - yr # 誤差 see = np.sum(ye**2) # 誤差平方和 248.308 sde = np.sqrt(see/(n-2)) # 誤差標準偏差 4.548 # ================= x,y の付けなおし u = np.linspace(xmin, xmax, 10) # 新規に等間隔のxの値 v = a + b*u # uに対応する回帰直線の値 # ================= 信頼区間の計算 ci = t*sde/np.sqrt(n)*np.sqrt(1+((u-mx)/sdx)**2) # 信頼区間幅 upper = v + np.abs(ci) lower = v - np.abs(ci) # ======================== 散布図等の作成 fig = plt.figure() ax = fig.add_subplot(1,1,1) ax.set_xlim(xmin, xmax) # グラフの表示範囲 ax.scatter(x, y) # 散布図 ax.plot(u, v, 'r-', linewidth=4) # 回帰直線 ax.plot(u, upper, 'g--', linewidth=2) # 上側信頼範囲 ax.plot(u, lower, 'g--', linewidth=2) # 下側信頼範囲 ax.grid() text = ('r = ' + str(round(r,2)) + '\nci = ' + str(p2)) ax.text(170, 35, text, size=14)
a (切片, slr.intercept_) = -81.555 + b (回帰係数. slr.coef_) = 0.8119 *x
r2 (決定係数, slr.score(x, y) ) = 0.7616, r (信頼係数 r = sqrt(r2) ) = 0.8727
t( 0.975,12 ) = 2.17 ci = 0.95
複数の説明変数がある場合です。回帰直線は、
y = a + b[0]x0 + b[1]x1 + …
になります。
説明変数間の値が大きく違うと、正確な処理ができので、各変数を、平均0、標準偏差1に変換、0~1の範囲に変換などの正規化をすべきです。
しかし煩雑になりますので、ここでは正規化の操作はしていません。
X軸が複数の変数になるため、散布図は作れません。ここでは、a, b の値を求めるだけにします。 その数学的な説明は、「重相関と重回帰」を参照
import numpy as np import matplotlib.pyplot as plt from sklearn import linear_model slr = linear_model.LinearRegression() # ========================入力データ xi = np.array([[180,170,162,171,160,165,148,153,170,154,160,166,177,152], [ 86, 84, 85, 84, 83, 82, 74, 81, 82, 82, 76, 75, 83, 77]]) y = np.array( [ 70, 55, 45, 60, 45, 55, 43, 50, 56, 46, 48, 47, 61, 35]) x = xi.T # ======================== 基本統計量 m = len(x[0]) # xの系列数 2 n = len(y) # 要素数 14 ymean = y.mean() # y の平均 51.14 # ======================== 回帰の計算 slr.fit(x, y) # ============= 回帰直線の式 b = slr.coef_ # 偏回帰係数(回帰直線の係数) [0.7291 0.341] print('b = ',b) a = slr.intercept_ # _回帰直線の定数項 -95.541 print('y = ',a,' + ',b[0],'*x[0] + ',b[1],'*x[1] + ...') # y = -95.541 + 0.7291*x[0] + 0.341*x[1] # ============= 決定係数 yr = slr.predict(x) ye = y - yr sall = ((y - ymean)**2).sum() # 全変動 1041.71 sreg = ((yr - ymean)**2).sum() # 回帰変動 807.80 sres = sall - sreg # 残差変動 233.91 r2 = 1-(sres/(n-1-m))/(sall/(n-1)) # 決定係数 0.7346 r = np.sqrt(r2) # 相関係数 0.857 # ============= 相関行列 corr = np.corrcoef(x.T) # T は転置行列 ┌ x[0]とx[1]の相関 print('corr\n',corr) # array([[1. , 0.605], # [0.605, 1.])
b = [0.7287 0.3405]
y = -95.5414 + 0.7287*x[0] + 0.3405*x[1]
corr
[[1 0.6053]
[0.6053 1. ]]
単純な統計量を得るだけなら、Pandas の DataFrame(df)を対象にした関数を使うのが簡便です。
import numpy as np import pandas as pd df = pd.DataFrame([ [3, 4, 4, 5, 4, 4], [6, 6, 7, 8, 7, 7], [6, 5, 7, 5, 5, 6], [6, 7, 5, 4, 6, 5], [5, 7, 6, 5, 5, 5], [4, 5, 5, 5, 6, 6], [6, 6, 7, 6, 4, 4], [5, 5, 4, 5, 5, 6], [6, 6, 6, 7, 7, 6], [6, 5, 6, 6, 5, 5], [5, 4, 4, 5, 5, 5], [5, 5, 6, 5, 4, 5], [6, 6, 5, 5, 6, 5], [5, 5, 4, 4, 5, 3], [5, 6, 4, 5, 6, 6], [6, 6, 6, 4, 4, 5], [4, 4, 3, 6, 5, 6], [6, 6, 7, 4, 5, 5], [5, 3, 4, 3, 5, 4], [4, 6, 6, 3, 5, 4]], columns = ['c0','c1','c2','c3','c4','c5']) # ============= 基本統計量 print('df.describe()\n',df.describe()) df.describe() # c0 c1 c2 c3 c4 c5 # count 20. 20. 20. 20. 20. 20. # mean 5.2 5.35 5.3 5. 5.2 5.1 # std 0.89 1.04 1.26 1.21 0.894 0.97 # min 3. 3. 3. 3. 4. 3. # 25% 5. 5. 4. 4. 5. 4.75 # 50% 5. 5.5 5.5 5. 5. 5. # 75% 6. 6. 6 5.25 6 6. # max 6. 7. 7. 8. 7. 7. # ============= 相関係数 df.corr() # c0 c1 c2 c3 c4 c5 # c0 1. 0.486 0.597 0.242 0.276 0.218 # c1 0.486 1. 0.557 0.125 0.316 0.172 # c2 0.597 0.557 1. 0.240 0.037 0.146 # c3 0.242 0.125 0.240 1. 0.436 0.627 # c4 0.276 0.316 0.037 0.436 1. 0.583 # c5 0.218 0.172 0.146 0.627 0.583 1.