Skip to content

Commit 08fad5d

Browse files
committed
add description exercise linear model
1 parent 1dba905 commit 08fad5d

1 file changed

Lines changed: 161 additions & 22 deletions

File tree

content/python_files/feature_engineering.py

Lines changed: 161 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -843,31 +843,99 @@ def splitter(X, y, index_generator):
843843
# .to_dict()
844844
# )
845845

846+
# %% [markdown]
847+
#
848+
# ### Exercise: non-linear feature engineering coupled with linear predictive model
849+
#
850+
# Now, it is your turn to make a predictive model. Towards this end, we request you
851+
# to preprocess the input features with non-linear feature engineering:
852+
#
853+
# - the first step is to impute the missing values using a `SimpleImputer`. Make sure
854+
# to include the indicator of missing values in the feature set (i.e. look at the
855+
# `add_indicator` parameter);
856+
# - use a `SplineTransformer` to create non-linear features. Use the default parameters
857+
# but make sure to set `sparse_output=True` since it subsequent processing will be
858+
# faster and more memory efficient with such data structure;
859+
# - use a `VarianceThreshold` to remove features with potential constant features;
860+
# - use a `SelectKBest` to select the most informative features. Set `k` to be chosen
861+
# from a log-uniform distribution between 100 and 1,000 (i.e. use `skrub.choose_int`);
862+
# - use a `Nystroem` to approximate an RBF kernel. Set `n_components` to be chosen
863+
# from a log-uniform distribution between 10 and 200 (i.e. use `skrub.choose_int`).
864+
# - finally, use a `Ridge` as the final predictive model. Set `alpha` to be
865+
# chosen from a log-uniform distribution between 1e-6 and 1e3 (i.e. use
866+
# `skrub.choose_float`).
867+
#
868+
# Use a scikit-learn `Pipeline` using `make_pipeline` to chain the steps together.
869+
#
870+
# Once the predictive model is defined, apply it on the `feature_with_dropped_cols`
871+
# expression. Do not forget to define that `target` is the `y` variable.
872+
873+
846874
# %%
847-
# TODO: Exercise applying a linear model with some additional feature engineering
875+
# Here we provide all the imports for creating the predictive model.
848876
from sklearn.feature_selection import SelectKBest, VarianceThreshold
849877
from sklearn.impute import SimpleImputer
850878
from sklearn.linear_model import Ridge
851879
from sklearn.kernel_approximation import Nystroem
852880
from sklearn.pipeline import make_pipeline
853881
from sklearn.preprocessing import SplineTransformer
854882

855-
model = make_pipeline(
856-
SimpleImputer(add_indicator=True),
857-
SplineTransformer(sparse_output=True),
858-
VarianceThreshold(threshold=1e-6),
859-
SelectKBest(k=skrub.choose_int(100, 1_000, log=True, name="n_selected_splines")),
860-
Nystroem(
861-
n_components=skrub.choose_int(
862-
10, 200, log=True, name="n_components", default=150
863-
)
883+
# %%
884+
# Write your code here.
885+
#
886+
#
887+
#
888+
#
889+
#
890+
#
891+
#
892+
#
893+
#
894+
#
895+
#
896+
897+
# %%
898+
predictions_ridge = features_with_dropped_cols.skb.apply(
899+
make_pipeline(
900+
SimpleImputer(add_indicator=True),
901+
SplineTransformer(sparse_output=True),
902+
VarianceThreshold(threshold=1e-6),
903+
SelectKBest(
904+
k=skrub.choose_int(100, 1_000, log=True, name="n_selected_splines")
905+
),
906+
Nystroem(
907+
n_components=skrub.choose_int(
908+
10, 200, log=True, name="n_components", default=150
909+
)
910+
),
911+
Ridge(
912+
alpha=skrub.choose_float(1e-6, 1e3, log=True, name="alpha", default=1e-2)
913+
),
864914
),
865-
Ridge(alpha=skrub.choose_float(1e-6, 1e3, log=True, name="alpha", default=1e-2)),
915+
y=target,
866916
)
867-
868-
predictions_ridge = features_with_dropped_cols.skb.apply(model, y=target)
869917
predictions_ridge
870918

919+
# %% [markdown]
920+
#
921+
# Now that you defined the predictive model, let's make a similar analysis than earlier.
922+
# First, let's make a sanity check that plot forecast of our model on a subset of the
923+
# training data to make a sanity check.
924+
925+
# %%
926+
# Write your code here.
927+
#
928+
#
929+
#
930+
#
931+
#
932+
#
933+
#
934+
#
935+
#
936+
#
937+
#
938+
871939
# %%
872940
altair.Chart(
873941
pl.concat(
@@ -887,6 +955,31 @@ def splitter(X, y, index_generator):
887955
x="prediction_time:T", y="value:Q", color="key:N"
888956
).interactive()
889957

958+
959+
# %% [markdown]
960+
#
961+
# Now, let's evaluate the performance of the model using cross-validation. Use the
962+
# time-based cross-validation splitter `ts_cv_5` defined earlier. Make sure to compute
963+
# the R2 score and the mean absolute percentage error. Return the training scores as
964+
# well as the fitted pipeline such that we can make additional analysis.
965+
#
966+
# Does this model perform better or worse than the previous model?
967+
# Is it underfitting or overfitting?
968+
969+
# %%
970+
# Write your code here.
971+
#
972+
#
973+
#
974+
#
975+
#
976+
#
977+
#
978+
#
979+
#
980+
#
981+
#
982+
890983
# %%
891984
cv_results_ridge = predictions_ridge.skb.cross_validate(
892985
cv=ts_cv_5,
@@ -900,6 +993,26 @@ def splitter(X, y, index_generator):
900993
n_jobs=-1,
901994
)
902995

996+
# %% [markdown]
997+
#
998+
# Call all cross-validated predictions to plot the Lorenz curve and the reliability
999+
# diagram. Call the function `collect_cv_predictions` to collect the predictions and
1000+
# then call the `plot_lorenz_curve` and `plot_reliability_diagram` functions to plot
1001+
# the results.
1002+
1003+
# %%
1004+
# Write your code here.
1005+
#
1006+
#
1007+
#
1008+
#
1009+
#
1010+
#
1011+
#
1012+
#
1013+
#
1014+
#
1015+
#
9031016

9041017
# %%
9051018
cv_predictions_ridge = collect_cv_predictions(
@@ -914,6 +1027,28 @@ def splitter(X, y, index_generator):
9141027
title="Reliability diagram from cross-validation predictions"
9151028
)
9161029

1030+
# %% [markdown]
1031+
#
1032+
# Now, you can perform a randomized search on the hyper-parameters of the model.
1033+
# Use the `ts_cv_2` splitter defined earlier. This search is quite computationally
1034+
# expensive, so feel free to reduce the number of iterations but doing at least 100
1035+
# iterations is nice to have an overview of the impact of the hyper-parameters.
1036+
# Use `plot_results` to show the parallel coordinates plot of the results.
1037+
1038+
# %%
1039+
# Write your code here.
1040+
#
1041+
#
1042+
#
1043+
#
1044+
#
1045+
#
1046+
#
1047+
#
1048+
#
1049+
#
1050+
#
1051+
9171052
# %%
9181053
randomized_search_ridge = predictions_ridge.skb.get_randomized_search(
9191054
cv=ts_cv_2,
@@ -955,6 +1090,10 @@ def splitter(X, y, index_generator):
9551090
# %%
9561091
# nested_cv_results_ridge.round(3)
9571092

1093+
# %% [markdown]
1094+
#
1095+
# ## Predicting multiple horizons with a multi-output model
1096+
9581097
# %%
9591098
from sklearn.multioutput import MultiOutputRegressor
9601099

@@ -1202,19 +1341,19 @@ def scoring(regressor, X, y):
12021341
)
12031342

12041343
# %%
1205-
cv_results_hgbr_05[[col for col in cv_results_hgbr_05.columns if col.startswith("test_")]].mean(
1206-
axis=0
1207-
).round(3)
1344+
cv_results_hgbr_05[
1345+
[col for col in cv_results_hgbr_05.columns if col.startswith("test_")]
1346+
].mean(axis=0).round(3)
12081347

12091348
# %%
1210-
cv_results_hgbr_50[[col for col in cv_results_hgbr_50.columns if col.startswith("test_")]].mean(
1211-
axis=0
1212-
).round(3)
1349+
cv_results_hgbr_50[
1350+
[col for col in cv_results_hgbr_50.columns if col.startswith("test_")]
1351+
].mean(axis=0).round(3)
12131352

12141353
# %%
1215-
cv_results_hgbr_95[[col for col in cv_results_hgbr_95.columns if col.startswith("test_")]].mean(
1216-
axis=0
1217-
).round(3)
1354+
cv_results_hgbr_95[
1355+
[col for col in cv_results_hgbr_95.columns if col.startswith("test_")]
1356+
].mean(axis=0).round(3)
12181357

12191358
# %%
12201359
results = pl.concat(

0 commit comments

Comments
 (0)