158 KiB
158 KiB
Загрузка и распаковка данных¶
In [ ]:
from urllib.request import urlretrieve
from zipfile import ZipFile
ds_url = "https://github.com/PacktPublishing/Interpretable-Machine-Learning-with-Python/raw/master/datasets/aa-domestic-delays-2018.csv.zip"
ds_zip_filename = "data/aa-domestic-delays-2018.csv.zip"
urlretrieve(ds_url, ds_zip_filename)
with ZipFile(ds_zip_filename, "r") as zObject:
zObject.extractall(path="data")
Загрузка данных в Dataframe¶
In [22]:
import pandas as pd
orig_df = pd.read_csv("data/aa-domestic-delays-2018.csv")
orig_df.info()
Подготовка данных и конструирование признаков¶
In [23]:
df = orig_df.copy()
# Преобразование даты из строки в datetime
df["PLANNED_DEP_DATETIME"] = pd.to_datetime(df["PLANNED_DEP_DATETIME"])
# Получение месяца и дня недели вылета из даты для учета сезонности и особенностей дня недели
df["DEP_MONTH"] = df["PLANNED_DEP_DATETIME"].dt.month
df["DEP_DOW"] = df["PLANNED_DEP_DATETIME"].dt.dayofweek
# Удаление столбца с датой
df = df.drop(["PLANNED_DEP_DATETIME"], axis=1)
# Список аэропортов-хабов
hubs = ["CLT", "ORD", "DFW", "LAX", "MIA", "JFK", "LGA", "PHL", "PHX", "DCA"]
# Определение признака хаба для аэропортов вылета и назначения
is_origin_hub = df["ORIGIN"].isin(hubs)
is_dest_hub = df["DEST"].isin(hubs)
# Установка признака хаба для данных
df["ORIGIN_HUB"] = 0
df.loc[is_origin_hub, "ORIGIN_HUB"] = 1
df["DEST_HUB"] = 0
df.loc[is_dest_hub, "DEST_HUB"] = 1
# Удаление лишних столбцов
df = df.drop(["FL_NUM", "ORIGIN", "DEST"], axis=1)
# Удаление столбца с общим временем задержки прибытия, так как данные значения будут иметь сильное влияние на результат
df = df.drop(["ARR_DELAY"], axis=1)
df
Out[23]:
Формирование тестовой и обучающей выборок данных¶
In [24]:
from sklearn.model_selection import train_test_split
# Задание фиксированного случайного состояния для воспроизводимости результатов
rand = 9
# Выделение признака, который модель должна предсказать
y = df["CARRIER_DELAY"]
# Формирование множества признаков, на основе которых модель будет обучаться (удаление столбца с y)
X = df.drop(["CARRIER_DELAY"], axis=1).copy()
X_train, X_test, y_train_reg, y_test_reg = train_test_split(
X, y, test_size=0.15, random_state=rand
)
# Создание классов для классификаторов в виде двоичных меток (опоздание свыше 15 минут - 1, иначе - 0)
y_train_class = y_train_reg.apply(lambda x: 1 if x > 15 else 0)
y_test_class = y_test_reg.apply(lambda x: 1 if x > 15 else 0)
display(X_train)
display(y_train_reg)
display(y_train_class)
X_train.info()
Определение линейной корреляции признаков с целевым признаком с помощью корреляции Пирсона¶
In [25]:
corr = df.corr()
abs(corr["CARRIER_DELAY"]).sort_values(ascending=False)
Out[25]:
Использование регрессионных моделей для предсказания задержки рейса¶
In [26]:
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import PolynomialFeatures, StandardScaler
from sklearn import linear_model, tree, neighbors, ensemble, neural_network
reg_models = {
# Обобщенные линейные модели (GLM-модели)
"linear": {"model": linear_model.LinearRegression(n_jobs=-1)},
"linear_poly": {
"model": make_pipeline(
PolynomialFeatures(degree=2, interaction_only=False),
linear_model.LinearRegression(fit_intercept=False, n_jobs=-1),
memory=None
)
},
"linear_interact": {
"model": make_pipeline(
PolynomialFeatures(degree=2, interaction_only=True),
linear_model.LinearRegression(fit_intercept=False, n_jobs=-1),
memory=None
)
},
"ridge": {"model": linear_model.RidgeCV()},
# Деревья
"decision_tree": {
"model": tree.DecisionTreeRegressor(max_depth=7, random_state=rand)
},
# Ближайшие соседи
"knn": {"model": neighbors.KNeighborsRegressor(n_neighbors=7, n_jobs=-1)},
# Ансамблевые методы
"random_forest": {
"model": ensemble.RandomForestRegressor(
max_depth=7, random_state=rand, n_jobs=-1
)
},
# Нейронные сети
"mlp": {
"model": neural_network.MLPRegressor(
hidden_layer_sizes=(21,),
max_iter=500,
early_stopping=True,
random_state=rand,
)
},
}
Обучение и оценка регрессионных моделей¶
In [27]:
import math
from sklearn import metrics
for model_name in reg_models.keys():
print(f'Model: {model_name}')
fitted_model = reg_models[model_name]["model"].fit(
X_train.values, y_train_reg.to_numpy().ravel()
)
y_train_pred = fitted_model.predict(X_train.values)
y_test_pred = fitted_model.predict(X_test.values)
reg_models[model_name]["fitted"] = fitted_model
reg_models[model_name]["preds"] = y_test_pred
reg_models[model_name]["RMSE_train"] = math.sqrt(
metrics.mean_squared_error(y_train_reg, y_train_pred)
)
reg_models[model_name]["RMSE_test"] = math.sqrt(
metrics.mean_squared_error(y_test_reg, y_test_pred)
)
reg_models[model_name]["R2_test"] = metrics.r2_score(y_test_reg, y_test_pred)
Вывод оценки в виде таблицы¶
In [28]:
reg_metrics = pd.DataFrame.from_dict(reg_models, "index")[
["RMSE_train", "RMSE_test", "R2_test"]
]
reg_metrics.sort_values(by="RMSE_test").style.background_gradient(
cmap="viridis", low=1, high=0.3, subset=["RMSE_train", "RMSE_test"]
).background_gradient(cmap="plasma", low=0.3, high=1, subset=["R2_test"])
Out[28]:
Использование классификаторов для предсказания задержки рейса¶
In [29]:
from sklearn import naive_bayes
class_models = {
# Обобщенные линейные модели (GLM-модели)
"logistic": {"model": linear_model.LogisticRegression()},
"ridge": {
"model": linear_model.LogisticRegression(penalty="l2", class_weight="balanced")
},
# Дерево
"decision_tree": {
"model": tree.DecisionTreeClassifier(max_depth=7, random_state=rand)
},
# Ближайшие соседи
"knn": {"model": neighbors.KNeighborsClassifier(n_neighbors=7)},
# Наивный Байес
"naive_bayes": {"model": naive_bayes.GaussianNB()},
# Ансамблевые методы
"gradient_boosting": {
"model": ensemble.GradientBoostingClassifier(n_estimators=210)
},
"random_forest": {
"model": ensemble.RandomForestClassifier(
max_depth=11, class_weight="balanced", random_state=rand
)
},
# Нейронные сети
"mlp": {
"model": make_pipeline(
StandardScaler(),
neural_network.MLPClassifier(
hidden_layer_sizes=(7,),
max_iter=500,
early_stopping=True,
random_state=rand,
),
memory=None
)
},
}
Определение сбалансированности выборки для классификации¶
In [30]:
y_train_class[y_train_class == 1].shape[0] / y_train_class.shape[0]
Out[30]:
Обучение и оценка классификаторов¶
In [31]:
import numpy as np
for model_name in class_models.keys():
print(f"Model: {model_name}")
fitted_model = class_models[model_name]["model"].fit(
X_train.values,
y_train_class.to_numpy().ravel(),
)
y_train_pred = fitted_model.predict(X_train.values)
y_test_prob = fitted_model.predict_proba(X_test.values)[:, 1]
y_test_pred = fitted_model.predict(X_test.values)
class_models[model_name]["fitted"] = fitted_model
class_models[model_name]["probs"] = y_test_prob
class_models[model_name]["preds"] = y_test_pred
class_models[model_name]["Accuracy_train"] = metrics.accuracy_score(
y_train_class, y_train_pred
)
class_models[model_name]["Accuracy_test"] = metrics.accuracy_score(
y_test_class, y_test_pred
)
class_models[model_name]["Recall_train"] = metrics.recall_score(
y_train_class, y_train_pred
)
class_models[model_name]["Recall_test"] = metrics.recall_score(
y_test_class, y_test_pred
)
class_models[model_name]["ROC_AUC_test"] = metrics.roc_auc_score(
y_test_class, y_test_prob
)
class_models[model_name]["F1_test"] = metrics.f1_score(y_test_class, y_test_pred)
class_models[model_name]["MCC_test"] = metrics.matthews_corrcoef(
y_test_class, y_test_pred
)
Вывод оценки в виде таблицы¶
In [32]:
class_metrics = pd.DataFrame.from_dict(class_models, "index")[
[
"Accuracy_train",
"Accuracy_test",
"Recall_train",
"Recall_test",
"ROC_AUC_test",
"F1_test",
"MCC_test",
]
]
class_metrics.sort_values(by="ROC_AUC_test", ascending=False).style.background_gradient(
cmap="plasma", low=0.3, high=1, subset=["Accuracy_train", "Accuracy_test"]
).background_gradient(
cmap="viridis",
low=1,
high=0.3,
subset=["Recall_train", "Recall_test", "ROC_AUC_test", "F1_test", "MCC_test"],
)
Out[32]:
Интерпретация результатов для моделей на основе "белого ящика"¶
Линейная регрессия
In [33]:
coefs_lm = reg_models["linear"]["fitted"].coef_
intercept_lm = reg_models["linear"]["fitted"].intercept_
print("coefficients:\t%s" % coefs_lm)
print("intercept:\t%s" % intercept_lm)
print(
"y = %0.2f + %0.4fX1 + %0.4fX2 + %0.3fX3 + ..."
% (intercept_lm, coefs_lm[0], coefs_lm[1], coefs_lm[2])
)
In [34]:
coef_df = pd.DataFrame({"feature": X_train.columns.values.tolist(), "coef": coefs_lm})
display(coef_df)
In [35]:
import statsmodels.api as sm
linreg_mdl = sm.OLS(y_train_reg, sm.add_constant(X_train))
linreg_mdl = linreg_mdl.fit()
summary_df = linreg_mdl.summary2().tables[1]
summary_df = (
summary_df.drop(["const"]).reset_index().rename(columns={"index": "feature"})
)
summary_df["t_abs"] = abs(summary_df["t"])
summary_df.sort_values(by="t_abs", ascending=False).style.background_gradient(
cmap="plasma_r", low=0, high=0.1, subset=["P>|t|"]
).background_gradient(cmap="plasma_r", low=0, high=0.1, subset=["t_abs"])
Out[35]:
Гребневая регрессия
In [36]:
coefs_ridge = reg_models["ridge"]["fitted"].coef_
coef_ridge_df = pd.DataFrame(
{
"feature": X_train.columns.values.tolist(),
"coef_linear": coefs_lm,
"coef_ridge": coefs_ridge,
}
)
coef_ridge_df.style.background_gradient(cmap="viridis_r", low=0.3, high=0.2, axis=1)
Out[36]:
Полиномиальная регрессия
In [38]:
display(
reg_models["linear_poly"]["fitted"].get_params()["linearregression"].coef_.shape[0]
)
display(
reg_models["linear_interact"]["fitted"]
.get_params()["linearregression"]
.coef_.shape[0]
)
Логистическая регрессия
In [39]:
coefs_log = class_models["logistic"]["fitted"].coef_
intercept_log = class_models["logistic"]["fitted"].intercept_
print("coefficients:\t%s" % coefs_log)
print("intercept:\t%s" % intercept_log)
stdv = np.std(X_train, 0)
abs(
coefs_log.reshape(
21,
)
* stdv
).sort_values(ascending=False)
Out[39]:
Дерево решений
In [40]:
text_tree = tree.export_text(
class_models["decision_tree"]["fitted"],
feature_names=X_train.columns.values.tolist(),
)
print(text_tree)
In [41]:
dt_imp_df = pd.DataFrame(
{
"feature": X_train.columns.values.tolist(),
"importance": class_models["decision_tree"]["fitted"].feature_importances_,
}
).sort_values(by="importance", ascending=False)
dt_imp_df
Out[41]:
k ближайших соседей
In [42]:
print(X_test.loc[721043, :])
In [43]:
class_models["knn"]["fitted"].kneighbors(
X_test.loc[721043, :].values.reshape(1, 21), 7
)
Out[43]:
In [44]:
y_train_class.iloc[[105172, 571912, 73409, 89450, 77474, 705972, 706911]]
Out[44]:
In [45]:
class_models["knn"]["fitted"].effective_metric_
Out[45]:
Гауссов наивный Байес
In [46]:
class_models["naive_bayes"]["fitted"].class_prior_
Out[46]:
In [47]:
class_models["naive_bayes"]["fitted"].var_
Out[47]:
In [48]:
class_models["naive_bayes"]["fitted"].theta_
Out[48]: