0
2

説明変数間で相関が見られると多重共線性が発生する事があります。
多重共線性は

重回帰分析やロジスティック回帰分析などの多変量解析を行ったときに,互いに関連性の高い説明変数(独立変数)が存在すると解析上の計算が不安定となり,回帰式の精度がきょくたんに悪くなったり,回帰係数やオッズ比などが異常な値をとったりする場合があります.このように解析結果が不安定な状態となる現象を多重共線性(またはマルチコ現象)といいます. 出典:『多重共線性 multicollinearity - 一般社団法人 日本理学療法学会連合』

ということです。
で、多重共線性への対処は自分の知っている限りで3つ言われています。
今回はその3つをやってみようと思います。

ライブラリからデータの読み込み

from sklearn.decomposition import PCA
from statsmodels.api import OLS
import statsmodels.api as sm
import pandas as pd
import numpy as np

df = pd.read_csv("boston.csv")
y = df["PRICE"]
x = df.drop("PRICE", axis=1)
x.corr()

image.png
所々で相関が見られることが分かります。

VIF

VIFは相関行列の逆行列を作成した際に見られる対角行列の値で、特に値が大きい変数を除去します。

vif = np.diag(np.linalg.inv(x.corr().values))
df_vif = pd.DataFrame(vif)
df_vif.columns = ["VIF"]
df_vif.index = x.corr().index
df_vif.sort_values("VIF", ascending=True)

image.png
ここではTAXとRADを削除します。

x1 = sm.add_constant(x.copy())
x1 = x1.drop(["RAD", "TAX"], axis=1)
model1 = OLS(y, x1).fit()
model1.summary()

image.png

VIFを用いた場合はこのような結果になりました。

相関係数

x1.corr()

image.png

相関係数としては高い相関の部分もあります。

主成分分析

主成分分析で変数を圧縮して多重共線性に対処する方法もあり、その中でも主成分分析を使った回帰も存在するくらいです。

主成分を理解するための関数

import pandas as pd
def factor_exp(pca, x):
    col = []
    for i in range(len(x.columns)):
        col.append("第%d主成分"%(i+1))
    exp = pca.explained_variance_ratio_
    df_exp = pd.DataFrame(exp)
    df_exp.index = col
    df_exp.columns = ["寄与率"]
    df_exp_sum = df_exp.cumsum()
    com = pca.components_
    fac = []
    for i in range(len(exp)):
        fac.append(np.sqrt(exp[i])*com[i])
    df_fac = pd.DataFrame(fac)
    df_fac.columns = x.columns
    df_fac.index = col
    return df_fac, df_exp_sum

プログラム

pca = PCA()
x2 = x.copy()
for col in x.columns:
    x2[col] = (x[col] - x[col].mean()) / x[col].std()
pca.fit(x2)
df_fac, df_exp = factor_exp(pca, x2)
pca = PCA(0.9)
pca.fit(x2)
tx = pca.transform(x2)
columns = []
for i in range(len(tx[0])):
    columns.append("PC%d"%(i+1))
df_tx = pd.DataFrame(tx)
df_tx.columns = columns
x2 = sm.add_constant(df_tx)
model2 = OLS(y, x2).fit()
model2.summary()

image.png

VIFの時より補正R2は下がりましたがこのような結果になりました。

相関係数

x2.corr()

image.png

相関係数を見てみると無相関が多いことが分かります。

ただ決定的な問題点もあります。

問題点

df_fac

image.png

主成分分析を用いた回帰の場合はこの因子負荷量について理解した上で係数の意味を考えなきゃいけなく、その時に恣意的に解釈してしまう可能性があります。
この場合は第8主成分までで、その中で大きな値とマイナスに大きな値を基に係数を考察しなければなりません。

p値が有意水準以上の物を排除(非推奨)

エクセルでは上記の機能を持ち合わせていませんので、この方法でしか多重共線性を考慮する事ができないので一応紹介します。
エクセルではそのためp値が小さい方が変数の影響力が高いため、p値が大きい物から排除して対処します。

x3 = x.copy()
model3 = OLS(y, x3).fit()
model3.summary()

image.png

INDUSを削除

x3 = x3.drop("INDUS", axis=1)
model3 = OLS(y, x3).fit()
model3.summary()

image.png

AGEを削除

x3 = x3.drop("AGE", axis=1)
model3 = OLS(y, x3).fit()
model3.summary()

image.png

NOXを削除

x3 = x3.drop("NOX", axis=1)
model3 = OLS(y, x3).fit()
model3.summary()

image.png
これでp値は全て有意水準未満になりました。

問題点

これによって全ての係数が有意になりましたが、各変数の相関係数が低くなったわけではありません。

x3.corr()

image.png

なので勝手に値を入れても良いわけではありませんのでそこは注意しないといけません。

部分的最小二乗回帰

PLS (Partial Least Squares) モデルはxとYの関係から潜在的な因子を抽出する回帰モデルです。 PLSは主に計量化学の分野でスペクトル解析などに用いられてきた回帰分析手法ですが、説明変数であるxの特徴量間の相関と目的変数Yとの相関を同時に考慮しながら潜在変数を抽出するので、時系列データに対しても多重共線性を回避しつつ、より高い予測精度が得られることが期待されます。 出典:『多重共線性に対応した回帰モデル — ごちきか』

これはScikit-Learnでやります。

標準化した場合

from sklearn.cross_decomposition import PLSRegression
from sklearn.metrics import r2_score

df = pd.read_csv("boston.csv")
y = df["PRICE"]
x = df.drop("PRICE", axis=1)

for col in x.columns:
    x[col] = (x[col] - x[col].mean()) / x[col].std()

model = PLSRegression(n_components=8)
model.fit(x, y)
y_pred = model.predict(x)
print(r2_score(y, y_pred))
df_coef = pd.DataFrame(model.coef_).T
df_coef.index = x.columns
df_intercept = pd.DataFrame(model.intercept_)
df_intercept.index = ["intercept"]
df_coef = pd.concat([df_coef, df_intercept])
df_coef.columns = ["coef"]
df_coef
0.7405917744617034

image.png
標準化してありますので係数が純粋に影響度を表すことができています。
また、主成分分析を使った回帰分析よりも高い精度(R2:0.706)になっていることが分かります。

標準化していない場合

df = pd.read_csv("boston.csv")
y = df["PRICE"]
x = df.drop("PRICE", axis=1)

model = PLSRegression(n_components=8)
model.fit(x, y)
y_pred = model.predict(x)
print(r2_score(y, y_pred))
df_coef = pd.DataFrame(model.coef_).T
df_coef.index = x.columns
df_intercept = pd.DataFrame(model.intercept_)
df_intercept.index = ["intercept"]
df_coef = pd.concat([df_coef, df_intercept])
df_coef.columns = ["coef"]
df_coef
0.7405917744617034

image.png
今度は標準化していないためそのまま値を入れたときの係数になります。

まとめ

一番その時いい方法を使いましょう。

参考文献

多重共線性 multicollinearity - 一般社団法人 日本理学療法学会連合
多重共線性に対応した回帰モデル — ごちきか

0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2