スタートページJavaScript他言語Python 目次>    ←散布図  →主成分分析

相関・回帰直線・信頼区間・重相関

keyword:基本統計量、共分散、回帰直線、相関係数、信頼区間、t分布、重相関
np.cov()、df.corr()
ライブラリ;scikit-learn(sklearn)、linear_model、linear_model.LinearRegression()

下記の青線の部分をGoogle Colaboratryの「コード」部分にコピーアンドペースト(ペーストは Cntl+V)して実行すれば、右図の画像が表示されます。


2変数の基本統計量

数学的説明

例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()

例2:scikit-learn(sklearn) を用いた線形回帰

線形回帰の分野では、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

例3:DataFrame へのsklearn の適用

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


信頼区間曲線

例4:例2への信頼区間曲線の追加

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. ]]

df.corr による重相関係数行列

単純な統計量を得るだけなら、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.