今回はみんな大好き重回帰分析を行いと思います。
結果が分かりやすく企画側のメンバーにも説明しやすいので私は好んで使っています。
精度はXgBoostなどのアルゴリズムには敵わないと思いますが、係数や切片さえ出してしまえばシステムにも組み込みやすく(SQLだけでもスコアが出せる)、結果の解釈のしやすいとても優秀な手法だと思います。
評価指標
住宅IdごとのSalePrice(販売価格)を予測するコンペです。
評価指標は予測SalePriceと実測SalePriceの対数を取ったRoot-Mean-Squared-Error(RMSE)の値のようです。
重回帰分析
分析用データの準備
事前に欠損値処理や特徴量エンジニアリングを実施してデータをエクスポートしています。
本記事と同じ結果にするためには事前に下記記事を確認してデータを用意してください。
(その3-2) エイムズの住宅価格のデータセットのデータ加工①
(その3-3) エイムズの住宅価格のデータセットのデータ加工②
学習用データとスコア付与用データの読み込み
import pandas as pd
import numpy as np
# エイムズの住宅価格のデータセットの訓練データとテストデータを読み込む
df = pd.read_csv("/Users/hinomaruc/Desktop/blog/dataset/ames/ames_train.csv")
df_test = pd.read_csv("/Users/hinomaruc/Desktop/blog/dataset/ames/ames_test.csv")
df.head()
Id LotFrontage LotArea LotShape Utilities LandSlope OverallQual OverallCond MasVnrArea ExterCond ... SaleType_New SaleType_Oth SaleType_WD SaleCondition_Abnorml SaleCondition_AdjLand SaleCondition_Alloca SaleCondition_Family SaleCondition_Normal SaleCondition_Partial SalePrice 0 1 65.0 8450 3.0 3.0 2.0 7 5 196.0 2.0 ... 0.0 0.0 1.0 0.0 0.0 0.0 0.0 1.0 0.0 208500 1 2 80.0 9600 3.0 3.0 2.0 6 8 0.0 2.0 ... 0.0 0.0 1.0 0.0 0.0 0.0 0.0 1.0 0.0 181500 2 3 68.0 11250 2.0 3.0 2.0 7 5 162.0 2.0 ... 0.0 0.0 1.0 0.0 0.0 0.0 0.0 1.0 0.0 223500 3 4 60.0 9550 2.0 3.0 2.0 7 5 0.0 2.0 ... 0.0 0.0 1.0 1.0 0.0 0.0 0.0 0.0 0.0 140000 4 5 84.0 14260 2.0 3.0 2.0 8 5 350.0 2.0 ... 0.0 0.0 1.0 0.0 0.0 0.0 0.0 1.0 0.0 250000 5 rows × 335 columns
# 描画設定
from IPython.display import HTML
import seaborn as sns
from matplotlib import ticker
import matplotlib.pyplot as plt
sns.set_style("whitegrid")
from matplotlib import rcParams
rcParams['font.family'] = 'Hiragino Sans' # Macの場合
#rcParams['font.family'] = 'Meiryo' # Windowsの場合
#rcParams['font.family'] = 'VL PGothic' # Linuxの場合
rcParams['xtick.labelsize'] = 12 # x軸のラベルのフォントサイズ
rcParams['ytick.labelsize'] = 12 # y軸のラベルのフォントサイズ
rcParams['axes.labelsize'] = 18 # ラベルのフォントとサイズ
rcParams['figure.figsize'] = 18,8 # 画像サイズの変更(inch)
重回帰に使用する変数を選ぶ
ステップワイズ法など色々方法はありますが、今回も前回の単回帰分析のときと同じ目的変数と相関が高い変数を選びたいと思います。
エイムズのデータセットの相関係数を色々なパターンで確認した結果がありますので、詳細は(その3-3) エイムズの住宅価格のデータセットのデータ加工②をご参照ください。
SalePriceとNeighborhood、YearBuiltがそこそこ高い値になっていますが、これはNeighborhoodもしくはYearBuiltごとにSalePriceに違いがありそうという意味になるのではないかと思っています。
SalePriceとの相関比が高かったNeighborhoodを説明変数に採用しようと思います。(地理的情報も欲しいので)
後は相関係数がSalePriceと高かった下記変数も採用します。
'TotalLivArea'
,'OverallQual'
,'TotalBathRms'
,'GarageCars'
,'BsmtQual'
,'FullBath'
,'GarageFinish'
,'FireplaceQu'
,'TotRmsAbvGrd'
重回帰で学習を実施
# 説明変数
ana_cols=[
'TotalLivArea'
, 'OverallQual'
, 'TotalBathRms'
, 'GarageCars'
, 'BsmtQual'
, 'FullBath'
, 'GarageFinish'
, 'FireplaceQu'
, 'TotRmsAbvGrd'
#, 'Neighborhood_Blmngtn' avoid multi-correlation
, 'Neighborhood_Blueste'
, 'Neighborhood_BrDale'
, 'Neighborhood_BrkSide'
, 'Neighborhood_ClearCr'
, 'Neighborhood_CollgCr'
, 'Neighborhood_Crawfor'
, 'Neighborhood_Edwards'
, 'Neighborhood_Gilbert'
, 'Neighborhood_IDOTRR'
, 'Neighborhood_MeadowV'
, 'Neighborhood_Mitchel'
, 'Neighborhood_NAmes'
, 'Neighborhood_NPkVill'
, 'Neighborhood_NWAmes'
, 'Neighborhood_NoRidge'
, 'Neighborhood_NridgHt'
, 'Neighborhood_OldTown'
, 'Neighborhood_SWISU'
, 'Neighborhood_Sawyer'
, 'Neighborhood_SawyerW'
, 'Neighborhood_Somerst'
, 'Neighborhood_StoneBr'
, 'Neighborhood_Timber'
, 'Neighborhood_Veenker'
]
# 説明変数と目的変数を指定
X_train = df[ana_cols]
Y_train = df["SalePrice"] # 販売価格
# pipelineでデータを標準化してモデリングをする
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LinearRegression
pipeline = make_pipeline(StandardScaler(), LinearRegression())
# fitする
fit_pipeline = pipeline.fit(X_train,Y_train)
# モデルパラメータ一覧
fit_pipeline.get_params()
{'memory': None, 'steps': [('standardscaler', StandardScaler()), ('linearregression', LinearRegression())], 'verbose': False, 'standardscaler': StandardScaler(), 'linearregression': LinearRegression(), 'standardscaler__copy': True, 'standardscaler__with_mean': True, 'standardscaler__with_std': True, 'linearregression__copy_X': True, 'linearregression__fit_intercept': True, 'linearregression__n_jobs': None, 'linearregression__normalize': 'deprecated', 'linearregression__positive': False}
# stepsから重回帰モデルの部分を抽出
model_pipeline = fit_pipeline.named_steps["linearregression"] # or pipeline.steps[1][1]
# モデル部分のパラメーターを確認。
model_pipeline.get_params()
{'copy_X': True, 'fit_intercept': True, 'n_jobs': None, 'normalize': 'deprecated', 'positive': False}
重回帰結果の確認
# 説明変数の係数を確認
coef = pd.DataFrame()
coef["features"] = fit_pipeline.feature_names_in_
coef["coef"] = model_pipeline.coef_
coef["coef_pct"] = np.abs(coef["coef"]) / np.abs(coef["coef"]).sum()
HTML(coef.sort_values(by="coef_pct",ascending=False).to_html())
features coef coef_pct 0 TotalLivArea 22464.525511 0.125844 1 OverallQual 20364.588506 0.114080 24 Neighborhood_NridgHt 15854.404547 0.088815 23 Neighborhood_NoRidge 12645.355971 0.070838 30 Neighborhood_StoneBr 9725.537216 0.054482 2 TotalBathRms 8839.926197 0.049520 3 GarageCars 8204.538135 0.045961 29 Neighborhood_Somerst 7360.293267 0.041232 14 Neighborhood_Crawfor 7178.199611 0.040212 13 Neighborhood_CollgCr 7132.226311 0.039954 12 Neighborhood_ClearCr 5229.469167 0.029295 31 Neighborhood_Timber 5088.466379 0.028505 20 Neighborhood_NAmes 4797.936931 0.026878 7 FireplaceQu 4376.854466 0.024519 8 TotRmsAbvGrd 4363.487602 0.024444 32 Neighborhood_Veenker 4050.878743 0.022693 11 Neighborhood_BrkSide 3871.312478 0.021687 6 GarageFinish 3642.730978 0.020406 27 Neighborhood_Sawyer 3268.357349 0.018309 28 Neighborhood_SawyerW 3242.540578 0.018164 19 Neighborhood_Mitchel 2443.801094 0.013690 16 Neighborhood_Gilbert 2401.716704 0.013454 4 BsmtQual 2280.411070 0.012775 15 Neighborhood_Edwards 2089.149708 0.011703 5 FullBath -1415.309722 0.007928 22 Neighborhood_NWAmes 1182.566236 0.006625 10 Neighborhood_BrDale -908.328878 0.005088 25 Neighborhood_OldTown 895.071543 0.005014 17 Neighborhood_IDOTRR 882.738396 0.004945 9 Neighborhood_Blueste -771.641440 0.004323 21 Neighborhood_NPkVill -638.344959 0.003576 18 Neighborhood_MeadowV 530.240067 0.002970 26 Neighborhood_SWISU 369.783933 0.002071
model_pipeline.intercept_
180921.19589041095
TotalLivAreaとOverallQualのモデルへの寄与度が高くなっているので想定通りです。
モデルを適用し、SalePriceの予測をする
df_test["SalePrice"] = fit_pipeline.predict(df_test[ana_cols])
df_test[["Id","SalePrice"]]
Id SalePrice 0 1461 101790.595660 1 1462 152801.604799 2 1463 168658.886147 3 1464 185152.822065 4 1465 253677.152032 ... ... ... 1454 2915 69415.163313 1455 2916 87164.482335 1456 2917 153543.604803 1457 2918 104717.547211 1458 2919 232089.359360 1459 rows × 2 columns
予測できていそうです。
Kaggleにスコア付与結果をアップロード
df_test[["Id","SalePrice"]].to_csv("ames_submission.csv",index=False)
!/Users/hinomaruc/Desktop/blog/my-venv/bin/kaggle competitions submit -c house-prices-advanced-regression-techniques -f ames_submission.csv -m "#2 multiple regression"
100%|██████████████████████████████████████| 33.6k/33.6k [00:03<00:00, 9.90kB/s] Successfully submitted to House Prices - Advanced Regression Techniques
#2 multiple regression 0.21305
単回帰より若干スコアが向上しました。
使用ライブラリのバージョン
pandas Version: 1.4.3
numpy Version: 1.22.4
scikit-learn Version: 1.1.1
seaborn Version: 0.11.2
matplotlib Version: 3.5.2
まとめ
それほど劇的に精度が良くなるということはありませんでした。
説明変数の組み合わせを変えることによってもう少し精度は上がるかも知れませんが、他のモデルを試してみようと思います。
次は多項式回帰を試してみようと思います。多項式回帰は欠損値を推定するのに実業務で使ったことがあります。