.. DO NOT EDIT. .. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. .. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: .. "auto_examples/02_data_ops/1120_multiple_tables.py" .. LINE NUMBERS ARE GIVEN BELOW. .. only:: html .. note:: :class: sphx-glr-download-link-note :ref:`Go to the end ` to download the full example code or to run this example in your browser via JupyterLite or Binder. .. rst-class:: sphx-glr-example-title .. _sphx_glr_auto_examples_02_data_ops_1120_multiple_tables.py: Multiples tables: building machine learning pipelines with DataOps ================================================================== In this example, we show how to build a DataOps plan to handle pre-processing, validation and hyperparameter tuning of a dataset with **multiple tables**. We consider the credit fraud dataset, which contains two tables: one for baskets (orders) and one for products. The goal is to predict whether a basket (a single order that has been placed with the website) is fraudulent or not, based on the products it contains. .. currentmodule:: skrub .. |choose_from| replace:: :func:`skrub.choose_from` .. |choose_int| replace:: :func:`skrub.choose_int` .. |choose_float| replace:: :func:`skrub.choose_float` .. |MinHashEncoder| replace:: :class:`~skrub.MinHashEncoder` .. |StringEncoder| replace:: :class:`~skrub.StringEncoder` .. |TableVectorizer| replace:: :class:`~skrub.TableVectorizer` .. |var| replace:: :func:`skrub.var` .. |TableReport| replace:: :class:`~skrub.TableReport` .. |HistGradientBoostingClassifier| replace:: :class:`~sklearn.ensemble.HistGradientBoostingClassifier` .. |make_randomized_search| replace:: :func:`~skrub.DataOp.skb.make_randomized_search` .. |RocCurveDisplay| replace:: :class:`~sklearn.metrics.RocCurveDisplay` .. GENERATED FROM PYTHON SOURCE LINES 33-40 The credit fraud dataset ------------------------ We fetch the credit fraud dataset using ``fetch_credit_fraud``. This dataset contains two tables: ``baskets`` and ``products``. We load the training split of the dataset to train the model. At the end of the example, we will load the test split to evaluate the model on unseen data. .. GENERATED FROM PYTHON SOURCE LINES 42-53 .. code-block:: Python import pandas as pd import skrub import skrub.datasets # Small display detail: open the graphs by default in the visualizations shown # in the rest of this notebook. skrub.set_config(data_ops_open_graph_dropdown=True) dataset = skrub.datasets.fetch_credit_fraud(split="train") .. GENERATED FROM PYTHON SOURCE LINES 54-59 We define two skrub variables that store the content of the two csv files. These variables will be used as inputs to the DataOps plan we will build. Later, when we want to apply the resulting model to new data, we will need to provide dataframes to the same variables, but with the content of the test split of the dataset instead. .. GENERATED FROM PYTHON SOURCE LINES 59-62 .. code-block:: Python baskets = skrub.var("baskets", pd.read_csv(dataset.baskets_path)) products = skrub.var("products", pd.read_csv(dataset.products_path)) .. GENERATED FROM PYTHON SOURCE LINES 63-66 Now we can use the |TableReport| provided by the Data Ops to inspect the two tables. The ``baskets`` table contains the list of basket IDs, and a fraud flag indicating whether the basket is fraudulent or not. .. GENERATED FROM PYTHON SOURCE LINES 66-67 .. code-block:: Python baskets .. raw:: html
<Var 'baskets'>
Show/Hide graph Var 'baskets'

Result:

Please enable javascript

The skrub table reports need javascript to display correctly. If you are displaying a report in a Jupyter notebook and you see this message, you may need to re-execute the cell or to trust the notebook (button on the top right or "File > Trust notebook").



.. GENERATED FROM PYTHON SOURCE LINES 68-72 We mark the "ID" column of the ``baskets`` table as ``X``, and the ``"fraud_flag"`` column as ``y``. This allows the Data Ops to track the indices of the variables when splitting for cross-validation. so that DataOps can use their indices for train-test splitting and cross-validation. .. GENERATED FROM PYTHON SOURCE LINES 72-74 .. code-block:: Python basket_ids = baskets[["ID"]].skb.mark_as_X() fraud_flags = baskets["fraud_flag"].skb.mark_as_y() .. GENERATED FROM PYTHON SOURCE LINES 75-79 The ``products`` table contains information about the products that have been purchased, and the basket they belong to. A basket contains at least one product. Products can be associated with the corresponding basket through the "basket_ID" column. .. GENERATED FROM PYTHON SOURCE LINES 81-82 .. code-block:: Python products .. raw:: html
<Var 'products'>
Show/Hide graph Var 'products'

Result:

Please enable javascript

The skrub table reports need javascript to display correctly. If you are displaying a report in a Jupyter notebook and you see this message, you may need to re-execute the cell or to trust the notebook (button on the top right or "File > Trust notebook").



.. GENERATED FROM PYTHON SOURCE LINES 83-112 A data-processing challenge ---------------------------- The general structure of the DataOps plan we want to build looks like this: .. image:: ../../_static/credit_fraud_diagram.svg :width: 300 We want to fit a |HistGradientBoostingClassifier| to predict the fraud flag (y). However, since the features for each basket are stored in the products table, we need to extract these features, aggregate them at the basket level, and merge the result with the basket data. .. admonition:: Why building a pipeline for this is hard :collapsible: closed We can use the |TableVectorizer| to vectorize the products, but we then need to aggregate the resulting vectors to obtain a single row per basket. Using a scikit-learn Pipeline is tricky because the |TableVectorizer| would be fitted on a table with a different number of rows than the target y (the baskets table), which scikit-learn does not allow. While we could fit the |TableVectorizer| manually, this would forfeit scikit-learn’s tooling for managing transformations, storing fitted estimators, splitting data, cross-validation, and hyper-parameter tuning. We would also have to handle the aggregation and join ourselves, likely with error-prone Pandas code. Fortunately, skrub DataOps provide a powerful alternative for building flexible plans that address these problems. .. GENERATED FROM PYTHON SOURCE LINES 114-122 Building a multi-table DataOps plan ------------------------------------ Since our DataOps expect dataframes for products, baskets and fraud flags, we manipulate those objects as we would manipulate pandas dataframes. For instance, we filter products to keep only those that match one of the baskets in the ``baskets`` table, and then add a column containing the total amount for each kind of product in a basket: %% .. GENERATED FROM PYTHON SOURCE LINES 122-128 .. code-block:: Python kept_products = products[products["basket_ID"].isin(basket_ids["ID"])] products_with_total = kept_products.assign( total_price=kept_products["Nbr_of_prod_purchas"] * kept_products["cash_price"] ) products_with_total .. raw:: html
<CallMethod 'assign'>
Show/Hide graph Var 'products' GetItem 'basket_ID' GetItem <CallMethod 'isin'> CallMethod 'isin' Var 'baskets' X: GetItem ['ID'] GetItem 'ID' GetItem 'Nbr_of_prod_purchas' GetItem 'cash_price' CallMethod 'assign' BinOp: mul

Result:

Please enable javascript

The skrub table reports need javascript to display correctly. If you are displaying a report in a Jupyter notebook and you see this message, you may need to re-execute the cell or to trust the notebook (button on the top right or "File > Trust notebook").



.. GENERATED FROM PYTHON SOURCE LINES 129-140 We then build a skrub |TableVectorizer| with different choices of the type of encoder for high-cardinality categorical or string columns, and the number of components it uses. With skrub, there’s no need to specify a separate grid of hyperparameters outside the pipeline. Instead, within a DataOps plan, we can directly replace a parameter’s value using one of skrub’s ``choose_*`` functions, which define the range of values to consider during hyperparameter selection. In this example, we use |choose_int| to select the number of components for the encoder and |choose_from| to select the type of encoder. .. GENERATED FROM PYTHON SOURCE LINES 142-152 .. code-block:: Python n = skrub.choose_int(5, 15, name="n_components") encoder = skrub.choose_from( { "MinHash": skrub.MinHashEncoder(n_components=n), "LSA": skrub.StringEncoder(n_components=n), }, name="encoder", ) vectorizer = skrub.TableVectorizer(high_cardinality=encoder) .. GENERATED FROM PYTHON SOURCE LINES 153-156 We can restrict the vectorizer to a subset of columns: in our case, we want to vectorize all columns except the ``"basket_ID"`` column, which is not a feature but a link to the basket it belongs to. .. GENERATED FROM PYTHON SOURCE LINES 158-162 .. code-block:: Python vectorized_products = products_with_total.skb.apply( vectorizer, exclude_cols="basket_ID" ) .. GENERATED FROM PYTHON SOURCE LINES 163-165 We then aggregate the vectorized products by basket ID, and then merge the result with the baskets table. .. GENERATED FROM PYTHON SOURCE LINES 167-172 .. code-block:: Python aggregated_products = vectorized_products.groupby("basket_ID").agg("mean").reset_index() augmented_baskets = basket_ids.merge( aggregated_products, left_on="ID", right_on="basket_ID" ).drop(columns=["ID", "basket_ID"]) .. GENERATED FROM PYTHON SOURCE LINES 173-175 Finally, we add a supervised estimator, and use |choose_float| to add the learning rate as a hyperparameter to tune. .. GENERATED FROM PYTHON SOURCE LINES 177-185 .. code-block:: Python from sklearn.ensemble import HistGradientBoostingClassifier hgb = HistGradientBoostingClassifier( learning_rate=skrub.choose_float(0.01, 0.9, log=True, name="learning_rate") ) predictions = augmented_baskets.skb.apply(hgb, y=fraud_flags) predictions .. raw:: html
<Apply HistGradientBoostingClassifier>
Show/Hide graph Var 'baskets' X: GetItem ['ID'] y: GetItem 'fraud_flag' GetItem 'ID' CallMethod 'merge' Var 'products' GetItem 'basket_ID' GetItem <CallMethod 'isin'> CallMethod 'isin' GetItem 'Nbr_of_prod_purchas' GetItem 'cash_price' CallMethod 'assign' BinOp: mul Apply TableVectorizer CallMethod 'groupby' CallMethod 'agg' CallMethod 'reset_index' CallMethod 'drop' Apply HistGradientBoostingClassifier

Result:

Please enable javascript

The skrub table reports need javascript to display correctly. If you are displaying a report in a Jupyter notebook and you see this message, you may need to re-execute the cell or to trust the notebook (button on the top right or "File > Trust notebook").



.. GENERATED FROM PYTHON SOURCE LINES 186-191 And our DataOps plan is complete! We can now use |make_randomized_search| to perform hyperparameter tuning and find the best hyperparameters for our model. Below, we display the hyperparameter combinations that define our search space. .. GENERATED FROM PYTHON SOURCE LINES 193-195 .. code-block:: Python print(predictions.skb.describe_param_grid()) .. rst-class:: sphx-glr-script-out .. code-block:: none - learning_rate: choose_float(0.01, 0.9, log=True, name='learning_rate') encoder: 'MinHash' n_components: choose_int(5, 15, name='n_components') - learning_rate: choose_float(0.01, 0.9, log=True, name='learning_rate') encoder: 'LSA' n_components: choose_int(5, 15, name='n_components') .. GENERATED FROM PYTHON SOURCE LINES 196-198 |make_randomized_search| returns a :class:`~skrub.ParamSearch` object, which contains our search result and some plotting logic. .. GENERATED FROM PYTHON SOURCE LINES 198-203 .. code-block:: Python search = predictions.skb.make_randomized_search( scoring="roc_auc", n_iter=8, n_jobs=4, random_state=0, fitted=True ) search.results_ .. raw:: html

Please enable javascript

The skrub table reports need javascript to display correctly. If you are displaying a report in a Jupyter notebook and you see this message, you may need to re-execute the cell or to trust the notebook (button on the top right or "File > Trust notebook").



.. GENERATED FROM PYTHON SOURCE LINES 204-205 We can also display the results of the search in a parallel coordinates plot: .. GENERATED FROM PYTHON SOURCE LINES 205-207 .. code-block:: Python search.plot_results() .. raw:: html


.. GENERATED FROM PYTHON SOURCE LINES 208-215 It seems here that using the LSA as an encoder brings better test scores, but at the expense of training and scoring time. We can get the best performing :class:`~skrub.SkrubLearner` via ``best_learner_``, and use it for inference on new data. We load the test split of the credit fraud dataset, and apply the best learner to it to obtain predictions. .. GENERATED FROM PYTHON SOURCE LINES 215-224 .. code-block:: Python new_data = skrub.datasets.fetch_credit_fraud(split="test") new_baskets = pd.read_csv(new_data.baskets_path) new_products = pd.read_csv(new_data.products_path) probabilities = search.best_learner_.predict_proba( {"baskets": new_baskets, "products": new_products} ) .. GENERATED FROM PYTHON SOURCE LINES 225-228 We can evaluate the performance of our model by plotting the ROC curve and calculating the AUC score. We can use the |RocCurveDisplay| from scikit-learn to plot the ROC curve. .. GENERATED FROM PYTHON SOURCE LINES 228-234 .. code-block:: Python import matplotlib.pyplot as plt from sklearn.metrics import RocCurveDisplay RocCurveDisplay.from_predictions(new_baskets["fraud_flag"], probabilities[:, 1]) plt.show() .. image-sg:: /auto_examples/02_data_ops/images/sphx_glr_1120_multiple_tables_001.png :alt: 1120 multiple tables :srcset: /auto_examples/02_data_ops/images/sphx_glr_1120_multiple_tables_001.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 235-247 Conclusion ---------- In this example, we have shown how to build a multi-table machine learning pipeline with skrub DataOps. We have seen how DataOps allow us to use familiar Pandas operations to manipulate dataframes, and how we can build a DataOps plan that works with multiple tables and performs hyperparameter tuning on the resulting pipeline. If you want to learn more about tuning hyperparameters using skrub DataOps, see the :ref:`Tuning Pipelines example ` for an in-depth tutorial. .. rst-class:: sphx-glr-timing **Total running time of the script:** (2 minutes 43.078 seconds) **Estimated memory usage:** 860 MB .. _sphx_glr_download_auto_examples_02_data_ops_1120_multiple_tables.py: .. only:: html .. container:: sphx-glr-footer sphx-glr-footer-example .. container:: binder-badge .. image:: images/binder_badge_logo.svg :target: https://mybinder.org/v2/gh/skrub-data/skrub/0.10.0?urlpath=lab/tree/notebooks/auto_examples/02_data_ops/1120_multiple_tables.ipynb :alt: Launch binder :width: 150 px .. container:: lite-badge .. image:: images/jupyterlite_badge_logo.svg :target: ../../lite/lab/index.html?path=auto_examples/02_data_ops/1120_multiple_tables.ipynb :alt: Launch JupyterLite :width: 150 px .. container:: sphx-glr-download sphx-glr-download-jupyter :download:`Download Jupyter notebook: 1120_multiple_tables.ipynb <1120_multiple_tables.ipynb>` .. container:: sphx-glr-download sphx-glr-download-python :download:`Download Python source code: 1120_multiple_tables.py <1120_multiple_tables.py>` .. container:: sphx-glr-download sphx-glr-download-zip :download:`Download zipped: 1120_multiple_tables.zip <1120_multiple_tables.zip>` .. only:: html .. rst-class:: sphx-glr-signature `Gallery generated by Sphinx-Gallery `_