Python
sklearn

ロジスティック回帰

More than 1 year has passed since last update.

ロジスティック回帰とは

線形回帰と似ているが、目的変数が2値のときに利用する。
例えば、この人は商品を購入するか否か、棒に当たるか否か、引っ越すか否か、転職するか否かなどなど。

予測モデルを、以下のロジスティック関数(シグモイド関数)を使って作成。
スクリーンショット 2016-05-03 16.47.40.png

ロジスティック関数の形は以下となる。0〜1の値を取り、単調増加。
スクリーンショット 2016-05-03 16.48.21.png

目的変数の行列xと説明変数yの関係は以下。(y=ax+bの右辺をexpの-1乗する形。)
スクリーンショット 2016-05-03 16.49.51.png

利用データ用意

sklearnで不倫用のデータを使う。

get_affair_dataset.py
from sklearn.linear_model import LogisticRegression # ロジスティック回帰用
from sklearn.cross_validation import train_test_split # クロスバリデーションのsplit用
import statsmodels.api as sm

df = sm.datasets.fair.load_pandas().data # 不倫データのロード

不倫データの概要

describe_affair.py
df.head()

スクリーンショット 2016-05-03 16.53.17.png

rate_marriage:幸せ度、age:年齢、yrs_married:結婚年数、children:子供の数、religious:信仰度、educ:最終学歴、occupation:妻の職業、occupation_husb:旦那の職業、affairs:不倫経験(0より大きいと不倫経験あり)、Had_Affair:不倫フラグ(affairsが0>なら1がセットされてる)

簡単に不倫有無と適当なサンプルの関係をみる

easy_display1.py
# 年齢と不倫有無
sns.countplot('age', data = df.sort('age'), hue = 'Had_Affair', palette='coolwarm')

スクリーンショット 2016-05-03 16.57.17.png

easy_display2.py
# 結婚年数と不倫有無
sns.countplot('yrs_married', data = df.sort('yrs_married'), hue = 'Had_Affair', palette='coolwarm')

スクリーンショット 2016-05-03 16.57.21.png

easy_display3.py
# 子供の人数と不倫有無
sns.countplot('children', data = df.sort('children'), hue = 'Had_Affair', palette='coolwarm')

スクリーンショット 2016-05-03 16.57.35.png

年齢が上がる/結婚年数が多い/子供がいる方が不倫率が高い

※ ただし年齢が上がれば結婚年数が増えたり、子供の数が増えるなど、説明変数間でも関係性があると思われる。

ロジスティック回帰やってみる

前処理

やる前に、職業の変数がカテゴリ変数になっているので、これをダミー変数に置き換える。
カテゴリ変数とは、中の値の大小に意味がないもの。

change_dummy_value.py
# numpyのget_dummiesでdummy変数に変換。
occ_dummies = pd.get_dummies(df.occupation)
hus_occ_dummies = pd.get_dummies(df.occupation_husb)

# カラム名セット
occ_dummies.columns = ['occ1','occ2','occ3','occ4','occ5','occ6']
hus_occ_dummies.columns = ['hocc1','hocc2','hocc3','hocc4','hocc5','hocc6']

occ_dummies.head()

スクリーンショット 2016-05-03 17.13.59.png

上の感じで、occ1〜6のうちどれに当たるかが0,1のフラグで置き換えてあげる。

続けて、説明変数を取得する。

get_x.py
# Xに、元のデータフレームから職業、旦那の職業、結婚有無を削除したものをセット。
X = df.drop(['occupation', 'occupation_husb', 'Had_Affair'], axis =1) 
# 職業をダミー変数化したデータフレームを用意
dummys = pd.concat([occ_dummies, hus_occ_dummies], axis =1)
# 職業等を削除したデータフレームに、職業ダミー変数データフレームを結合
X = pd.concat([X, dummys], axis=1)

X.head()

ここまでの説明変数用データセット
スクリーンショット 2016-05-03 17.18.57.png

多重共線性

ある説明変数が、他の説明変数を1つまたは複数で表現できる場合、多重共線性があるという。例えば今回は、occ1は、occ2〜occ6の値で一意に決まる関係にある。(occ2〜6に1が1個以上あれば、occ1=0、そうでなければocc1=1)
この場合、逆行列が計算できなかったり、計算できても得られる結果の信頼性が低くなってしまう。
なので、これをなくすべく、occ1とhocc1を削除する。

drop_nonavailable_value.py
X = X.drop('hocc1', axis = 1)
X = X.drop('hocc1', axis = 1)
# affairsは、目的変数を作成するのに使っているので、これも説明変数から除く。
X = X.drop('affairs', axis =1 )

X.head()

最終的な形

スクリーンショット 2016-05-03 17.28.45.png

sklearnを使って実行

do_logistic_regression.py
# 目的変数セット
Y = df.Had_Affair
Y = np.ravel(Y)    # np.ravelでYを1次元配列にする

# ロジスティック回帰実行
log_model = LogisticRegression() # インスタンス生成 
log_model.fit(X, Y)              # モデル作成実行
log_model.score(X, Y)            # モデルの予測精度確認(72.6%)
> 0.7260446120012567

各変数の係数を確認

confirm_coefficient.py
# インスタンスの.coef_[0]に、係数が入っている
coeff_df = DataFrame([X.columns, log_model.coef_[0]]).T
coeff_df

スクリーンショット 2016-05-03 17.36.33.png

この係数が大きいところが、影響が多いところになる。
ただし、説明変数のデータの単位が統一されていないので、単純に横並びで比較はできない。
例えば、occ5はyrs_marriedの9倍くらいあるから、結婚年数は見なくてOK!!とは単純にならない。

ついでに

いつものごとく、trainとtestに分ける方法も書いとく。

do_logistic_regression_train_test.py
# trainとtest用データ用意
X_train, X_test, Y_train, Y_test = train_test_split(X, Y)

log_model2 = LogisticRegression() 
log_model2.fit(X_train, Y_train)           # trainデータでモデル作成
class_predict = log_model2.predict(X_test) # testデータを予測

from sklearn import metrics # 予測精度確認用

metrics.accuracy_score(Y_test, class_predict) # 精度確認
>0.73115577889447236

73%くらいの精度が出てることがわかる。