RuleFit

Friedman, Jerome H and Bogdan E Popescu. “Predictive learning via rule ensembles.” The Annals of Applied Statistics. JSTOR, 916–54. (2008).(pdf)

実験用のデータを取得する

openmlで公開されている CC0 Public Domain のデータセットhouse_sales データセットを使用して回帰モデルを作成します。

上記openmlページではデータの出典が不明ですが自分が調べた限りではデータの提供元はここのようです。

import japanize_matplotlib
import matplotlib.pyplot as plt
import pandas as pd
from sklearn.datasets import fetch_openml

dataset = fetch_openml(data_id=42092)
X = pd.DataFrame(dataset.data, columns=dataset.feature_names)
X = X.select_dtypes("number")
y = dataset.target

データの中身を確認する

X.head(10)
bedroomsbathroomssqft_livingsqft_lotfloorswaterfrontviewconditiongradesqft_abovesqft_basementyr_builtyr_renovatedlatlongsqft_living15sqft_lot15
03.01.001180.05650.01.00.00.03.07.01180.00.01955.00.047.5112-122.2571340.05650.0
13.02.252570.07242.02.00.00.03.07.02170.0400.01951.01991.047.7210-122.3191690.07639.0
22.01.00770.010000.01.00.00.03.06.0770.00.01933.00.047.7379-122.2332720.08062.0
34.03.001960.05000.01.00.00.05.07.01050.0910.01965.00.047.5208-122.3931360.05000.0
43.02.001680.08080.01.00.00.03.08.01680.00.01987.00.047.6168-122.0451800.07503.0
54.04.505420.0101930.01.00.00.03.011.03890.01530.02001.00.047.6561-122.0054760.0101930.0
63.02.251715.06819.02.00.00.03.07.01715.00.01995.00.047.3097-122.3272238.06819.0
73.01.501060.09711.01.00.00.03.07.01060.00.01963.00.047.4095-122.3151650.09711.0
83.01.001780.07470.01.00.00.03.07.01050.0730.01960.00.047.5123-122.3371780.08113.0
93.02.501890.06560.02.00.00.03.07.01890.00.02003.00.047.3684-122.0312390.07570.0

RuleFitを実行する

Python implementation of the rulefit algorithm - GitHubの実装を使用してRuleFitを動かしてみます。

※実行する際は import warnings;warnings.simplefilter('ignore') は外してください

from rulefit import RuleFit
import warnings

warnings.simplefilter("ignore")  ## ConvergenceWarning

rf = RuleFit(max_rules=100)
rf.fit(X.values, y, feature_names=list(X.columns))
RuleFit(max_rules=100,
        tree_generator=GradientBoostingRegressor(learning_rate=0.01,
                                                 max_depth=100,
                                                 max_leaf_nodes=5,
                                                 n_estimators=28,
                                                 random_state=27,
                                                 subsample=0.04543939429397564))

作成されたルールを確認する

rules = rf.get_rules()
rules = rules[rules.coef != 0].sort_values(by="importance", ascending=False)
rules.head(10)
ruletypecoefsupportimportance
8gradelinear6.199314e+041.00000066184.725645
29sqft_living > 9475.0rule1.927942e+060.00101861491.753935
43grade > 8.5 & sqft_living > 3405.0 & long <= -...rule3.570118e+050.02444055126.384264
2sqft_livinglinear5.532732e+011.00000046347.924165
11yr_builtlinear-1.522004e+031.00000044393.726859
15sqft_living15linear5.344916e+011.00000034501.058499
62lat <= 47.516000747680664 & sqft_living <= 3920.0rule-6.549757e+040.36150731467.457947
103sqft_basement <= 3660.0 & grade > 9.5rule1.240216e+050.06822831270.434139
48sqft_living <= 9475.0 & grade > 9.5 & long > -...rule-1.473030e+050.04073329117.596559
67sqft_living <= 4695.0 & waterfront > 0.5 & sqf...rule3.936285e+050.00509228016.079499

ルールが正しいか確認してみる

sqft_above linear 8.632149e+01 1.000000 66243.550192 のルールに基づいて、sqft_above が増加すると y(price)が増える傾向にあるかどうか確認します。

plt.figure(figsize=(6, 6))
plt.scatter(X["sqft_above"], y, marker=".")
plt.xlabel("sqft_above")
plt.ylabel("price")

png

sqft_living <= 3935.0 & lat <= 47.5314998626709 rule -8.271074e+04 0.377800 40101.257833 のルールに該当するデータのみ抽出してみます。 係数がマイナスになっているので、このルールに該当するデータのy(price)は低い傾向にあるはずです。 log(y)を箱髭図で確認すると、確かにルールに該当しているデータのyはルールに該当しないデータのyと比較すると低くなっています。

applicable_data = np.log(
    y[X.query("sqft_living <= 3935.0 & lat <= 47.5314998626709").index]
)
not_applicable_data = np.log(
    y[X.query("not(sqft_living <= 3935.0 & lat <= 47.5314998626709)").index]
)

plt.figure(figsize=(10, 6))
plt.boxplot([applicable_data, not_applicable_data], labels=["ルールに該当", "ルールに該当しない"])
plt.ylabel("log(price)")
plt.grid()
plt.show()

png