{ "cells": [ { "cell_type": "markdown", "metadata": { "toc": true }, "source": [ "

Table of Contents

\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Google colab link:\n", "\n", "[https://colab.research.google.com/github/oegedijk/explainerdashboard/blob/master/dashboard_examples.ipynb](https://colab.research.google.com/github/oegedijk/explainerdashboard/blob/master/dashboard_examples.ipynb)" ] }, { "cell_type": "markdown", "metadata": { "ExecuteTime": { "end_time": "2020-10-01T09:06:26.294359Z", "start_time": "2020-10-01T09:06:26.289751Z" } }, "source": [ "Uncomment to install explainerdashboard:" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "ExecuteTime": { "end_time": "2021-01-20T16:13:22.406016Z", "start_time": "2021-01-20T16:13:22.401826Z" } }, "outputs": [], "source": [ "#!pip install explainerdashboard" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Set notebook properties:" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "ExecuteTime": { "end_time": "2021-01-20T16:13:23.384613Z", "start_time": "2021-01-20T16:13:23.380671Z" } }, "outputs": [], "source": [ "from IPython.core.interactiveshell import InteractiveShell\n", "\n", "InteractiveShell.ast_node_interactivity = \"all\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# ClassifierExplainer examples" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Load classifier data:\n", " - predicting probability that a person on the titanic survived" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "ExecuteTime": { "end_time": "2021-01-20T16:13:28.231562Z", "start_time": "2021-01-20T16:13:24.550567Z" } }, "outputs": [], "source": [ "from explainerdashboard.datasets import titanic_survive, titanic_names" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "ExecuteTime": { "end_time": "2021-01-20T16:13:28.246402Z", "start_time": "2021-01-20T16:13:28.234402Z" } }, "outputs": [], "source": [ "X_train, y_train, X_test, y_test = titanic_survive()\n", "train_names, test_names = titanic_names()" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "ExecuteTime": { "end_time": "2021-01-20T16:13:28.282574Z", "start_time": "2021-01-20T16:13:28.250686Z" } }, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
FareAgePassengerClassNo_of_siblings_plus_spouses_on_boardNo_of_parents_plus_children_on_boardSex_femaleSex_maleSex_nanDeck_ADeck_B...Deck_DDeck_EDeck_FDeck_GDeck_TDeck_UnkownEmbarked_CherbourgEmbarked_QueenstownEmbarked_SouthamptonEmbarked_Unknown
Passenger
Braund, Mr. Owen Harris7.250022.031001000...0000010010
Heikkinen, Miss. Laina7.925026.030010000...0000010010
Allen, Mr. William Henry8.050035.030001000...0000010010
Moran, Mr. James8.4583-999.030001000...0000010100
McCarthy, Mr. Timothy J51.862554.010001000...0100000010
\n", "

5 rows × 21 columns

\n", "
" ], "text/plain": [ " Fare Age PassengerClass \\\n", "Passenger \n", "Braund, Mr. Owen Harris 7.2500 22.0 3 \n", "Heikkinen, Miss. Laina 7.9250 26.0 3 \n", "Allen, Mr. William Henry 8.0500 35.0 3 \n", "Moran, Mr. James 8.4583 -999.0 3 \n", "McCarthy, Mr. Timothy J 51.8625 54.0 1 \n", "\n", " No_of_siblings_plus_spouses_on_board \\\n", "Passenger \n", "Braund, Mr. Owen Harris 1 \n", "Heikkinen, Miss. Laina 0 \n", "Allen, Mr. William Henry 0 \n", "Moran, Mr. James 0 \n", "McCarthy, Mr. Timothy J 0 \n", "\n", " No_of_parents_plus_children_on_board Sex_female \\\n", "Passenger \n", "Braund, Mr. Owen Harris 0 0 \n", "Heikkinen, Miss. Laina 0 1 \n", "Allen, Mr. William Henry 0 0 \n", "Moran, Mr. James 0 0 \n", "McCarthy, Mr. Timothy J 0 0 \n", "\n", " Sex_male Sex_nan Deck_A Deck_B ... Deck_D \\\n", "Passenger ... \n", "Braund, Mr. Owen Harris 1 0 0 0 ... 0 \n", "Heikkinen, Miss. Laina 0 0 0 0 ... 0 \n", "Allen, Mr. William Henry 1 0 0 0 ... 0 \n", "Moran, Mr. James 1 0 0 0 ... 0 \n", "McCarthy, Mr. Timothy J 1 0 0 0 ... 0 \n", "\n", " Deck_E Deck_F Deck_G Deck_T Deck_Unkown \\\n", "Passenger \n", "Braund, Mr. Owen Harris 0 0 0 0 1 \n", "Heikkinen, Miss. Laina 0 0 0 0 1 \n", "Allen, Mr. William Henry 0 0 0 0 1 \n", "Moran, Mr. James 0 0 0 0 1 \n", "McCarthy, Mr. Timothy J 1 0 0 0 0 \n", "\n", " Embarked_Cherbourg Embarked_Queenstown \\\n", "Passenger \n", "Braund, Mr. Owen Harris 0 0 \n", "Heikkinen, Miss. Laina 0 0 \n", "Allen, Mr. William Henry 0 0 \n", "Moran, Mr. James 0 1 \n", "McCarthy, Mr. Timothy J 0 0 \n", "\n", " Embarked_Southampton Embarked_Unknown \n", "Passenger \n", "Braund, Mr. Owen Harris 1 0 \n", "Heikkinen, Miss. Laina 1 0 \n", "Allen, Mr. William Henry 1 0 \n", "Moran, Mr. James 0 0 \n", "McCarthy, Mr. Timothy J 1 0 \n", "\n", "[5 rows x 21 columns]" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "X_train.head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We'll use the passenger names later as idxs for the Explainer, such that they get displayed on the contributions tab of the dashboard, and you can also use them to pass as an index into various methods:" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "ExecuteTime": { "end_time": "2021-01-20T16:13:28.287941Z", "start_time": "2021-01-20T16:13:28.284599Z" } }, "outputs": [ { "data": { "text/plain": [ "['Braund, Mr. Owen Harris',\n", " 'Heikkinen, Miss. Laina',\n", " 'Allen, Mr. William Henry',\n", " 'Moran, Mr. James',\n", " 'McCarthy, Mr. Timothy J']" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "train_names[:5]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## One line example:\n", "- click on the link (http://localhost:8050) to go to the dashboard\n", "- Interrupt the kernel to stop the dashboard" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "ExecuteTime": { "end_time": "2021-01-20T16:13:28.292151Z", "start_time": "2021-01-20T16:13:28.289481Z" } }, "outputs": [], "source": [ "from sklearn.ensemble import RandomForestClassifier\n", "from explainerdashboard import ClassifierExplainer, ExplainerDashboard" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "ExecuteTime": { "end_time": "2021-01-20T16:14:02.984253Z", "start_time": "2021-01-20T16:13:28.549231Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Note: shap=='guess' so guessing for RandomForestClassifier shap='tree'...\n", "Detected RandomForestClassifier model: Changing class type to RandomForestClassifierExplainer...\n", "Note: model_output=='probability', so assuming that raw shap output of RandomForestClassifier is in probability space...\n", "Generating self.shap_explainer = shap.TreeExplainer(model)\n", "Building ExplainerDashboard..\n", "Detected notebook environment, consider setting mode='external', mode='inline' or mode='jupyterlab' to keep the notebook interactive while the dashboard is running...\n", "Warning: calculating shap interaction values can be slow! Pass shap_interaction=False to remove interactions tab.\n", "Generating layout...\n", "Calculating shap values...\n", "Calculating dependencies...\n", "Calculating permutation importances (if slow, try setting n_jobs parameter)...\n", "Calculating predictions...\n", "Calculating pred_percentiles...\n", "Calculating prediction probabilities...\n", "Calculating shap interaction values...\n", "Reminder: TreeShap computational complexity is O(TLD^2), where T is the number of trees, L is the maximum number of leaves in any tree and D the maximal depth of any tree. So reducing these will speed up the calculation.\n", "Reminder: you can store the explainer (including calculated dependencies) with explainer.dump('explainer.joblib') and reload with e.g. ClassifierExplainer.from_file('explainer.joblib')\n", "Registering callbacks...\n", "Starting ExplainerDashboard on http://localhost:8050\n", "Dash is running on http://0.0.0.0:8050/\n", "\n", " * Serving Flask app \"explainerdashboard.dashboards\" (lazy loading)\n", " * Environment: production\n", "\u001b[31m WARNING: This is a development server. Do not use it in a production deployment.\u001b[0m\n", "\u001b[2m Use a production WSGI server instead.\u001b[0m\n", " * Debug mode: off\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ " * Running on http://0.0.0.0:8050/ (Press CTRL+C to quit)\n", "127.0.0.1 - - [20/Jan/2021 17:13:58] \"\u001b[37mGET / HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [20/Jan/2021 17:13:58] \"\u001b[37mGET /_dash-component-suites/dash_core_components/dash_core_components-shared.v1_13_0m1604001906.js HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [20/Jan/2021 17:13:58] \"\u001b[37mGET /_dash-component-suites/dash_table/bundle.v4_11_0m1604001256.js HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [20/Jan/2021 17:13:58] \"\u001b[37mGET /_dash-component-suites/dash_core_components/dash_core_components.v1_13_0m1604001906.min.js HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [20/Jan/2021 17:13:58] \"\u001b[37mGET /_dash-component-suites/dash_bootstrap_components/_components/dash_bootstrap_components.v0_10_6m1600683264.min.js HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [20/Jan/2021 17:13:58] \"\u001b[37mGET /_dash-dependencies HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [20/Jan/2021 17:13:58] \"\u001b[37mGET /_dash-layout HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [20/Jan/2021 17:13:58] \"\u001b[37mGET /_dash-component-suites/dash_core_components/async-dropdown.v1_13_0m1604001894.js HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [20/Jan/2021 17:13:58] \"\u001b[37mGET /_dash-component-suites/dash_core_components/async-graph.v1_13_0m1604001894.js HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [20/Jan/2021 17:13:58] \"\u001b[37mPOST /_dash-update-component HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [20/Jan/2021 17:13:58] \"\u001b[37mPOST /_dash-update-component HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [20/Jan/2021 17:13:58] \"\u001b[37mPOST /_dash-update-component HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [20/Jan/2021 17:13:58] \"\u001b[37mPOST /_dash-update-component HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [20/Jan/2021 17:13:58] \"\u001b[37mPOST /_dash-update-component HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [20/Jan/2021 17:13:58] \"\u001b[37mPOST /_dash-update-component HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [20/Jan/2021 17:13:58] \"\u001b[37mPOST /_dash-update-component HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [20/Jan/2021 17:13:58] \"\u001b[37mPOST /_dash-update-component HTTP/1.1\u001b[0m\" 204 -\n", "127.0.0.1 - - [20/Jan/2021 17:13:58] \"\u001b[37mPOST /_dash-update-component HTTP/1.1\u001b[0m\" 204 -\n", "127.0.0.1 - - [20/Jan/2021 17:13:58] \"\u001b[37mPOST /_dash-update-component HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [20/Jan/2021 17:13:58] \"\u001b[37mGET /_dash-component-suites/dash_core_components/async-plotlyjs.v1_13_0m1604001894.js HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [20/Jan/2021 17:13:58] \"\u001b[37mPOST /_dash-update-component HTTP/1.1\u001b[0m\" 204 -\n", "127.0.0.1 - - [20/Jan/2021 17:13:58] \"\u001b[37mPOST /_dash-update-component HTTP/1.1\u001b[0m\" 204 -\n", "127.0.0.1 - - [20/Jan/2021 17:13:58] \"\u001b[37mPOST /_dash-update-component HTTP/1.1\u001b[0m\" 204 -\n", "127.0.0.1 - - [20/Jan/2021 17:13:59] \"\u001b[37mPOST /_dash-update-component HTTP/1.1\u001b[0m\" 204 -\n", "127.0.0.1 - - [20/Jan/2021 17:13:59] \"\u001b[37mPOST /_dash-update-component HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [20/Jan/2021 17:13:59] \"\u001b[37mPOST /_dash-update-component HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [20/Jan/2021 17:13:59] \"\u001b[37mPOST /_dash-update-component HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [20/Jan/2021 17:13:59] \"\u001b[37mPOST /_dash-update-component HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [20/Jan/2021 17:13:59] \"\u001b[37mPOST /_dash-update-component HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [20/Jan/2021 17:13:59] \"\u001b[37mPOST /_dash-update-component HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [20/Jan/2021 17:13:59] \"\u001b[37mPOST /_dash-update-component HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [20/Jan/2021 17:13:59] \"\u001b[37mPOST /_dash-update-component HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [20/Jan/2021 17:13:59] \"\u001b[37mPOST /_dash-update-component HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [20/Jan/2021 17:13:59] \"\u001b[37mPOST /_dash-update-component HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [20/Jan/2021 17:13:59] \"\u001b[37mPOST /_dash-update-component HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [20/Jan/2021 17:13:59] \"\u001b[37mPOST /_dash-update-component HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [20/Jan/2021 17:13:59] \"\u001b[37mPOST /_dash-update-component HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [20/Jan/2021 17:13:59] \"\u001b[37mPOST /_dash-update-component HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [20/Jan/2021 17:14:00] \"\u001b[37mPOST /_dash-update-component HTTP/1.1\u001b[0m\" 204 -\n", "127.0.0.1 - - [20/Jan/2021 17:14:00] \"\u001b[37mPOST /_dash-update-component HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [20/Jan/2021 17:14:00] \"\u001b[37mPOST /_dash-update-component HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [20/Jan/2021 17:14:00] \"\u001b[37mPOST /_dash-update-component HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [20/Jan/2021 17:14:00] \"\u001b[37mPOST /_dash-update-component HTTP/1.1\u001b[0m\" 200 -\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Exception on /_dash-update-component [POST]\n", "Traceback (most recent call last):\n", " File \"/Users/oege/projects/explainerdashboard/venv/lib/python3.8/site-packages/flask/app.py\", line 2447, in wsgi_app\n", " response = self.full_dispatch_request()\n", " File \"/Users/oege/projects/explainerdashboard/venv/lib/python3.8/site-packages/flask/app.py\", line 1952, in full_dispatch_request\n", " rv = self.handle_user_exception(e)\n", " File \"/Users/oege/projects/explainerdashboard/venv/lib/python3.8/site-packages/flask/app.py\", line 1821, in handle_user_exception\n", " reraise(exc_type, exc_value, tb)\n", " File \"/Users/oege/projects/explainerdashboard/venv/lib/python3.8/site-packages/flask/_compat.py\", line 39, in reraise\n", " raise value\n", " File \"/Users/oege/projects/explainerdashboard/venv/lib/python3.8/site-packages/flask/app.py\", line 1950, in full_dispatch_request\n", " rv = self.dispatch_request()\n", " File \"/Users/oege/projects/explainerdashboard/venv/lib/python3.8/site-packages/flask/app.py\", line 1936, in dispatch_request\n", " return self.view_functions[rule.endpoint](**req.view_args)\n", " File \"/Users/oege/projects/explainerdashboard/venv/lib/python3.8/site-packages/dash/dash.py\", line 1076, in dispatch\n", " response.set_data(func(*args, outputs_list=outputs_list))\n", " File \"/Users/oege/projects/explainerdashboard/venv/lib/python3.8/site-packages/dash/dash.py\", line 1007, in add_context\n", " output_value = func(*args, **kwargs) # %% callback invoked %%\n", " File \"/Users/oege/projects/explainerdashboard/explainerdashboard/dashboard_components/classifier_components.py\", line 351, in update_output_div\n", " preds_df = self.explainer.prediction_result_df(index, round=self.round, logodds=True)\n", " File \"/Users/oege/projects/explainerdashboard/explainerdashboard/explainers.py\", line 2069, in prediction_result_df\n", " preds_df.probability.apply(lambda p: np.log(p / (1-p)))\n", " File \"/Users/oege/projects/explainerdashboard/venv/lib/python3.8/site-packages/pandas/core/series.py\", line 4108, in apply\n", " mapped = lib.map_infer(values, f, convert=convert_dtype)\n", " File \"pandas/_libs/lib.pyx\", line 2467, in pandas._libs.lib.map_infer\n", " File \"/Users/oege/projects/explainerdashboard/explainerdashboard/explainers.py\", line 2069, in \n", " preds_df.probability.apply(lambda p: np.log(p / (1-p)))\n", "ZeroDivisionError: float division by zero\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "127.0.0.1 - - [20/Jan/2021 17:14:00] \"\u001b[35m\u001b[1mPOST /_dash-update-component HTTP/1.1\u001b[0m\" 500 -\n", "127.0.0.1 - - [20/Jan/2021 17:14:00] \"\u001b[37mPOST /_dash-update-component HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [20/Jan/2021 17:14:00] \"\u001b[37mPOST /_dash-update-component HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [20/Jan/2021 17:14:00] \"\u001b[37mPOST /_dash-update-component HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [20/Jan/2021 17:14:00] \"\u001b[37mPOST /_dash-update-component HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [20/Jan/2021 17:14:00] \"\u001b[37mPOST /_dash-update-component HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [20/Jan/2021 17:14:00] \"\u001b[37mPOST /_dash-update-component HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [20/Jan/2021 17:14:00] \"\u001b[37mPOST /_dash-update-component HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [20/Jan/2021 17:14:00] \"\u001b[37mPOST /_dash-update-component HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [20/Jan/2021 17:14:00] \"\u001b[37mPOST /_dash-update-component HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [20/Jan/2021 17:14:00] \"\u001b[37mPOST /_dash-update-component HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [20/Jan/2021 17:14:01] \"\u001b[37mPOST /_dash-update-component HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [20/Jan/2021 17:14:01] \"\u001b[37mPOST /_dash-update-component HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [20/Jan/2021 17:14:01] \"\u001b[37mPOST /_dash-update-component HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [20/Jan/2021 17:14:01] \"\u001b[37mPOST /_dash-update-component HTTP/1.1\u001b[0m\" 200 -\n" ] } ], "source": [ "ExplainerDashboard(\n", " ClassifierExplainer(RandomForestClassifier().fit(X_train, y_train), X_test, y_test)\n", ").run()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Multi line example\n", "- create an explainer object out the model and the X and y that you wish to display.\n", "- the explainer object calculates shap values, permutation importances, pdp's, etc, and provides all kinds of plots that will be used by the ExplainerDashboard object" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "ExecuteTime": { "end_time": "2021-01-20T16:14:21.318672Z", "start_time": "2021-01-20T16:14:21.218883Z" } }, "outputs": [ { "data": { "text/plain": [ "RandomForestClassifier(max_depth=5, n_estimators=50)" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" }, { "name": "stdout", "output_type": "stream", "text": [ "Note: shap=='guess' so guessing for RandomForestClassifier shap='tree'...\n", "Detected RandomForestClassifier model: Changing class type to RandomForestClassifierExplainer...\n", "Note: model_output=='probability', so assuming that raw shap output of RandomForestClassifier is in probability space...\n", "Generating self.shap_explainer = shap.TreeExplainer(model)\n" ] } ], "source": [ "model = RandomForestClassifier(n_estimators=50, max_depth=5)\n", "model.fit(X_train, y_train)\n", "\n", "explainer = ClassifierExplainer(model, X_test, y_test)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now create an ExplainerDashboard instance out of the explainer instance:\n", "- depending on which tabs are included, all necessary calculations (shap values, importances, etc) get done up front" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "ExecuteTime": { "end_time": "2021-01-20T16:14:23.618940Z", "start_time": "2021-01-20T16:14:22.502651Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Building ExplainerDashboard..\n", "Detected notebook environment, consider setting mode='external', mode='inline' or mode='jupyterlab' to keep the notebook interactive while the dashboard is running...\n", "Warning: calculating shap interaction values can be slow! Pass shap_interaction=False to remove interactions tab.\n", "Generating layout...\n", "Calculating shap values...\n", "Calculating dependencies...\n", "Calculating permutation importances (if slow, try setting n_jobs parameter)...\n", "Calculating predictions...\n", "Calculating pred_percentiles...\n", "Calculating prediction probabilities...\n", "Calculating shap interaction values...\n", "Reminder: TreeShap computational complexity is O(TLD^2), where T is the number of trees, L is the maximum number of leaves in any tree and D the maximal depth of any tree. So reducing these will speed up the calculation.\n", "Reminder: you can store the explainer (including calculated dependencies) with explainer.dump('explainer.joblib') and reload with e.g. ClassifierExplainer.from_file('explainer.joblib')\n", "Registering callbacks...\n" ] } ], "source": [ "db = ExplainerDashboard(explainer)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And run the dashboard on the default port (=8050):" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "db.run()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Or on another port, e.g. port 8051:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Run on specific port" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "db.run(8051)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Switch on/off specific tabs (+add title)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "By default all the tabs that should work are displayed, exceptions:\n", "- shap_interaction tab is disabled when model doesn't support shap interaction values\n", " - e.g. linear models, or when calculating shap values in probability space for gradient boosting models\n", "- **Depending on your model and data calculating shap interaction values may be slow, so in that case switch off the interactions tab manually!**\n", "- decision_trees tab is disabled unless explainer is RandomForestClassifierExplainer or RandomForestRegressionExplainer\n", "\n", "- You can also manually switch tabs on or off with booleans, as shown below:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ExplainerDashboard(\n", " explainer,\n", " title=\"Titanic Explainer\",\n", " importances=False,\n", " model_summary=True,\n", " contributions=False,\n", " whatif=True,\n", " shap_dependence=False,\n", " shap_interaction=False,\n", " decision_trees=False,\n", ").run()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### cats, idxs, descriptions, labels\n", "\n", "You can make the dashboard a bit more user friendly by passing in some additional information about the variables in the model:\n", "\n", "- `cats`: If you have onehotencoded some variables, you get a lot of shap values for binary features that are either 0 or 1, which are hard to interpret as a whole. \n", " - However, given that shap values are additive, we can sum them up and give a single shap value for the onehotencoded variables! \n", " - Furthermore, we can use different types of default plots for categorical variables than continuous ones. \n", " - By passing a list of variables that have been encoded with `varname_category` `explainerdashboard` will allow you to group the cats and show appropriate plots\n", " - In our sample dataset this would be:\n", " - `Sex`: `Sex_female`, `Sex_male` \n", " - `Deck`: `Deck_A`, `Deck_B`, etc\n", " - `Embarked`: `Embarked_Southampton`, `Embarked_Cherbourg`, etc\n", "- `idxs`: You may have specific identifiers (names, customer id's, etc) for each row in your dataset.\n", " - If you pass these the the Explainer object, you can index using both the numerical index, e.g. `explainer.get_contrib_df(0)` for the first row, or using the identifier, e.g. `explainer.get_contrib_df(\"Braund, Mr. Owen Harris\")` \n", " - The proper name or id will also be displayed and searchable on the contributions tab\n", "- `descriptions`: a dictionary of descriptions for each variable.\n", " - In order to be explanatory, you often have to explain the meaning of the features themselves (especially if the naming is not obvious)\n", " - Passing the dict along to `descriptions` will show hover-over tooltips for the various variables in the dashboard\n", "- `target`: the name of the target variable to be displayed, e.g. `Survival` or `Fare`.\n", "- `labels`: The outcome variables for a classification problem are assumed to be encoded 0, 1 (, 2, 3, ...)\n", " - This is not very human readable, so you can pass a list of human readable labels" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "ExecuteTime": { "end_time": "2020-10-12T08:55:18.240677Z", "start_time": "2020-10-12T08:55:18.236782Z" } }, "outputs": [ { "data": { "text/plain": [ "{'Sex': 'Gender of passenger',\n", " 'Deck': 'The deck the passenger had their cabin on',\n", " 'PassengerClass': 'The class of the ticket: 1st, 2nd or 3rd class',\n", " 'Fare': 'The amount of money people paid',\n", " 'No_of_relatives_on_board': 'number of siblings, spouses, parents plus children on board',\n", " 'Embarked': 'the port where the passenger boarded the Titanic. Either Southampton, Cherbourg or Queenstown',\n", " 'Age': 'Age of the passenger',\n", " 'No_of_siblings_plus_spouses_on_board': 'The sum of the number of siblings plus the number of spouses on board',\n", " 'No_of_parents_plus_children_on_board': 'The sum of the number of parents plus the number of children on board'}" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from explainerdashboard.datasets import feature_descriptions\n", "\n", "feature_descriptions" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "explainer = ClassifierExplainer(\n", " model,\n", " X_test,\n", " y_test,\n", " cats=[\"Sex\", \"Deck\", \"Embarked\"], # makes it easy to group onehotencoded vars\n", " idxs=test_names, # names of passengers # index by name\n", " descriptions=feature_descriptions, # show feature descriptions in plots\n", " target=\"Survival\",\n", " labels=[\"Not survived\", \"Survived\"],\n", ") # show nice labels\n", "ExplainerDashboard(explainer).run()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### X_background, model_output and shap\n", "- `X_background`: \n", " - Some models like LogisticRegression (as well as certain gradienst boosting algorithms in probability space) need a background dataset to calculate shap values. These can be passed as `X_background`.\n", " - If you don't pass an `X_background`, Explainer uses X instead but gives off a warning.\n", "- `model_output`: \n", " - By default model_output for classifier is set to \"probability\", as this is more intuitively explainable to non data scientist stakeholders\n", " - However for certain models (e.g. XGBClassifier, LGBMCLassifier, CatBoostClassifier), need a background dataset X_background to calculate shap values in probability space, and are not able to calculate shap interaction values.\n", " - Therefore you can also pass `model_output='logodds'`, in which case shap values get calculated as logodds\n", "- `shap`:\n", " - By default `shap='guess'`, which means that the Explainer will try to guess based on the model what kind of shap explainer it needs: e.g. `shap.TreeExplainer(...)`, `shap.LinearExplainer(...)`, etc.\n", " - In case the guess fails or you'd like to override it, you can set it manually:\n", " - e.g. `shap='tree'`, `shap='linear'`, `shap='kernel'`, `shap='deep'` " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from sklearn.linear_model import LogisticRegression\n", "from explainerdashboard.datasets import titanic_survive\n", "\n", "model = LogisticRegression()\n", "model.fit(X_train, y_train)\n", "\n", "explainer = ClassifierExplainer(\n", " model,\n", " X_test,\n", " y_test,\n", " shap=\"linear\",\n", " X_background=X_train,\n", " model_output=\"logodds\",\n", " cats=[\"Sex\", \"Deck\", \"Embarked\"],\n", ")\n", "ExplainerDashboard(explainer).run()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# mode='inline', 'jupyterlab', 'external'" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## mode='inline'\n", "\n", "Besides the default `dash.Dash(__name__)` dashboard, plotly also released a package called `jupyter-dash` that makes it more convenient to deploy dashboards either inline in a notebook, or run from a notebook cell, while the rest of the notebook stays interactive. \n", "\n", "By passing an appropriate mode to the `ExplainerDashboard` constructur you can make use of `JupyterDash`. The three modes are:\n", "\n", "- `\"inline\"`, displays the dashboard in the output area underneath the current cell\n", "- `\"jupyterlab\"` displays the dashboard in a seperate pane in jupyterlab\n", "- `\"external\"` opens up a server that you can acces from a seperate browser tab\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note: although the default is `mode='dash'`, when a google Colab environment is detected, the default is `'external'`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ExplainerDashboard(explainer, mode=\"inline\").run()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ExplainerDashboard(explainer, mode=\"inline\", width=1200, height=1000).run()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ExplainerDashboard(explainer, mode=\"jupyterlab\").run()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ExplainerDashboard(explainer, mode=\"external\").run()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Display a single tab or simple dashboard\n", "If you just want to display a single tab, you can use pass that tab to ExplainerDashboard:" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "ExecuteTime": { "end_time": "2021-01-20T16:15:03.126731Z", "start_time": "2021-01-20T16:15:03.121764Z" } }, "outputs": [], "source": [ "from explainerdashboard.custom import ContributionsTab" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "ExecuteTime": { "end_time": "2021-01-20T16:15:12.083719Z", "start_time": "2021-01-20T16:15:05.264746Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Building ExplainerDashboard..\n", "Detected notebook environment, consider setting mode='external', mode='inline' or mode='jupyterlab' to keep the notebook interactive while the dashboard is running...\n", "Generating layout...\n", "Calculating dependencies...\n", "Reminder: you can store the explainer (including calculated dependencies) with explainer.dump('explainer.joblib') and reload with e.g. ClassifierExplainer.from_file('explainer.joblib')\n", "Registering callbacks...\n", "Starting ExplainerDashboard on http://localhost:8050\n", "Dash is running on http://0.0.0.0:8050/\n", "\n", "Dash is running on http://0.0.0.0:8050/\n", "\n", "Dash is running on http://0.0.0.0:8050/\n", "\n", " * Serving Flask app \"explainerdashboard.dashboards\" (lazy loading)\n", " * Environment: production\n", "\u001b[31m WARNING: This is a development server. Do not use it in a production deployment.\u001b[0m\n", "\u001b[2m Use a production WSGI server instead.\u001b[0m\n", " * Debug mode: off\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ " * Running on http://0.0.0.0:8050/ (Press CTRL+C to quit)\n", "127.0.0.1 - - [20/Jan/2021 17:15:07] \"\u001b[37mGET / HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [20/Jan/2021 17:15:07] \"\u001b[37mGET /_dash-dependencies HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [20/Jan/2021 17:15:07] \"\u001b[37mGET /_dash-layout HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [20/Jan/2021 17:15:07] \"\u001b[37mGET /_dash-component-suites/dash_core_components/async-slider.v1_13_0m1604001894.js HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [20/Jan/2021 17:15:07] \"\u001b[37mPOST /_dash-update-component HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [20/Jan/2021 17:15:07] \"\u001b[37mPOST /_dash-update-component HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [20/Jan/2021 17:15:07] \"\u001b[37mPOST /_dash-update-component HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [20/Jan/2021 17:15:08] \"\u001b[37mPOST /_dash-update-component HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [20/Jan/2021 17:15:08] \"\u001b[37mPOST /_dash-update-component HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [20/Jan/2021 17:15:08] \"\u001b[37mPOST /_dash-update-component HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [20/Jan/2021 17:15:08] \"\u001b[37mPOST /_dash-update-component HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [20/Jan/2021 17:15:08] \"\u001b[37mPOST /_dash-update-component HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [20/Jan/2021 17:15:08] \"\u001b[37mPOST /_dash-update-component HTTP/1.1\u001b[0m\" 200 -\n" ] } ], "source": [ "ExplainerDashboard(explainer, ContributionsTab).run()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Can also instantiate first:" ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "ExecuteTime": { "end_time": "2021-01-20T16:15:22.703034Z", "start_time": "2021-01-20T16:15:16.872769Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Building ExplainerDashboard..\n", "Detected notebook environment, consider setting mode='external', mode='inline' or mode='jupyterlab' to keep the notebook interactive while the dashboard is running...\n", "Generating layout...\n", "Calculating dependencies...\n", "Reminder: you can store the explainer (including calculated dependencies) with explainer.dump('explainer.joblib') and reload with e.g. ClassifierExplainer.from_file('explainer.joblib')\n", "Registering callbacks...\n", "Starting ExplainerDashboard on http://localhost:8050\n", "Dash is running on http://0.0.0.0:8050/\n", "\n", "Dash is running on http://0.0.0.0:8050/\n", "\n", "Dash is running on http://0.0.0.0:8050/\n", "\n", "Dash is running on http://0.0.0.0:8050/\n", "\n", " * Serving Flask app \"explainerdashboard.dashboards\" (lazy loading)\n", " * Environment: production\n", "\u001b[31m WARNING: This is a development server. Do not use it in a production deployment.\u001b[0m\n", "\u001b[2m Use a production WSGI server instead.\u001b[0m\n", " * Debug mode: off\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ " * Running on http://0.0.0.0:8050/ (Press CTRL+C to quit)\n", "127.0.0.1 - - [20/Jan/2021 17:15:18] \"\u001b[37mGET / HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [20/Jan/2021 17:15:18] \"\u001b[37mGET /_dash-dependencies HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [20/Jan/2021 17:15:18] \"\u001b[37mGET /_dash-layout HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [20/Jan/2021 17:15:19] \"\u001b[37mPOST /_dash-update-component HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [20/Jan/2021 17:15:19] \"\u001b[37mPOST /_dash-update-component HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [20/Jan/2021 17:15:19] \"\u001b[37mPOST /_dash-update-component HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [20/Jan/2021 17:15:19] \"\u001b[37mPOST /_dash-update-component HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [20/Jan/2021 17:15:19] \"\u001b[37mPOST /_dash-update-component HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [20/Jan/2021 17:15:19] \"\u001b[37mPOST /_dash-update-component HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [20/Jan/2021 17:15:19] \"\u001b[37mPOST /_dash-update-component HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [20/Jan/2021 17:15:19] \"\u001b[37mPOST /_dash-update-component HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [20/Jan/2021 17:15:20] \"\u001b[37mPOST /_dash-update-component HTTP/1.1\u001b[0m\" 200 -\n" ] } ], "source": [ "db = ContributionsTab(explainer)\n", "ExplainerDashboard(explainer, db).run()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Or even use string shorthands:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ExplainerDashboard(explainer, \"model_summary\").run()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**New in version 0.3.5**: Simple dashboard for regression/classification is now supported. It shows critical components for evaulating a classifier/regressor:\n", "\n", "- Simple classifier dashboard will display all of the followings on a single tab:\n", " - Confusion matrix \n", " - One other model quality indicator; default is ROC AUC curve, you may change it to other valid `classifier_components`: ['pr_auc', 'precision_graph', 'lift_curve', 'class_graph', 'roc_auc']\n", " - Shap importances\n", " - Shap dependence\n", " - index selector\n", " - index prediction summary\n", " - Shap contributions graph\n", "- Similarly, the new Simple regression dashboard will display all of the followings on a single tab:\n", " - Goodness of fit\n", " - One other model quality indicator; default is plot vs feature, you may change it to other valid `regression_components`: ['residuals', 'metrics'].\n", " - Shap importances\n", " - Shap dependence\n", " - index selector\n", " - index prediction summary\n", " - Shap contributions graph\n", "\n", "All you need is to pass `simple=True` when invoking an `ExplainerDashboard` object.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "db = ExplainerDashboard(\n", " explainer, simple=True\n", ") # by default ROC/AUC curve for classification\n", "db.run()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Change customized classification component on the top right corner, e.g. [PrecisionComponent](https://explainerdashboard.readthedocs.io/en/latest/components.html#precisioncomponent):" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "db = ExplainerDashboard(\n", " explainer, simple=True, classifier_custom_component=\"precision_graph\"\n", ")\n", "db.run()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## InlineExplainer\n", "\n", "Alternative API to display a single component inline in a notebook" ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "ExecuteTime": { "end_time": "2021-01-20T16:15:47.053796Z", "start_time": "2021-01-20T16:15:47.050286Z" } }, "outputs": [], "source": [ "from explainerdashboard import InlineExplainer" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "ExecuteTime": { "end_time": "2021-01-20T16:15:47.312606Z", "start_time": "2021-01-20T16:15:47.309934Z" } }, "outputs": [], "source": [ "ie = InlineExplainer(explainer)" ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "ExecuteTime": { "end_time": "2021-01-20T16:15:47.664170Z", "start_time": "2021-01-20T16:15:47.625282Z" } }, "outputs": [ { "data": { "text/html": [ "\n", " \n", " " ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "ie.model_stats()" ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "ExecuteTime": { "end_time": "2021-01-20T16:15:53.444394Z", "start_time": "2021-01-20T16:15:53.072097Z" }, "scrolled": true }, "outputs": [ { "data": { "text/html": [ "\n", " \n", " " ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "ie.tab.contributions()" ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "ExecuteTime": { "end_time": "2021-01-20T16:15:56.559913Z", "start_time": "2021-01-20T16:15:56.199941Z" } }, "outputs": [ { "data": { "text/html": [ "\n", " \n", " " ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "ie.classifier.confusion_matrix()" ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "ExecuteTime": { "end_time": "2021-01-20T16:15:59.378917Z", "start_time": "2021-01-20T16:15:59.014973Z" } }, "outputs": [ { "data": { "text/html": [ "\n", " \n", " " ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "ie.shap.dependence()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Terminating 'inline', 'jupyterlab' or 'external' dashboard\n", "\n", "You can terminate a `JupyterDash` based dashboard with the `.terminate(port)` method.\n", "\n", "Any ExplainerDashboard or InlineExplainer dashboard can terminate any other running dashboard as long you \n", "pass the right port." ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "ExecuteTime": { "end_time": "2021-01-20T16:16:11.988450Z", "start_time": "2021-01-20T16:16:11.977511Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Trying to shut down dashboard on port 8050...\n" ] } ], "source": [ "ie.terminate()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "db1 = ExplainerDashboard(explainer, mode=\"external\")\n", "db1.run(8051)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Also works as a `classmethod`:" ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "ExecuteTime": { "end_time": "2021-01-20T16:16:42.860081Z", "start_time": "2021-01-20T16:16:42.851769Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Trying to shut down dashboard on port 8051...\n", "Something seems to have failed: HTTPConnectionPool(host='localhost', port=8051): Max retries exceeded with url: /_shutdown_cb5be9b0-e8a9-4cc0-9181-cdb386085421 (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 61] Connection refused'))\n" ] } ], "source": [ "ExplainerDashboard.terminate(8051)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# XGBClassifier, LGBMClassifier, CatBoostClassifier\n", "- default for ClassifierExplainer is `model_output='probability'`, but for most gradient boosting classifier algorithms (e.g xgboost, lightgbm, catboost):\n", " - You have to pass an `X_background` to calculate the shape values against (defaults to using `X`)\n", " - You can't calculate shap interaction values\n", "- alternative is to pass model_output='logodds':\n", " - Can then calculate shap values based on trees alone (so no background data needed), and can calculate interaction values as well.\n", " - plus: faster" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# from lightgbm.sklearn import LGBMClassifier\n", "# model = LGBMClassifier()\n", "\n", "# from catboost import CatBoostClassifier\n", "# model = CatBoostClassifier(iterations=100, learning_rate=100)\n", "\n", "from xgboost import XGBClassifier\n", "\n", "model = XGBClassifier()\n", "\n", "model.fit(X_train, y_train)\n", "\n", "explainer = ClassifierExplainer(\n", " model,\n", " X_test,\n", " y_test,\n", " X_background=X_train,\n", " cats=[\"Sex\", \"Deck\", \"Embarked\"],\n", " idxs=test_names, # names of passengers\n", " labels=[\"Not survived\", \"Survived\"],\n", ")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ExplainerDashboard(explainer, mode=\"external\").run(8052)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# RegressionExplainer" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Load regression data:\n", " - predicting the fare that a titanic passenger paid for their ticket" ] }, { "cell_type": "code", "execution_count": 23, "metadata": { "ExecuteTime": { "end_time": "2020-10-12T09:02:26.311006Z", "start_time": "2020-10-12T09:02:26.307162Z" } }, "outputs": [], "source": [ "from explainerdashboard.datasets import titanic_fare, titanic_names" ] }, { "cell_type": "code", "execution_count": 24, "metadata": { "ExecuteTime": { "end_time": "2020-10-12T09:02:26.795632Z", "start_time": "2020-10-12T09:02:26.790486Z" } }, "outputs": [], "source": [ "X_train, y_train, X_test, y_test = titanic_fare()\n", "train_names, test_names = titanic_names()" ] }, { "cell_type": "code", "execution_count": 25, "metadata": { "ExecuteTime": { "end_time": "2020-10-12T09:02:27.238136Z", "start_time": "2020-10-12T09:02:27.219716Z" } }, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
SurvivedNo_of_relatives_on_boardAgePassengerClassNo_of_siblings_plus_spouses_on_boardNo_of_parents_plus_children_on_boardSex_femaleSex_maleSex_nanDeck_A...Deck_DDeck_EDeck_FDeck_GDeck_TDeck_UnkownEmbarked_CherbourgEmbarked_QueenstownEmbarked_SouthamptonEmbarked_Unknown
00122.03100100...0000010010
11026.03001000...0000010010
20035.03000100...0000010010
300-999.03000100...0000010100
40054.01000100...0100000010
\n", "

5 rows × 22 columns

\n", "
" ], "text/plain": [ " Survived No_of_relatives_on_board Age PassengerClass \\\n", "0 0 1 22.0 3 \n", "1 1 0 26.0 3 \n", "2 0 0 35.0 3 \n", "3 0 0 -999.0 3 \n", "4 0 0 54.0 1 \n", "\n", " No_of_siblings_plus_spouses_on_board No_of_parents_plus_children_on_board \\\n", "0 1 0 \n", "1 0 0 \n", "2 0 0 \n", "3 0 0 \n", "4 0 0 \n", "\n", " Sex_female Sex_male Sex_nan Deck_A ... Deck_D Deck_E Deck_F Deck_G \\\n", "0 0 1 0 0 ... 0 0 0 0 \n", "1 1 0 0 0 ... 0 0 0 0 \n", "2 0 1 0 0 ... 0 0 0 0 \n", "3 0 1 0 0 ... 0 0 0 0 \n", "4 0 1 0 0 ... 0 1 0 0 \n", "\n", " Deck_T Deck_Unkown Embarked_Cherbourg Embarked_Queenstown \\\n", "0 0 1 0 0 \n", "1 0 1 0 0 \n", "2 0 1 0 0 \n", "3 0 1 0 1 \n", "4 0 0 0 0 \n", "\n", " Embarked_Southampton Embarked_Unknown \n", "0 1 0 \n", "1 1 0 \n", "2 1 0 \n", "3 0 0 \n", "4 1 0 \n", "\n", "[5 rows x 22 columns]" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "X_train.head()" ] }, { "cell_type": "code", "execution_count": 26, "metadata": { "ExecuteTime": { "end_time": "2020-10-12T09:02:27.737291Z", "start_time": "2020-10-12T09:02:27.730334Z" } }, "outputs": [ { "data": { "text/plain": [ "0 7.2500\n", "1 7.9250\n", "2 8.0500\n", "3 8.4583\n", "4 51.8625\n", "Name: Fare, dtype: float64" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "y_train.head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Adding units of target\n", "- In this case we are predicting the price of the fare, so we can add the units as `\"$\"`\n", " - this will then be displayed along the axis of certain plots" ] }, { "cell_type": "code", "execution_count": 28, "metadata": { "ExecuteTime": { "end_time": "2020-10-12T09:02:52.461711Z", "start_time": "2020-10-12T09:02:52.359099Z" } }, "outputs": [ { "data": { "text/plain": [ "RandomForestRegressor(max_depth=5, n_estimators=50)" ] }, "execution_count": 28, "metadata": {}, "output_type": "execute_result" }, { "name": "stdout", "output_type": "stream", "text": [ "Note: shap=='guess' so guessing for RandomForestRegressor shap='tree'...\n", "Generating self.shap_explainer = shap.TreeExplainer(model)\n", "Changing class type to RandomForestRegressionExplainer...\n" ] } ], "source": [ "from sklearn.ensemble import RandomForestRegressor\n", "from explainerdashboard import RegressionExplainer\n", "\n", "model = RandomForestRegressor(n_estimators=50, max_depth=5)\n", "model.fit(X_train, y_train)\n", "\n", "explainer = RegressionExplainer(\n", " model,\n", " X_test,\n", " y_test,\n", " cats=[\"Sex\", \"Deck\", \"Embarked\"],\n", " idxs=test_names,\n", " descriptions=feature_descriptions,\n", " target=\"Fare\",\n", " units=\"$\",\n", ")" ] }, { "cell_type": "code", "execution_count": 29, "metadata": { "ExecuteTime": { "end_time": "2020-10-12T09:02:55.006908Z", "start_time": "2020-10-12T09:02:54.161128Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Building ExplainerDashboard..\n", "Generating ShadowDecTree for each individual decision tree...\n", "Generating layout...\n", "Calculating shap values...\n", "Calculating predictions...\n", "Calculating residuals...\n", "Calculating absolute residuals...\n", "Calculating dependencies...\n", "Calculating importances...\n", "Calculating shap interaction values...\n", "Registering callbacks...\n" ] }, { "data": { "text/html": [ "\n", " \n", " " ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "Calculating prediction percentiles...\n" ] } ], "source": [ "ExplainerDashboard(explainer, mode=\"inline\").run()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## LGBMRegressor, LinearRegression, CatBoostRegressor, XGBRegressor" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from lightgbm.sklearn import LGBMRegressor\n", "\n", "model = LGBMRegressor()\n", "\n", "# from sklearn.linear_model import LinearRegression\n", "# model = LinearRegression()\n", "\n", "# from catboost import CatBoostRegressor\n", "# model = CatBoostRegressor(iterations=100, learning_rate=0.1, verbose=0)\n", "\n", "# from xgboost import XGBRegressor\n", "# model = XGBRegressor()\n", "\n", "model.fit(X_train, y_train)\n", "explainer = RegressionExplainer(\n", " model, X_test, y_test, cats=[\"Sex\", \"Deck\", \"Embarked\"], idxs=test_names, units=\"$\"\n", ")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ExplainerDashboard(explainer).run()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Multiclass Classifiers" ] }, { "cell_type": "code", "execution_count": 30, "metadata": { "ExecuteTime": { "end_time": "2020-10-12T09:03:29.475750Z", "start_time": "2020-10-12T09:03:29.468922Z" } }, "outputs": [], "source": [ "from explainerdashboard.datasets import titanic_embarked, titanic_names\n", "\n", "X_train, y_train, X_test, y_test = titanic_embarked()\n", "train_names, test_names = titanic_names()" ] }, { "cell_type": "code", "execution_count": 31, "metadata": { "ExecuteTime": { "end_time": "2020-10-12T09:03:34.226135Z", "start_time": "2020-10-12T09:03:34.042544Z" } }, "outputs": [ { "data": { "text/plain": [ "RandomForestClassifier()" ] }, "execution_count": 31, "metadata": {}, "output_type": "execute_result" }, { "name": "stdout", "output_type": "stream", "text": [ "Note: shap=='guess' so guessing for RandomForestClassifier shap='tree'...\n", "Note: model_output=='probability', so assuming that raw shap output of RandomForestClassifier is in probability space...\n", "Generating self.shap_explainer = shap.TreeExplainer(model)\n", "Detected RandomForestClassifier model: Changing class type to RandomForestClassifierExplainer...\n" ] } ], "source": [ "from sklearn.ensemble import RandomForestClassifier\n", "\n", "model = RandomForestClassifier()\n", "\n", "model.fit(X_train, y_train)\n", "\n", "explainer = ClassifierExplainer(\n", " model,\n", " X_test,\n", " y_test,\n", " cats=[\"Sex\", \"Deck\"],\n", " idxs=test_names,\n", " labels=[\"Queenstown\", \"Southampton\", \"Cherbourg\"],\n", " pos_label=\"Southampton\",\n", ")" ] }, { "cell_type": "code", "execution_count": 32, "metadata": { "ExecuteTime": { "end_time": "2020-10-12T09:04:02.751994Z", "start_time": "2020-10-12T09:03:35.879002Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Building ExplainerDashboard..\n", "Generating ShadowDecTree for each individual decision tree...\n", "Generating layout...\n", "Calculating shap values...\n", "Calculating dependencies...\n", "Calculating permutation importances (if slow, try setting n_jobs parameter)...\n", "Calculating categorical permutation importances (if slow, try setting n_jobs parameter)...\n", "Calculating predictions...\n", "Calculating prediction probabilities...\n", "Calculating pred_percentiles...\n", "Calculating shap interaction values...\n", "Registering callbacks...\n", "Starting ExplainerDashboard on http://localhost:8050\n", "Dash app running on http://127.0.0.1:8050/\n" ] } ], "source": [ "ExplainerDashboard(explainer, mode=\"external\").run()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3.7.1 64-bit ('flask-ui': conda)", "name": "python371jvsc74a57bd040fae5cf2b0560dbe75793bcfc027cd1f0058fbc531fbe58e6470975acaad7bd" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.1" }, "toc": { "base_numbering": 1, "nav_menu": {}, "number_sections": true, "sideBar": true, "skip_h1_title": false, "title_cell": "Table of Contents", "title_sidebar": "Contents", "toc_cell": true, "toc_position": { "height": "calc(100% - 180px)", "left": "10px", "top": "150px", "width": "285.33px" }, "toc_section_display": true, "toc_window_display": true } }, "nbformat": 4, "nbformat_minor": 4 }