BDC-team-1/Sport/Modelization/CA_segment_sport.ipynb

3583 lines
174 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{
"cells": [
{
"cell_type": "markdown",
"id": "84b6e27e-4bda-4d38-8689-ec7fc0da1848",
"metadata": {},
"source": [
"# Define segment and predict sales associated"
]
},
{
"cell_type": "markdown",
"id": "ec059482-45d3-4ae6-99bc-9b4ced115db3",
"metadata": {},
"source": [
"## Importations of packages "
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "9771bf29-d08e-4674-8c23-9a2672fbef8f",
"metadata": {},
"outputs": [],
"source": [
"import pandas as pd\n",
"from pandas import DataFrame\n",
"import numpy as np\n",
"import os\n",
"import s3fs\n",
"import re\n",
"from sklearn.linear_model import LogisticRegression\n",
"from sklearn.ensemble import RandomForestClassifier\n",
"from sklearn.metrics import accuracy_score, confusion_matrix, classification_report, recall_score\n",
"from sklearn.utils import class_weight\n",
"from sklearn.neighbors import KNeighborsClassifier\n",
"from sklearn.pipeline import Pipeline\n",
"from sklearn.compose import ColumnTransformer\n",
"from sklearn.preprocessing import OneHotEncoder\n",
"from sklearn.impute import SimpleImputer\n",
"from sklearn.model_selection import GridSearchCV\n",
"from sklearn.preprocessing import StandardScaler, MaxAbsScaler, MinMaxScaler\n",
"from sklearn.metrics import make_scorer, f1_score, balanced_accuracy_score\n",
"import seaborn as sns\n",
"import matplotlib.pyplot as plt\n",
"from sklearn.metrics import roc_curve, auc, precision_recall_curve, average_precision_score\n",
"from sklearn.exceptions import ConvergenceWarning, DataConversionWarning\n",
"from sklearn.naive_bayes import GaussianNB\n",
"from scipy.optimize import fsolve\n",
"import io\n",
"\n",
"import pickle\n",
"import warnings"
]
},
{
"cell_type": "markdown",
"id": "048fcd7c-800a-4a6b-b725-faf8410f924a",
"metadata": {},
"source": [
"## load databases"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "539ccbdf-f29f-4f04-99c1-8c88d0efe514",
"metadata": {},
"outputs": [],
"source": [
"# Create filesystem object\n",
"S3_ENDPOINT_URL = \"https://\" + os.environ[\"AWS_S3_ENDPOINT\"]\n",
"fs = s3fs.S3FileSystem(client_kwargs={'endpoint_url': S3_ENDPOINT_URL})"
]
},
{
"cell_type": "code",
"execution_count": 75,
"id": "d6017ed0-6233-4888-85a7-05dec50a255b",
"metadata": {},
"outputs": [],
"source": [
"type_of_activity = \"musique\""
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "0c3a6ddc-9345-4a42-b6bf-a20a95de3028",
"metadata": {},
"outputs": [],
"source": [
"def load_train_test(type_of_activity):\n",
" # BUCKET = f\"projet-bdc2324-team1/Generalization/{type_of_activity}\"\n",
" BUCKET = f\"projet-bdc2324-team1/Generalization_v2/{type_of_activity}\"\n",
" File_path_train = BUCKET + \"/Train_set.csv\"\n",
" File_path_test = BUCKET + \"/Test_set.csv\"\n",
" \n",
" with fs.open( File_path_train, mode=\"rb\") as file_in:\n",
" dataset_train = pd.read_csv(file_in, sep=\",\")\n",
" # dataset_train['y_has_purchased'] = dataset_train['y_has_purchased'].fillna(0)\n",
"\n",
" with fs.open(File_path_test, mode=\"rb\") as file_in:\n",
" dataset_test = pd.read_csv(file_in, sep=\",\")\n",
" # dataset_test['y_has_purchased'] = dataset_test['y_has_purchased'].fillna(0)\n",
" \n",
" return dataset_train, dataset_test"
]
},
{
"cell_type": "code",
"execution_count": 76,
"id": "2831d546-b365-498b-8248-c618bd9c3057",
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"/tmp/ipykernel_552/3983721681.py:8: DtypeWarning: Columns (10,19,20,21,24) have mixed types. Specify dtype option on import or set low_memory=False.\n",
" dataset_train = pd.read_csv(file_in, sep=\",\")\n",
"/tmp/ipykernel_552/3983721681.py:12: DtypeWarning: Columns (19,20,21,24) have mixed types. Specify dtype option on import or set low_memory=False.\n",
" dataset_test = pd.read_csv(file_in, sep=\",\")\n"
]
},
{
"data": {
"text/plain": [
"customer_id 0\n",
"street_id 0\n",
"structure_id 327020\n",
"mcp_contact_id 135470\n",
"fidelity 0\n",
" ... \n",
"purchases_8_2021 113963\n",
"purchases_8_2022 0\n",
"purchases_9_2021 113963\n",
"purchases_9_2022 0\n",
"y_has_purchased 0\n",
"Length: 87, dtype: int64"
]
},
"execution_count": 76,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"dataset_train, dataset_test = load_train_test(type_of_activity)\n",
"dataset_train.isna().sum()"
]
},
{
"cell_type": "code",
"execution_count": 17,
"id": "b8827f7b-b304-4f51-9814-c7a98ed88cf0",
"metadata": {},
"outputs": [],
"source": [
"def features_target_split(dataset_train, dataset_test):\n",
" \n",
" features_l = ['nb_tickets', 'nb_purchases', 'total_amount', 'nb_suppliers', 'purchase_date_min', 'purchase_date_max', \n",
" 'time_between_purchase', 'fidelity', 'is_email_true', 'opt_in', #'is_partner', 'nb_tickets_internet',, 'vente_internet_max'\n",
" 'gender_female', 'gender_male', 'gender_other', 'nb_campaigns', 'nb_campaigns_opened']\n",
"\n",
" # we suppress fidelity, time between purchase, and gender other (colinearity issue)\n",
" \"\"\"\n",
" features_l = ['nb_tickets', 'nb_purchases', 'total_amount', 'nb_suppliers', 'vente_internet_max', \n",
" 'purchase_date_min', 'purchase_date_max', 'nb_tickets_internet', 'is_email_true', \n",
" 'opt_in', 'gender_female', 'gender_male', 'nb_campaigns', 'nb_campaigns_opened']\n",
" \"\"\"\n",
" \n",
" X_train = dataset_train # [features_l]\n",
" y_train = dataset_train[['y_has_purchased']]\n",
"\n",
" X_test = dataset_test # [features_l]\n",
" y_test = dataset_test[['y_has_purchased']]\n",
" return X_train, X_test, y_train, y_test"
]
},
{
"cell_type": "code",
"execution_count": 77,
"id": "c18195fc-ed40-4e39-a59e-c9ecc5a8e6c3",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Shape train : (354365, 87)\n",
"Shape test : (151874, 87)\n"
]
}
],
"source": [
"X_train, X_test, y_train, y_test = features_target_split(dataset_train, dataset_test)\n",
"print(\"Shape train : \", X_train.shape)\n",
"print(\"Shape test : \", X_test.shape)"
]
},
{
"cell_type": "markdown",
"id": "74eda066-5e01-43aa-b0cf-cc6d9bbf770e",
"metadata": {},
"source": [
"## get results from the logit cross validated model"
]
},
{
"cell_type": "code",
"execution_count": 78,
"id": "7c81390e-598c-4f02-bd56-dd03b00dcb33",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>customer_id</th>\n",
" <th>street_id</th>\n",
" <th>structure_id</th>\n",
" <th>mcp_contact_id</th>\n",
" <th>fidelity</th>\n",
" <th>tenant_id</th>\n",
" <th>is_partner</th>\n",
" <th>deleted_at</th>\n",
" <th>is_email_true</th>\n",
" <th>opt_in</th>\n",
" <th>...</th>\n",
" <th>purchases_5_2022</th>\n",
" <th>purchases_6_2021</th>\n",
" <th>purchases_6_2022</th>\n",
" <th>purchases_7_2021</th>\n",
" <th>purchases_7_2022</th>\n",
" <th>purchases_8_2021</th>\n",
" <th>purchases_8_2022</th>\n",
" <th>purchases_9_2021</th>\n",
" <th>purchases_9_2022</th>\n",
" <th>y_has_purchased</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>10_699783</td>\n",
" <td>139</td>\n",
" <td>NaN</td>\n",
" <td>186852.0</td>\n",
" <td>0</td>\n",
" <td>875</td>\n",
" <td>False</td>\n",
" <td>NaN</td>\n",
" <td>True</td>\n",
" <td>0</td>\n",
" <td>...</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>10_38307</td>\n",
" <td>862</td>\n",
" <td>NaN</td>\n",
" <td>17621.0</td>\n",
" <td>7</td>\n",
" <td>875</td>\n",
" <td>False</td>\n",
" <td>NaN</td>\n",
" <td>True</td>\n",
" <td>0</td>\n",
" <td>...</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>10_556101</td>\n",
" <td>1063</td>\n",
" <td>NaN</td>\n",
" <td>136909.0</td>\n",
" <td>0</td>\n",
" <td>875</td>\n",
" <td>False</td>\n",
" <td>NaN</td>\n",
" <td>True</td>\n",
" <td>1</td>\n",
" <td>...</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>10_686663</td>\n",
" <td>443226</td>\n",
" <td>NaN</td>\n",
" <td>186611.0</td>\n",
" <td>1</td>\n",
" <td>875</td>\n",
" <td>False</td>\n",
" <td>NaN</td>\n",
" <td>True</td>\n",
" <td>1</td>\n",
" <td>...</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>1.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>10_91656</td>\n",
" <td>316684</td>\n",
" <td>NaN</td>\n",
" <td>21559.0</td>\n",
" <td>2</td>\n",
" <td>875</td>\n",
" <td>False</td>\n",
" <td>NaN</td>\n",
" <td>True</td>\n",
" <td>0</td>\n",
" <td>...</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>...</th>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" </tr>\n",
" <tr>\n",
" <th>151869</th>\n",
" <td>14_1843791</td>\n",
" <td>718883</td>\n",
" <td>224.0</td>\n",
" <td>394849.0</td>\n",
" <td>1</td>\n",
" <td>862</td>\n",
" <td>False</td>\n",
" <td>NaN</td>\n",
" <td>True</td>\n",
" <td>1</td>\n",
" <td>...</td>\n",
" <td>0.0</td>\n",
" <td>NaN</td>\n",
" <td>0.0</td>\n",
" <td>NaN</td>\n",
" <td>0.0</td>\n",
" <td>NaN</td>\n",
" <td>0.0</td>\n",
" <td>NaN</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>151870</th>\n",
" <td>14_4630858</td>\n",
" <td>741826</td>\n",
" <td>NaN</td>\n",
" <td>1555631.0</td>\n",
" <td>0</td>\n",
" <td>862</td>\n",
" <td>False</td>\n",
" <td>NaN</td>\n",
" <td>True</td>\n",
" <td>1</td>\n",
" <td>...</td>\n",
" <td>0.0</td>\n",
" <td>NaN</td>\n",
" <td>0.0</td>\n",
" <td>NaN</td>\n",
" <td>0.0</td>\n",
" <td>NaN</td>\n",
" <td>0.0</td>\n",
" <td>NaN</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>151871</th>\n",
" <td>14_4659926</td>\n",
" <td>871477</td>\n",
" <td>NaN</td>\n",
" <td>1542180.0</td>\n",
" <td>0</td>\n",
" <td>862</td>\n",
" <td>False</td>\n",
" <td>NaN</td>\n",
" <td>True</td>\n",
" <td>1</td>\n",
" <td>...</td>\n",
" <td>0.0</td>\n",
" <td>NaN</td>\n",
" <td>0.0</td>\n",
" <td>NaN</td>\n",
" <td>0.0</td>\n",
" <td>NaN</td>\n",
" <td>0.0</td>\n",
" <td>NaN</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>151872</th>\n",
" <td>14_4881492</td>\n",
" <td>917272</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>1</td>\n",
" <td>862</td>\n",
" <td>False</td>\n",
" <td>NaN</td>\n",
" <td>True</td>\n",
" <td>1</td>\n",
" <td>...</td>\n",
" <td>0.0</td>\n",
" <td>NaN</td>\n",
" <td>0.0</td>\n",
" <td>NaN</td>\n",
" <td>0.0</td>\n",
" <td>NaN</td>\n",
" <td>0.0</td>\n",
" <td>NaN</td>\n",
" <td>1.0</td>\n",
" <td>0.0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>151873</th>\n",
" <td>14_8124</td>\n",
" <td>2762</td>\n",
" <td>NaN</td>\n",
" <td>10077.0</td>\n",
" <td>2</td>\n",
" <td>862</td>\n",
" <td>False</td>\n",
" <td>NaN</td>\n",
" <td>True</td>\n",
" <td>0</td>\n",
" <td>...</td>\n",
" <td>0.0</td>\n",
" <td>NaN</td>\n",
" <td>0.0</td>\n",
" <td>NaN</td>\n",
" <td>0.0</td>\n",
" <td>NaN</td>\n",
" <td>0.0</td>\n",
" <td>NaN</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"<p>151874 rows × 87 columns</p>\n",
"</div>"
],
"text/plain": [
" customer_id street_id structure_id mcp_contact_id fidelity \\\n",
"0 10_699783 139 NaN 186852.0 0 \n",
"1 10_38307 862 NaN 17621.0 7 \n",
"2 10_556101 1063 NaN 136909.0 0 \n",
"3 10_686663 443226 NaN 186611.0 1 \n",
"4 10_91656 316684 NaN 21559.0 2 \n",
"... ... ... ... ... ... \n",
"151869 14_1843791 718883 224.0 394849.0 1 \n",
"151870 14_4630858 741826 NaN 1555631.0 0 \n",
"151871 14_4659926 871477 NaN 1542180.0 0 \n",
"151872 14_4881492 917272 NaN NaN 1 \n",
"151873 14_8124 2762 NaN 10077.0 2 \n",
"\n",
" tenant_id is_partner deleted_at is_email_true opt_in ... \\\n",
"0 875 False NaN True 0 ... \n",
"1 875 False NaN True 0 ... \n",
"2 875 False NaN True 1 ... \n",
"3 875 False NaN True 1 ... \n",
"4 875 False NaN True 0 ... \n",
"... ... ... ... ... ... ... \n",
"151869 862 False NaN True 1 ... \n",
"151870 862 False NaN True 1 ... \n",
"151871 862 False NaN True 1 ... \n",
"151872 862 False NaN True 1 ... \n",
"151873 862 False NaN True 0 ... \n",
"\n",
" purchases_5_2022 purchases_6_2021 purchases_6_2022 purchases_7_2021 \\\n",
"0 0.0 0.0 0.0 0.0 \n",
"1 0.0 0.0 0.0 0.0 \n",
"2 0.0 0.0 0.0 0.0 \n",
"3 0.0 0.0 0.0 0.0 \n",
"4 0.0 0.0 0.0 0.0 \n",
"... ... ... ... ... \n",
"151869 0.0 NaN 0.0 NaN \n",
"151870 0.0 NaN 0.0 NaN \n",
"151871 0.0 NaN 0.0 NaN \n",
"151872 0.0 NaN 0.0 NaN \n",
"151873 0.0 NaN 0.0 NaN \n",
"\n",
" purchases_7_2022 purchases_8_2021 purchases_8_2022 \\\n",
"0 0.0 0.0 0.0 \n",
"1 0.0 0.0 0.0 \n",
"2 0.0 0.0 0.0 \n",
"3 0.0 0.0 1.0 \n",
"4 0.0 0.0 0.0 \n",
"... ... ... ... \n",
"151869 0.0 NaN 0.0 \n",
"151870 0.0 NaN 0.0 \n",
"151871 0.0 NaN 0.0 \n",
"151872 0.0 NaN 0.0 \n",
"151873 0.0 NaN 0.0 \n",
"\n",
" purchases_9_2021 purchases_9_2022 y_has_purchased \n",
"0 0.0 0.0 0.0 \n",
"1 0.0 0.0 0.0 \n",
"2 0.0 0.0 0.0 \n",
"3 0.0 0.0 0.0 \n",
"4 0.0 0.0 0.0 \n",
"... ... ... ... \n",
"151869 NaN 0.0 0.0 \n",
"151870 NaN 0.0 0.0 \n",
"151871 NaN 0.0 0.0 \n",
"151872 NaN 1.0 0.0 \n",
"151873 NaN 0.0 0.0 \n",
"\n",
"[151874 rows x 87 columns]"
]
},
"execution_count": 78,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"X_test"
]
},
{
"cell_type": "code",
"execution_count": 20,
"id": "c708f439-bb75-4688-bf4f-4c04e13deaae",
"metadata": {},
"outputs": [],
"source": [
"def load_model(type_of_activity, model):\n",
" # BUCKET = f\"projet-bdc2324-team1/Output_model/{type_of_activity}/{model}/\"\n",
" BUCKET = f\"projet-bdc2324-team1/basique/{type_of_activity}/{model}/\"\n",
" filename = model + '.pkl'\n",
" file_path = BUCKET + filename\n",
" with fs.open(file_path, mode=\"rb\") as f:\n",
" model_bytes = f.read()\n",
"\n",
" model = pickle.loads(model_bytes)\n",
" return model"
]
},
{
"cell_type": "code",
"execution_count": 92,
"id": "5261a803-05b8-41a0-968c-dc7bde48ddd3",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<style>#sk-container-id-7 {\n",
" /* Definition of color scheme common for light and dark mode */\n",
" --sklearn-color-text: black;\n",
" --sklearn-color-line: gray;\n",
" /* Definition of color scheme for unfitted estimators */\n",
" --sklearn-color-unfitted-level-0: #fff5e6;\n",
" --sklearn-color-unfitted-level-1: #f6e4d2;\n",
" --sklearn-color-unfitted-level-2: #ffe0b3;\n",
" --sklearn-color-unfitted-level-3: chocolate;\n",
" /* Definition of color scheme for fitted estimators */\n",
" --sklearn-color-fitted-level-0: #f0f8ff;\n",
" --sklearn-color-fitted-level-1: #d4ebff;\n",
" --sklearn-color-fitted-level-2: #b3dbfd;\n",
" --sklearn-color-fitted-level-3: cornflowerblue;\n",
"\n",
" /* Specific color for light theme */\n",
" --sklearn-color-text-on-default-background: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, black)));\n",
" --sklearn-color-background: var(--sg-background-color, var(--theme-background, var(--jp-layout-color0, white)));\n",
" --sklearn-color-border-box: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, black)));\n",
" --sklearn-color-icon: #696969;\n",
"\n",
" @media (prefers-color-scheme: dark) {\n",
" /* Redefinition of color scheme for dark theme */\n",
" --sklearn-color-text-on-default-background: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, white)));\n",
" --sklearn-color-background: var(--sg-background-color, var(--theme-background, var(--jp-layout-color0, #111)));\n",
" --sklearn-color-border-box: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, white)));\n",
" --sklearn-color-icon: #878787;\n",
" }\n",
"}\n",
"\n",
"#sk-container-id-7 {\n",
" color: var(--sklearn-color-text);\n",
"}\n",
"\n",
"#sk-container-id-7 pre {\n",
" padding: 0;\n",
"}\n",
"\n",
"#sk-container-id-7 input.sk-hidden--visually {\n",
" border: 0;\n",
" clip: rect(1px 1px 1px 1px);\n",
" clip: rect(1px, 1px, 1px, 1px);\n",
" height: 1px;\n",
" margin: -1px;\n",
" overflow: hidden;\n",
" padding: 0;\n",
" position: absolute;\n",
" width: 1px;\n",
"}\n",
"\n",
"#sk-container-id-7 div.sk-dashed-wrapped {\n",
" border: 1px dashed var(--sklearn-color-line);\n",
" margin: 0 0.4em 0.5em 0.4em;\n",
" box-sizing: border-box;\n",
" padding-bottom: 0.4em;\n",
" background-color: var(--sklearn-color-background);\n",
"}\n",
"\n",
"#sk-container-id-7 div.sk-container {\n",
" /* jupyter's `normalize.less` sets `[hidden] { display: none; }`\n",
" but bootstrap.min.css set `[hidden] { display: none !important; }`\n",
" so we also need the `!important` here to be able to override the\n",
" default hidden behavior on the sphinx rendered scikit-learn.org.\n",
" See: https://github.com/scikit-learn/scikit-learn/issues/21755 */\n",
" display: inline-block !important;\n",
" position: relative;\n",
"}\n",
"\n",
"#sk-container-id-7 div.sk-text-repr-fallback {\n",
" display: none;\n",
"}\n",
"\n",
"div.sk-parallel-item,\n",
"div.sk-serial,\n",
"div.sk-item {\n",
" /* draw centered vertical line to link estimators */\n",
" background-image: linear-gradient(var(--sklearn-color-text-on-default-background), var(--sklearn-color-text-on-default-background));\n",
" background-size: 2px 100%;\n",
" background-repeat: no-repeat;\n",
" background-position: center center;\n",
"}\n",
"\n",
"/* Parallel-specific style estimator block */\n",
"\n",
"#sk-container-id-7 div.sk-parallel-item::after {\n",
" content: \"\";\n",
" width: 100%;\n",
" border-bottom: 2px solid var(--sklearn-color-text-on-default-background);\n",
" flex-grow: 1;\n",
"}\n",
"\n",
"#sk-container-id-7 div.sk-parallel {\n",
" display: flex;\n",
" align-items: stretch;\n",
" justify-content: center;\n",
" background-color: var(--sklearn-color-background);\n",
" position: relative;\n",
"}\n",
"\n",
"#sk-container-id-7 div.sk-parallel-item {\n",
" display: flex;\n",
" flex-direction: column;\n",
"}\n",
"\n",
"#sk-container-id-7 div.sk-parallel-item:first-child::after {\n",
" align-self: flex-end;\n",
" width: 50%;\n",
"}\n",
"\n",
"#sk-container-id-7 div.sk-parallel-item:last-child::after {\n",
" align-self: flex-start;\n",
" width: 50%;\n",
"}\n",
"\n",
"#sk-container-id-7 div.sk-parallel-item:only-child::after {\n",
" width: 0;\n",
"}\n",
"\n",
"/* Serial-specific style estimator block */\n",
"\n",
"#sk-container-id-7 div.sk-serial {\n",
" display: flex;\n",
" flex-direction: column;\n",
" align-items: center;\n",
" background-color: var(--sklearn-color-background);\n",
" padding-right: 1em;\n",
" padding-left: 1em;\n",
"}\n",
"\n",
"\n",
"/* Toggleable style: style used for estimator/Pipeline/ColumnTransformer box that is\n",
"clickable and can be expanded/collapsed.\n",
"- Pipeline and ColumnTransformer use this feature and define the default style\n",
"- Estimators will overwrite some part of the style using the `sk-estimator` class\n",
"*/\n",
"\n",
"/* Pipeline and ColumnTransformer style (default) */\n",
"\n",
"#sk-container-id-7 div.sk-toggleable {\n",
" /* Default theme specific background. It is overwritten whether we have a\n",
" specific estimator or a Pipeline/ColumnTransformer */\n",
" background-color: var(--sklearn-color-background);\n",
"}\n",
"\n",
"/* Toggleable label */\n",
"#sk-container-id-7 label.sk-toggleable__label {\n",
" cursor: pointer;\n",
" display: block;\n",
" width: 100%;\n",
" margin-bottom: 0;\n",
" padding: 0.5em;\n",
" box-sizing: border-box;\n",
" text-align: center;\n",
"}\n",
"\n",
"#sk-container-id-7 label.sk-toggleable__label-arrow:before {\n",
" /* Arrow on the left of the label */\n",
" content: \"▸\";\n",
" float: left;\n",
" margin-right: 0.25em;\n",
" color: var(--sklearn-color-icon);\n",
"}\n",
"\n",
"#sk-container-id-7 label.sk-toggleable__label-arrow:hover:before {\n",
" color: var(--sklearn-color-text);\n",
"}\n",
"\n",
"/* Toggleable content - dropdown */\n",
"\n",
"#sk-container-id-7 div.sk-toggleable__content {\n",
" max-height: 0;\n",
" max-width: 0;\n",
" overflow: hidden;\n",
" text-align: left;\n",
" /* unfitted */\n",
" background-color: var(--sklearn-color-unfitted-level-0);\n",
"}\n",
"\n",
"#sk-container-id-7 div.sk-toggleable__content.fitted {\n",
" /* fitted */\n",
" background-color: var(--sklearn-color-fitted-level-0);\n",
"}\n",
"\n",
"#sk-container-id-7 div.sk-toggleable__content pre {\n",
" margin: 0.2em;\n",
" border-radius: 0.25em;\n",
" color: var(--sklearn-color-text);\n",
" /* unfitted */\n",
" background-color: var(--sklearn-color-unfitted-level-0);\n",
"}\n",
"\n",
"#sk-container-id-7 div.sk-toggleable__content.fitted pre {\n",
" /* unfitted */\n",
" background-color: var(--sklearn-color-fitted-level-0);\n",
"}\n",
"\n",
"#sk-container-id-7 input.sk-toggleable__control:checked~div.sk-toggleable__content {\n",
" /* Expand drop-down */\n",
" max-height: 200px;\n",
" max-width: 100%;\n",
" overflow: auto;\n",
"}\n",
"\n",
"#sk-container-id-7 input.sk-toggleable__control:checked~label.sk-toggleable__label-arrow:before {\n",
" content: \"▾\";\n",
"}\n",
"\n",
"/* Pipeline/ColumnTransformer-specific style */\n",
"\n",
"#sk-container-id-7 div.sk-label input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
" color: var(--sklearn-color-text);\n",
" background-color: var(--sklearn-color-unfitted-level-2);\n",
"}\n",
"\n",
"#sk-container-id-7 div.sk-label.fitted input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
" background-color: var(--sklearn-color-fitted-level-2);\n",
"}\n",
"\n",
"/* Estimator-specific style */\n",
"\n",
"/* Colorize estimator box */\n",
"#sk-container-id-7 div.sk-estimator input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
" /* unfitted */\n",
" background-color: var(--sklearn-color-unfitted-level-2);\n",
"}\n",
"\n",
"#sk-container-id-7 div.sk-estimator.fitted input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
" /* fitted */\n",
" background-color: var(--sklearn-color-fitted-level-2);\n",
"}\n",
"\n",
"#sk-container-id-7 div.sk-label label.sk-toggleable__label,\n",
"#sk-container-id-7 div.sk-label label {\n",
" /* The background is the default theme color */\n",
" color: var(--sklearn-color-text-on-default-background);\n",
"}\n",
"\n",
"/* On hover, darken the color of the background */\n",
"#sk-container-id-7 div.sk-label:hover label.sk-toggleable__label {\n",
" color: var(--sklearn-color-text);\n",
" background-color: var(--sklearn-color-unfitted-level-2);\n",
"}\n",
"\n",
"/* Label box, darken color on hover, fitted */\n",
"#sk-container-id-7 div.sk-label.fitted:hover label.sk-toggleable__label.fitted {\n",
" color: var(--sklearn-color-text);\n",
" background-color: var(--sklearn-color-fitted-level-2);\n",
"}\n",
"\n",
"/* Estimator label */\n",
"\n",
"#sk-container-id-7 div.sk-label label {\n",
" font-family: monospace;\n",
" font-weight: bold;\n",
" display: inline-block;\n",
" line-height: 1.2em;\n",
"}\n",
"\n",
"#sk-container-id-7 div.sk-label-container {\n",
" text-align: center;\n",
"}\n",
"\n",
"/* Estimator-specific */\n",
"#sk-container-id-7 div.sk-estimator {\n",
" font-family: monospace;\n",
" border: 1px dotted var(--sklearn-color-border-box);\n",
" border-radius: 0.25em;\n",
" box-sizing: border-box;\n",
" margin-bottom: 0.5em;\n",
" /* unfitted */\n",
" background-color: var(--sklearn-color-unfitted-level-0);\n",
"}\n",
"\n",
"#sk-container-id-7 div.sk-estimator.fitted {\n",
" /* fitted */\n",
" background-color: var(--sklearn-color-fitted-level-0);\n",
"}\n",
"\n",
"/* on hover */\n",
"#sk-container-id-7 div.sk-estimator:hover {\n",
" /* unfitted */\n",
" background-color: var(--sklearn-color-unfitted-level-2);\n",
"}\n",
"\n",
"#sk-container-id-7 div.sk-estimator.fitted:hover {\n",
" /* fitted */\n",
" background-color: var(--sklearn-color-fitted-level-2);\n",
"}\n",
"\n",
"/* Specification for estimator info (e.g. \"i\" and \"?\") */\n",
"\n",
"/* Common style for \"i\" and \"?\" */\n",
"\n",
".sk-estimator-doc-link,\n",
"a:link.sk-estimator-doc-link,\n",
"a:visited.sk-estimator-doc-link {\n",
" float: right;\n",
" font-size: smaller;\n",
" line-height: 1em;\n",
" font-family: monospace;\n",
" background-color: var(--sklearn-color-background);\n",
" border-radius: 1em;\n",
" height: 1em;\n",
" width: 1em;\n",
" text-decoration: none !important;\n",
" margin-left: 1ex;\n",
" /* unfitted */\n",
" border: var(--sklearn-color-unfitted-level-1) 1pt solid;\n",
" color: var(--sklearn-color-unfitted-level-1);\n",
"}\n",
"\n",
".sk-estimator-doc-link.fitted,\n",
"a:link.sk-estimator-doc-link.fitted,\n",
"a:visited.sk-estimator-doc-link.fitted {\n",
" /* fitted */\n",
" border: var(--sklearn-color-fitted-level-1) 1pt solid;\n",
" color: var(--sklearn-color-fitted-level-1);\n",
"}\n",
"\n",
"/* On hover */\n",
"div.sk-estimator:hover .sk-estimator-doc-link:hover,\n",
".sk-estimator-doc-link:hover,\n",
"div.sk-label-container:hover .sk-estimator-doc-link:hover,\n",
".sk-estimator-doc-link:hover {\n",
" /* unfitted */\n",
" background-color: var(--sklearn-color-unfitted-level-3);\n",
" color: var(--sklearn-color-background);\n",
" text-decoration: none;\n",
"}\n",
"\n",
"div.sk-estimator.fitted:hover .sk-estimator-doc-link.fitted:hover,\n",
".sk-estimator-doc-link.fitted:hover,\n",
"div.sk-label-container:hover .sk-estimator-doc-link.fitted:hover,\n",
".sk-estimator-doc-link.fitted:hover {\n",
" /* fitted */\n",
" background-color: var(--sklearn-color-fitted-level-3);\n",
" color: var(--sklearn-color-background);\n",
" text-decoration: none;\n",
"}\n",
"\n",
"/* Span, style for the box shown on hovering the info icon */\n",
".sk-estimator-doc-link span {\n",
" display: none;\n",
" z-index: 9999;\n",
" position: relative;\n",
" font-weight: normal;\n",
" right: .2ex;\n",
" padding: .5ex;\n",
" margin: .5ex;\n",
" width: min-content;\n",
" min-width: 20ex;\n",
" max-width: 50ex;\n",
" color: var(--sklearn-color-text);\n",
" box-shadow: 2pt 2pt 4pt #999;\n",
" /* unfitted */\n",
" background: var(--sklearn-color-unfitted-level-0);\n",
" border: .5pt solid var(--sklearn-color-unfitted-level-3);\n",
"}\n",
"\n",
".sk-estimator-doc-link.fitted span {\n",
" /* fitted */\n",
" background: var(--sklearn-color-fitted-level-0);\n",
" border: var(--sklearn-color-fitted-level-3);\n",
"}\n",
"\n",
".sk-estimator-doc-link:hover span {\n",
" display: block;\n",
"}\n",
"\n",
"/* \"?\"-specific style due to the `<a>` HTML tag */\n",
"\n",
"#sk-container-id-7 a.estimator_doc_link {\n",
" float: right;\n",
" font-size: 1rem;\n",
" line-height: 1em;\n",
" font-family: monospace;\n",
" background-color: var(--sklearn-color-background);\n",
" border-radius: 1rem;\n",
" height: 1rem;\n",
" width: 1rem;\n",
" text-decoration: none;\n",
" /* unfitted */\n",
" color: var(--sklearn-color-unfitted-level-1);\n",
" border: var(--sklearn-color-unfitted-level-1) 1pt solid;\n",
"}\n",
"\n",
"#sk-container-id-7 a.estimator_doc_link.fitted {\n",
" /* fitted */\n",
" border: var(--sklearn-color-fitted-level-1) 1pt solid;\n",
" color: var(--sklearn-color-fitted-level-1);\n",
"}\n",
"\n",
"/* On hover */\n",
"#sk-container-id-7 a.estimator_doc_link:hover {\n",
" /* unfitted */\n",
" background-color: var(--sklearn-color-unfitted-level-3);\n",
" color: var(--sklearn-color-background);\n",
" text-decoration: none;\n",
"}\n",
"\n",
"#sk-container-id-7 a.estimator_doc_link.fitted:hover {\n",
" /* fitted */\n",
" background-color: var(--sklearn-color-fitted-level-3);\n",
"}\n",
"</style><div id=\"sk-container-id-7\" class=\"sk-top-container\"><div class=\"sk-text-repr-fallback\"><pre>Pipeline(steps=[(&#x27;preprocessor&#x27;,\n",
" ColumnTransformer(transformers=[(&#x27;num&#x27;,\n",
" Pipeline(steps=[(&#x27;imputer&#x27;,\n",
" SimpleImputer(fill_value=0,\n",
" strategy=&#x27;constant&#x27;)),\n",
" (&#x27;scaler&#x27;,\n",
" StandardScaler())]),\n",
" [&#x27;nb_campaigns&#x27;,\n",
" &#x27;taux_ouverture_mail&#x27;,\n",
" &#x27;prop_purchases_internet&#x27;,\n",
" &#x27;nb_tickets&#x27;, &#x27;nb_purchases&#x27;,\n",
" &#x27;total_amount&#x27;,\n",
" &#x27;nb_suppliers&#x27;,\n",
" &#x27;purchases_10_2021&#x27;,\n",
" &#x27;purchases_10_2022&#x27;,\n",
" &#x27;purchases_...\n",
" &#x27;categorie_age_40_50&#x27;,\n",
" &#x27;categorie_age_50_60&#x27;,\n",
" &#x27;categorie_age_60_70&#x27;,\n",
" &#x27;categorie_age_70_80&#x27;,\n",
" &#x27;categorie_age_plus_80&#x27;,\n",
" &#x27;categorie_age_inconnue&#x27;,\n",
" &#x27;country_fr&#x27;,\n",
" &#x27;is_profession_known&#x27;,\n",
" &#x27;is_zipcode_known&#x27;,\n",
" &#x27;opt_in&#x27;])])),\n",
" (&#x27;LogisticRegression_Benchmark&#x27;,\n",
" LogisticRegression(class_weight={0.0: 0.5480249666729557,\n",
" 1.0: 5.705625684291879},\n",
" max_iter=5000, n_jobs=-1, solver=&#x27;saga&#x27;))])</pre><b>In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook. <br />On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.</b></div><div class=\"sk-container\" hidden><div class=\"sk-item sk-dashed-wrapped\"><div class=\"sk-label-container\"><div class=\"sk-label fitted sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-50\" type=\"checkbox\" ><label for=\"sk-estimator-id-50\" class=\"sk-toggleable__label fitted sk-toggleable__label-arrow fitted\">&nbsp;&nbsp;Pipeline<a class=\"sk-estimator-doc-link fitted\" rel=\"noreferrer\" target=\"_blank\" href=\"https://scikit-learn.org/1.4/modules/generated/sklearn.pipeline.Pipeline.html\">?<span>Documentation for Pipeline</span></a><span class=\"sk-estimator-doc-link fitted\">i<span>Fitted</span></span></label><div class=\"sk-toggleable__content fitted\"><pre>Pipeline(steps=[(&#x27;preprocessor&#x27;,\n",
" ColumnTransformer(transformers=[(&#x27;num&#x27;,\n",
" Pipeline(steps=[(&#x27;imputer&#x27;,\n",
" SimpleImputer(fill_value=0,\n",
" strategy=&#x27;constant&#x27;)),\n",
" (&#x27;scaler&#x27;,\n",
" StandardScaler())]),\n",
" [&#x27;nb_campaigns&#x27;,\n",
" &#x27;taux_ouverture_mail&#x27;,\n",
" &#x27;prop_purchases_internet&#x27;,\n",
" &#x27;nb_tickets&#x27;, &#x27;nb_purchases&#x27;,\n",
" &#x27;total_amount&#x27;,\n",
" &#x27;nb_suppliers&#x27;,\n",
" &#x27;purchases_10_2021&#x27;,\n",
" &#x27;purchases_10_2022&#x27;,\n",
" &#x27;purchases_...\n",
" &#x27;categorie_age_40_50&#x27;,\n",
" &#x27;categorie_age_50_60&#x27;,\n",
" &#x27;categorie_age_60_70&#x27;,\n",
" &#x27;categorie_age_70_80&#x27;,\n",
" &#x27;categorie_age_plus_80&#x27;,\n",
" &#x27;categorie_age_inconnue&#x27;,\n",
" &#x27;country_fr&#x27;,\n",
" &#x27;is_profession_known&#x27;,\n",
" &#x27;is_zipcode_known&#x27;,\n",
" &#x27;opt_in&#x27;])])),\n",
" (&#x27;LogisticRegression_Benchmark&#x27;,\n",
" LogisticRegression(class_weight={0.0: 0.5480249666729557,\n",
" 1.0: 5.705625684291879},\n",
" max_iter=5000, n_jobs=-1, solver=&#x27;saga&#x27;))])</pre></div> </div></div><div class=\"sk-serial\"><div class=\"sk-item sk-dashed-wrapped\"><div class=\"sk-label-container\"><div class=\"sk-label fitted sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-51\" type=\"checkbox\" ><label for=\"sk-estimator-id-51\" class=\"sk-toggleable__label fitted sk-toggleable__label-arrow fitted\">&nbsp;preprocessor: ColumnTransformer<a class=\"sk-estimator-doc-link fitted\" rel=\"noreferrer\" target=\"_blank\" href=\"https://scikit-learn.org/1.4/modules/generated/sklearn.compose.ColumnTransformer.html\">?<span>Documentation for preprocessor: ColumnTransformer</span></a></label><div class=\"sk-toggleable__content fitted\"><pre>ColumnTransformer(transformers=[(&#x27;num&#x27;,\n",
" Pipeline(steps=[(&#x27;imputer&#x27;,\n",
" SimpleImputer(fill_value=0,\n",
" strategy=&#x27;constant&#x27;)),\n",
" (&#x27;scaler&#x27;, StandardScaler())]),\n",
" [&#x27;nb_campaigns&#x27;, &#x27;taux_ouverture_mail&#x27;,\n",
" &#x27;prop_purchases_internet&#x27;, &#x27;nb_tickets&#x27;,\n",
" &#x27;nb_purchases&#x27;, &#x27;total_amount&#x27;,\n",
" &#x27;nb_suppliers&#x27;, &#x27;purchases_10_2021&#x27;,\n",
" &#x27;purchases_10_2022&#x27;, &#x27;purchases_11_2021&#x27;,\n",
" &#x27;purchases_12_2021&#x27;, &#x27;pu...\n",
" SimpleImputer(strategy=&#x27;most_frequent&#x27;))]),\n",
" [&#x27;gender_female&#x27;, &#x27;gender_male&#x27;,\n",
" &#x27;achat_internet&#x27;, &#x27;categorie_age_0_10&#x27;,\n",
" &#x27;categorie_age_10_20&#x27;, &#x27;categorie_age_20_30&#x27;,\n",
" &#x27;categorie_age_30_40&#x27;, &#x27;categorie_age_40_50&#x27;,\n",
" &#x27;categorie_age_50_60&#x27;, &#x27;categorie_age_60_70&#x27;,\n",
" &#x27;categorie_age_70_80&#x27;,\n",
" &#x27;categorie_age_plus_80&#x27;,\n",
" &#x27;categorie_age_inconnue&#x27;, &#x27;country_fr&#x27;,\n",
" &#x27;is_profession_known&#x27;, &#x27;is_zipcode_known&#x27;,\n",
" &#x27;opt_in&#x27;])])</pre></div> </div></div><div class=\"sk-parallel\"><div class=\"sk-parallel-item\"><div class=\"sk-item\"><div class=\"sk-label-container\"><div class=\"sk-label fitted sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-52\" type=\"checkbox\" ><label for=\"sk-estimator-id-52\" class=\"sk-toggleable__label fitted sk-toggleable__label-arrow fitted\">num</label><div class=\"sk-toggleable__content fitted\"><pre>[&#x27;nb_campaigns&#x27;, &#x27;taux_ouverture_mail&#x27;, &#x27;prop_purchases_internet&#x27;, &#x27;nb_tickets&#x27;, &#x27;nb_purchases&#x27;, &#x27;total_amount&#x27;, &#x27;nb_suppliers&#x27;, &#x27;purchases_10_2021&#x27;, &#x27;purchases_10_2022&#x27;, &#x27;purchases_11_2021&#x27;, &#x27;purchases_12_2021&#x27;, &#x27;purchases_1_2022&#x27;, &#x27;purchases_2_2022&#x27;, &#x27;purchases_3_2022&#x27;, &#x27;purchases_4_2022&#x27;, &#x27;purchases_5_2021&#x27;, &#x27;purchases_5_2022&#x27;, &#x27;purchases_6_2021&#x27;, &#x27;purchases_6_2022&#x27;, &#x27;purchases_7_2021&#x27;, &#x27;purchases_7_2022&#x27;, &#x27;purchases_8_2021&#x27;, &#x27;purchases_8_2022&#x27;, &#x27;purchases_9_2021&#x27;, &#x27;purchases_9_2022&#x27;, &#x27;purchase_date_min&#x27;, &#x27;purchase_date_max&#x27;, &#x27;nb_targets&#x27;, &#x27;time_to_open&#x27;]</pre></div> </div></div><div class=\"sk-serial\"><div class=\"sk-item\"><div class=\"sk-serial\"><div class=\"sk-item\"><div class=\"sk-estimator fitted sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-53\" type=\"checkbox\" ><label for=\"sk-estimator-id-53\" class=\"sk-toggleable__label fitted sk-toggleable__label-arrow fitted\">&nbsp;SimpleImputer<a class=\"sk-estimator-doc-link fitted\" rel=\"noreferrer\" target=\"_blank\" href=\"https://scikit-learn.org/1.4/modules/generated/sklearn.impute.SimpleImputer.html\">?<span>Documentation for SimpleImputer</span></a></label><div class=\"sk-toggleable__content fitted\"><pre>SimpleImputer(fill_value=0, strategy=&#x27;constant&#x27;)</pre></div> </div></div><div class=\"sk-item\"><div class=\"sk-estimator fitted sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-54\" type=\"checkbox\" ><label for=\"sk-estimator-id-54\" class=\"sk-toggleable__label fitted sk-toggleable__label-arrow fitted\">&nbsp;StandardScaler<a class=\"sk-estimator-doc-link fitted\" rel=\"noreferrer\" target=\"_blank\" href=\"https://scikit-learn.org/1.4/modules/generated/sklearn.preprocessing.StandardScaler.html\">?<span>Documentation for StandardScaler</span></a></label><div class=\"sk-toggleable__content fitted\"><pre>StandardScaler()</pre></div> </div></div></div></div></div></div></div><div class=\"sk-parallel-item\"><div class=\"sk-item\"><div class=\"sk-label-container\"><div class=\"sk-label fitted sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-55\" type=\"checkbox\" ><label for=\"sk-estimator-id-55\" class=\"sk-toggleable__label fitted sk-toggleable__label-arrow fitted\">bin</label><div class=\"sk-toggleable__content fitted\"><pre>[&#x27;gender_female&#x27;, &#x27;gender_male&#x27;, &#x27;achat_internet&#x27;, &#x27;categorie_age_0_10&#x27;, &#x27;categorie_age_10_20&#x27;, &#x27;categorie_age_20_30&#x27;, &#x27;categorie_age_30_40&#x27;, &#x27;categorie_age_40_50&#x27;, &#x27;categorie_age_50_60&#x27;, &#x27;categorie_age_60_70&#x27;, &#x27;categorie_age_70_80&#x27;, &#x27;categorie_age_plus_80&#x27;, &#x27;categorie_age_inconnue&#x27;, &#x27;country_fr&#x27;, &#x27;is_profession_known&#x27;, &#x27;is_zipcode_known&#x27;, &#x27;opt_in&#x27;]</pre></div> </div></div><div class=\"sk-serial\"><div class=\"sk-item\"><div class=\"sk-serial\"><div class=\"sk-item\"><div class=\"sk-estimator fitted sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-56\" type=\"checkbox\" ><label for=\"sk-estimator-id-56\" class=\"sk-toggleable__label fitted sk-toggleable__label-arrow fitted\">&nbsp;SimpleImputer<a class=\"sk-estimator-doc-link fitted\" rel=\"noreferrer\" target=\"_blank\" href=\"https://scikit-learn.org/1.4/modules/generated/sklearn.impute.SimpleImputer.html\">?<span>Documentation for SimpleImputer</span></a></label><div class=\"sk-toggleable__content fitted\"><pre>SimpleImputer(strategy=&#x27;most_frequent&#x27;)</pre></div> </div></div></div></div></div></div></div></div></div><div class=\"sk-item\"><div class=\"sk-estimator fitted sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-57\" type=\"checkbox\" ><label for=\"sk-estimator-id-57\" class=\"sk-toggleable__label fitted sk-toggleable__label-arrow fitted\">&nbsp;LogisticRegression<a class=\"sk-estimator-doc-link fitted\" rel=\"noreferrer\" target=\"_blank\" href=\"https://scikit-learn.org/1.4/modules/generated/sklearn.linear_model.LogisticRegression.html\">?<span>Documentation for LogisticRegression</span></a></label><div class=\"sk-toggleable__content fitted\"><pre>LogisticRegression(class_weight={0.0: 0.5480249666729557,\n",
" 1.0: 5.705625684291879},\n",
" max_iter=5000, n_jobs=-1, solver=&#x27;saga&#x27;)</pre></div> </div></div></div></div></div></div>"
],
"text/plain": [
"Pipeline(steps=[('preprocessor',\n",
" ColumnTransformer(transformers=[('num',\n",
" Pipeline(steps=[('imputer',\n",
" SimpleImputer(fill_value=0,\n",
" strategy='constant')),\n",
" ('scaler',\n",
" StandardScaler())]),\n",
" ['nb_campaigns',\n",
" 'taux_ouverture_mail',\n",
" 'prop_purchases_internet',\n",
" 'nb_tickets', 'nb_purchases',\n",
" 'total_amount',\n",
" 'nb_suppliers',\n",
" 'purchases_10_2021',\n",
" 'purchases_10_2022',\n",
" 'purchases_...\n",
" 'categorie_age_40_50',\n",
" 'categorie_age_50_60',\n",
" 'categorie_age_60_70',\n",
" 'categorie_age_70_80',\n",
" 'categorie_age_plus_80',\n",
" 'categorie_age_inconnue',\n",
" 'country_fr',\n",
" 'is_profession_known',\n",
" 'is_zipcode_known',\n",
" 'opt_in'])])),\n",
" ('LogisticRegression_Benchmark',\n",
" LogisticRegression(class_weight={0.0: 0.5480249666729557,\n",
" 1.0: 5.705625684291879},\n",
" max_iter=5000, n_jobs=-1, solver='saga'))])"
]
},
"execution_count": 92,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"model = load_model(type_of_activity, \"LogisticRegression_Benchmark\")\n",
"# model = load_model(type_of_activity, \"randomF_cv\")\n",
"model"
]
},
{
"cell_type": "markdown",
"id": "006819e7-e9c5-48d9-85ee-aa43d5e4c9c2",
"metadata": {},
"source": [
"## Quartile clustering"
]
},
{
"cell_type": "code",
"execution_count": 93,
"id": "018d8ff4-3436-4eec-8507-d1a265cbabf1",
"metadata": {},
"outputs": [],
"source": [
"y_pred = model.predict(X_test)\n",
"y_pred_prob = model.predict_proba(X_test)[:, 1]"
]
},
{
"cell_type": "code",
"execution_count": 94,
"id": "846f53b9-73c2-4a8b-9d9e-f11bf59ce9ba",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>customer_id</th>\n",
" <th>street_id</th>\n",
" <th>structure_id</th>\n",
" <th>mcp_contact_id</th>\n",
" <th>fidelity</th>\n",
" <th>tenant_id</th>\n",
" <th>is_partner</th>\n",
" <th>deleted_at</th>\n",
" <th>is_email_true</th>\n",
" <th>opt_in</th>\n",
" <th>...</th>\n",
" <th>purchases_8_2021</th>\n",
" <th>purchases_8_2022</th>\n",
" <th>purchases_9_2021</th>\n",
" <th>purchases_9_2022</th>\n",
" <th>y_has_purchased</th>\n",
" <th>has_purchased</th>\n",
" <th>has_purchased_estim</th>\n",
" <th>score</th>\n",
" <th>quartile</th>\n",
" <th>score_adjusted</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>10_699783</td>\n",
" <td>139</td>\n",
" <td>NaN</td>\n",
" <td>186852.0</td>\n",
" <td>0</td>\n",
" <td>875</td>\n",
" <td>False</td>\n",
" <td>NaN</td>\n",
" <td>True</td>\n",
" <td>0</td>\n",
" <td>...</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.131180</td>\n",
" <td>1</td>\n",
" <td>0.017574</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>10_38307</td>\n",
" <td>862</td>\n",
" <td>NaN</td>\n",
" <td>17621.0</td>\n",
" <td>7</td>\n",
" <td>875</td>\n",
" <td>False</td>\n",
" <td>NaN</td>\n",
" <td>True</td>\n",
" <td>0</td>\n",
" <td>...</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.321635</td>\n",
" <td>2</td>\n",
" <td>0.042466</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>10_556101</td>\n",
" <td>1063</td>\n",
" <td>NaN</td>\n",
" <td>136909.0</td>\n",
" <td>0</td>\n",
" <td>875</td>\n",
" <td>False</td>\n",
" <td>NaN</td>\n",
" <td>True</td>\n",
" <td>1</td>\n",
" <td>...</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.005068</td>\n",
" <td>1</td>\n",
" <td>0.000676</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>10_686663</td>\n",
" <td>443226</td>\n",
" <td>NaN</td>\n",
" <td>186611.0</td>\n",
" <td>1</td>\n",
" <td>875</td>\n",
" <td>False</td>\n",
" <td>NaN</td>\n",
" <td>True</td>\n",
" <td>1</td>\n",
" <td>...</td>\n",
" <td>0.0</td>\n",
" <td>1.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.166979</td>\n",
" <td>1</td>\n",
" <td>0.018397</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>10_91656</td>\n",
" <td>316684</td>\n",
" <td>NaN</td>\n",
" <td>21559.0</td>\n",
" <td>2</td>\n",
" <td>875</td>\n",
" <td>False</td>\n",
" <td>NaN</td>\n",
" <td>True</td>\n",
" <td>0</td>\n",
" <td>...</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.161523</td>\n",
" <td>1</td>\n",
" <td>0.018632</td>\n",
" </tr>\n",
" <tr>\n",
" <th>5</th>\n",
" <td>10_35956</td>\n",
" <td>106204</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>1</td>\n",
" <td>875</td>\n",
" <td>False</td>\n",
" <td>NaN</td>\n",
" <td>True</td>\n",
" <td>0</td>\n",
" <td>...</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.098139</td>\n",
" <td>1</td>\n",
" <td>0.010129</td>\n",
" </tr>\n",
" <tr>\n",
" <th>6</th>\n",
" <td>10_560058</td>\n",
" <td>1063</td>\n",
" <td>NaN</td>\n",
" <td>161812.0</td>\n",
" <td>0</td>\n",
" <td>875</td>\n",
" <td>False</td>\n",
" <td>NaN</td>\n",
" <td>True</td>\n",
" <td>1</td>\n",
" <td>...</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.005377</td>\n",
" <td>1</td>\n",
" <td>0.000715</td>\n",
" </tr>\n",
" <tr>\n",
" <th>7</th>\n",
" <td>10_38603</td>\n",
" <td>513642</td>\n",
" <td>1865.0</td>\n",
" <td>7660.0</td>\n",
" <td>4</td>\n",
" <td>875</td>\n",
" <td>False</td>\n",
" <td>NaN</td>\n",
" <td>True</td>\n",
" <td>1</td>\n",
" <td>...</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>1.0</td>\n",
" <td>1.0</td>\n",
" <td>1.0</td>\n",
" <td>0.906698</td>\n",
" <td>4</td>\n",
" <td>0.461388</td>\n",
" </tr>\n",
" <tr>\n",
" <th>8</th>\n",
" <td>10_563294</td>\n",
" <td>1063</td>\n",
" <td>NaN</td>\n",
" <td>167549.0</td>\n",
" <td>0</td>\n",
" <td>875</td>\n",
" <td>False</td>\n",
" <td>NaN</td>\n",
" <td>True</td>\n",
" <td>1</td>\n",
" <td>...</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.007399</td>\n",
" <td>1</td>\n",
" <td>0.000974</td>\n",
" </tr>\n",
" <tr>\n",
" <th>9</th>\n",
" <td>10_548983</td>\n",
" <td>268636</td>\n",
" <td>NaN</td>\n",
" <td>173318.0</td>\n",
" <td>1</td>\n",
" <td>875</td>\n",
" <td>False</td>\n",
" <td>NaN</td>\n",
" <td>True</td>\n",
" <td>0</td>\n",
" <td>...</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.163529</td>\n",
" <td>1</td>\n",
" <td>0.022102</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"<p>10 rows × 92 columns</p>\n",
"</div>"
],
"text/plain": [
" customer_id street_id structure_id mcp_contact_id fidelity tenant_id \\\n",
"0 10_699783 139 NaN 186852.0 0 875 \n",
"1 10_38307 862 NaN 17621.0 7 875 \n",
"2 10_556101 1063 NaN 136909.0 0 875 \n",
"3 10_686663 443226 NaN 186611.0 1 875 \n",
"4 10_91656 316684 NaN 21559.0 2 875 \n",
"5 10_35956 106204 NaN NaN 1 875 \n",
"6 10_560058 1063 NaN 161812.0 0 875 \n",
"7 10_38603 513642 1865.0 7660.0 4 875 \n",
"8 10_563294 1063 NaN 167549.0 0 875 \n",
"9 10_548983 268636 NaN 173318.0 1 875 \n",
"\n",
" is_partner deleted_at is_email_true opt_in ... purchases_8_2021 \\\n",
"0 False NaN True 0 ... 0.0 \n",
"1 False NaN True 0 ... 0.0 \n",
"2 False NaN True 1 ... 0.0 \n",
"3 False NaN True 1 ... 0.0 \n",
"4 False NaN True 0 ... 0.0 \n",
"5 False NaN True 0 ... 0.0 \n",
"6 False NaN True 1 ... 0.0 \n",
"7 False NaN True 1 ... 0.0 \n",
"8 False NaN True 1 ... 0.0 \n",
"9 False NaN True 0 ... 0.0 \n",
"\n",
" purchases_8_2022 purchases_9_2021 purchases_9_2022 y_has_purchased \\\n",
"0 0.0 0.0 0.0 0.0 \n",
"1 0.0 0.0 0.0 0.0 \n",
"2 0.0 0.0 0.0 0.0 \n",
"3 1.0 0.0 0.0 0.0 \n",
"4 0.0 0.0 0.0 0.0 \n",
"5 0.0 0.0 0.0 0.0 \n",
"6 0.0 0.0 0.0 0.0 \n",
"7 0.0 0.0 0.0 1.0 \n",
"8 0.0 0.0 0.0 0.0 \n",
"9 0.0 0.0 0.0 0.0 \n",
"\n",
" has_purchased has_purchased_estim score quartile score_adjusted \n",
"0 0.0 0.0 0.131180 1 0.017574 \n",
"1 0.0 0.0 0.321635 2 0.042466 \n",
"2 0.0 0.0 0.005068 1 0.000676 \n",
"3 0.0 0.0 0.166979 1 0.018397 \n",
"4 0.0 0.0 0.161523 1 0.018632 \n",
"5 0.0 0.0 0.098139 1 0.010129 \n",
"6 0.0 0.0 0.005377 1 0.000715 \n",
"7 1.0 1.0 0.906698 4 0.461388 \n",
"8 0.0 0.0 0.007399 1 0.000974 \n",
"9 0.0 0.0 0.163529 1 0.022102 \n",
"\n",
"[10 rows x 92 columns]"
]
},
"execution_count": 94,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"X_test_segment = X_test\n",
"\n",
"X_test_segment[\"has_purchased\"] = y_test\n",
"X_test_segment[\"has_purchased_estim\"] = y_pred\n",
"X_test_segment[\"score\"] = y_pred_prob\n",
"X_test_segment[\"quartile\"] = np.where(X_test['score']<0.25, '1',\n",
" np.where(X_test['score']<0.5, '2',\n",
" np.where(X_test['score']<0.75, '3', '4')))\n",
"X_test_segment.head(10)"
]
},
{
"cell_type": "code",
"execution_count": 24,
"id": "fb592fe3-ea40-4e83-8fe9-c52b9ee42f2a",
"metadata": {},
"outputs": [],
"source": [
"def df_segment(df, y, model) :\n",
"\n",
" y_pred = model.predict(df)\n",
" y_pred_prob = model.predict_proba(df)[:, 1]\n",
"\n",
" df_segment = df\n",
"\n",
" df_segment[\"has_purchased\"] = y\n",
" df_segment[\"has_purchased_estim\"] = y_pred\n",
" df_segment[\"score\"] = y_pred_prob\n",
" df_segment[\"quartile\"] = np.where(df_segment['score']<0.25, '1',\n",
" np.where(df_segment['score']<0.5, '2',\n",
" np.where(df_segment['score']<0.75, '3', '4')))\n",
"\n",
" return df_segment"
]
},
{
"cell_type": "code",
"execution_count": 88,
"id": "968645d5-58cc-485a-bd8b-99f4cfc26fec",
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"/tmp/ipykernel_1080/2624515794.py:8: SettingWithCopyWarning: \n",
"A value is trying to be set on a copy of a slice from a DataFrame.\n",
"Try using .loc[row_indexer,col_indexer] = value instead\n",
"\n",
"See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n",
" df_segment[\"has_purchased\"] = y\n",
"/tmp/ipykernel_1080/2624515794.py:9: SettingWithCopyWarning: \n",
"A value is trying to be set on a copy of a slice from a DataFrame.\n",
"Try using .loc[row_indexer,col_indexer] = value instead\n",
"\n",
"See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n",
" df_segment[\"has_purchased_estim\"] = y_pred\n",
"/tmp/ipykernel_1080/2624515794.py:10: SettingWithCopyWarning: \n",
"A value is trying to be set on a copy of a slice from a DataFrame.\n",
"Try using .loc[row_indexer,col_indexer] = value instead\n",
"\n",
"See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n",
" df_segment[\"score\"] = y_pred_prob\n",
"/tmp/ipykernel_1080/2624515794.py:11: SettingWithCopyWarning: \n",
"A value is trying to be set on a copy of a slice from a DataFrame.\n",
"Try using .loc[row_indexer,col_indexer] = value instead\n",
"\n",
"See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n",
" df_segment[\"quartile\"] = np.where(df_segment['score']<0.25, '1',\n"
]
},
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>nb_tickets</th>\n",
" <th>nb_purchases</th>\n",
" <th>total_amount</th>\n",
" <th>nb_suppliers</th>\n",
" <th>vente_internet_max</th>\n",
" <th>purchase_date_min</th>\n",
" <th>purchase_date_max</th>\n",
" <th>time_between_purchase</th>\n",
" <th>nb_tickets_internet</th>\n",
" <th>fidelity</th>\n",
" <th>...</th>\n",
" <th>opt_in</th>\n",
" <th>gender_female</th>\n",
" <th>gender_male</th>\n",
" <th>gender_other</th>\n",
" <th>nb_campaigns</th>\n",
" <th>nb_campaigns_opened</th>\n",
" <th>has_purchased</th>\n",
" <th>has_purchased_estim</th>\n",
" <th>score</th>\n",
" <th>quartile</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>4.0</td>\n",
" <td>1.0</td>\n",
" <td>100.00</td>\n",
" <td>1.0</td>\n",
" <td>0.0</td>\n",
" <td>5.177187</td>\n",
" <td>5.177187</td>\n",
" <td>0.000000</td>\n",
" <td>0.0</td>\n",
" <td>1</td>\n",
" <td>...</td>\n",
" <td>False</td>\n",
" <td>1</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.006066</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>1.0</td>\n",
" <td>1.0</td>\n",
" <td>55.00</td>\n",
" <td>1.0</td>\n",
" <td>0.0</td>\n",
" <td>426.265613</td>\n",
" <td>426.265613</td>\n",
" <td>0.000000</td>\n",
" <td>0.0</td>\n",
" <td>2</td>\n",
" <td>...</td>\n",
" <td>True</td>\n",
" <td>0</td>\n",
" <td>1</td>\n",
" <td>0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>1.0</td>\n",
" <td>0.0</td>\n",
" <td>0.288847</td>\n",
" <td>2</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>17.0</td>\n",
" <td>1.0</td>\n",
" <td>80.00</td>\n",
" <td>1.0</td>\n",
" <td>0.0</td>\n",
" <td>436.033437</td>\n",
" <td>436.033437</td>\n",
" <td>0.000000</td>\n",
" <td>0.0</td>\n",
" <td>2</td>\n",
" <td>...</td>\n",
" <td>True</td>\n",
" <td>1</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.103264</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>4.0</td>\n",
" <td>1.0</td>\n",
" <td>120.00</td>\n",
" <td>1.0</td>\n",
" <td>0.0</td>\n",
" <td>5.196412</td>\n",
" <td>5.196412</td>\n",
" <td>0.000000</td>\n",
" <td>0.0</td>\n",
" <td>1</td>\n",
" <td>...</td>\n",
" <td>False</td>\n",
" <td>1</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.008928</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>34.0</td>\n",
" <td>2.0</td>\n",
" <td>416.00</td>\n",
" <td>1.0</td>\n",
" <td>0.0</td>\n",
" <td>478.693148</td>\n",
" <td>115.631470</td>\n",
" <td>363.061678</td>\n",
" <td>0.0</td>\n",
" <td>4</td>\n",
" <td>...</td>\n",
" <td>False</td>\n",
" <td>1</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>1.0</td>\n",
" <td>1.0</td>\n",
" <td>0.992809</td>\n",
" <td>4</td>\n",
" </tr>\n",
" <tr>\n",
" <th>...</th>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" </tr>\n",
" <tr>\n",
" <th>96091</th>\n",
" <td>1.0</td>\n",
" <td>1.0</td>\n",
" <td>67.31</td>\n",
" <td>1.0</td>\n",
" <td>1.0</td>\n",
" <td>278.442257</td>\n",
" <td>278.442257</td>\n",
" <td>0.000000</td>\n",
" <td>1.0</td>\n",
" <td>2</td>\n",
" <td>...</td>\n",
" <td>False</td>\n",
" <td>0</td>\n",
" <td>1</td>\n",
" <td>0</td>\n",
" <td>15.0</td>\n",
" <td>5.0</td>\n",
" <td>1.0</td>\n",
" <td>0.0</td>\n",
" <td>0.351762</td>\n",
" <td>2</td>\n",
" </tr>\n",
" <tr>\n",
" <th>96092</th>\n",
" <td>1.0</td>\n",
" <td>1.0</td>\n",
" <td>61.41</td>\n",
" <td>1.0</td>\n",
" <td>1.0</td>\n",
" <td>189.207373</td>\n",
" <td>189.207373</td>\n",
" <td>0.000000</td>\n",
" <td>1.0</td>\n",
" <td>1</td>\n",
" <td>...</td>\n",
" <td>False</td>\n",
" <td>0</td>\n",
" <td>1</td>\n",
" <td>0</td>\n",
" <td>12.0</td>\n",
" <td>9.0</td>\n",
" <td>0.0</td>\n",
" <td>1.0</td>\n",
" <td>0.567814</td>\n",
" <td>3</td>\n",
" </tr>\n",
" <tr>\n",
" <th>96093</th>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.00</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>550.000000</td>\n",
" <td>550.000000</td>\n",
" <td>-1.000000</td>\n",
" <td>0.0</td>\n",
" <td>1</td>\n",
" <td>...</td>\n",
" <td>True</td>\n",
" <td>1</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>29.0</td>\n",
" <td>3.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.004652</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>96094</th>\n",
" <td>1.0</td>\n",
" <td>1.0</td>\n",
" <td>79.43</td>\n",
" <td>1.0</td>\n",
" <td>1.0</td>\n",
" <td>279.312905</td>\n",
" <td>279.312905</td>\n",
" <td>0.000000</td>\n",
" <td>1.0</td>\n",
" <td>1</td>\n",
" <td>...</td>\n",
" <td>False</td>\n",
" <td>0</td>\n",
" <td>1</td>\n",
" <td>0</td>\n",
" <td>20.0</td>\n",
" <td>4.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.293042</td>\n",
" <td>2</td>\n",
" </tr>\n",
" <tr>\n",
" <th>96095</th>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.00</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>550.000000</td>\n",
" <td>550.000000</td>\n",
" <td>-1.000000</td>\n",
" <td>0.0</td>\n",
" <td>2</td>\n",
" <td>...</td>\n",
" <td>False</td>\n",
" <td>0</td>\n",
" <td>1</td>\n",
" <td>0</td>\n",
" <td>31.0</td>\n",
" <td>4.0</td>\n",
" <td>0.0</td>\n",
" <td>1.0</td>\n",
" <td>0.787852</td>\n",
" <td>4</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"<p>96096 rows × 21 columns</p>\n",
"</div>"
],
"text/plain": [
" nb_tickets nb_purchases total_amount nb_suppliers \\\n",
"0 4.0 1.0 100.00 1.0 \n",
"1 1.0 1.0 55.00 1.0 \n",
"2 17.0 1.0 80.00 1.0 \n",
"3 4.0 1.0 120.00 1.0 \n",
"4 34.0 2.0 416.00 1.0 \n",
"... ... ... ... ... \n",
"96091 1.0 1.0 67.31 1.0 \n",
"96092 1.0 1.0 61.41 1.0 \n",
"96093 0.0 0.0 0.00 0.0 \n",
"96094 1.0 1.0 79.43 1.0 \n",
"96095 0.0 0.0 0.00 0.0 \n",
"\n",
" vente_internet_max purchase_date_min purchase_date_max \\\n",
"0 0.0 5.177187 5.177187 \n",
"1 0.0 426.265613 426.265613 \n",
"2 0.0 436.033437 436.033437 \n",
"3 0.0 5.196412 5.196412 \n",
"4 0.0 478.693148 115.631470 \n",
"... ... ... ... \n",
"96091 1.0 278.442257 278.442257 \n",
"96092 1.0 189.207373 189.207373 \n",
"96093 0.0 550.000000 550.000000 \n",
"96094 1.0 279.312905 279.312905 \n",
"96095 0.0 550.000000 550.000000 \n",
"\n",
" time_between_purchase nb_tickets_internet fidelity ... opt_in \\\n",
"0 0.000000 0.0 1 ... False \n",
"1 0.000000 0.0 2 ... True \n",
"2 0.000000 0.0 2 ... True \n",
"3 0.000000 0.0 1 ... False \n",
"4 363.061678 0.0 4 ... False \n",
"... ... ... ... ... ... \n",
"96091 0.000000 1.0 2 ... False \n",
"96092 0.000000 1.0 1 ... False \n",
"96093 -1.000000 0.0 1 ... True \n",
"96094 0.000000 1.0 1 ... False \n",
"96095 -1.000000 0.0 2 ... False \n",
"\n",
" gender_female gender_male gender_other nb_campaigns \\\n",
"0 1 0 0 0.0 \n",
"1 0 1 0 0.0 \n",
"2 1 0 0 0.0 \n",
"3 1 0 0 0.0 \n",
"4 1 0 0 0.0 \n",
"... ... ... ... ... \n",
"96091 0 1 0 15.0 \n",
"96092 0 1 0 12.0 \n",
"96093 1 0 0 29.0 \n",
"96094 0 1 0 20.0 \n",
"96095 0 1 0 31.0 \n",
"\n",
" nb_campaigns_opened has_purchased has_purchased_estim score \\\n",
"0 0.0 0.0 0.0 0.006066 \n",
"1 0.0 1.0 0.0 0.288847 \n",
"2 0.0 0.0 0.0 0.103264 \n",
"3 0.0 0.0 0.0 0.008928 \n",
"4 0.0 1.0 1.0 0.992809 \n",
"... ... ... ... ... \n",
"96091 5.0 1.0 0.0 0.351762 \n",
"96092 9.0 0.0 1.0 0.567814 \n",
"96093 3.0 0.0 0.0 0.004652 \n",
"96094 4.0 0.0 0.0 0.293042 \n",
"96095 4.0 0.0 1.0 0.787852 \n",
"\n",
" quartile \n",
"0 1 \n",
"1 2 \n",
"2 1 \n",
"3 1 \n",
"4 4 \n",
"... ... \n",
"96091 2 \n",
"96092 3 \n",
"96093 1 \n",
"96094 2 \n",
"96095 4 \n",
"\n",
"[96096 rows x 21 columns]"
]
},
"execution_count": 88,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"df_segment(X_test, y_test, model)"
]
},
{
"cell_type": "markdown",
"id": "ad16b8ab-7e01-404b-971e-866e9b9d5aa4",
"metadata": {},
"source": [
"## definition of functions to compute the bias of scores and adjust it \n",
"\n",
"Le biais est calculé de la façon suivante. \n",
"En notant $\\hat{p(x_i)}$ le score calculé (estimé par la modélisation) et $p(x_i)$ le vrai score (sans biais), et $\\beta$ le logarithme du biais, on a : \\\n",
"$\\ln{\\frac{\\hat{p(x_i)}}{1-\\hat{p(x_i)}}} = \\beta + \\ln{\\frac{p(x_i)}{1-p(x_i)}}$ \\\n",
"$ \\frac{\\hat{p(x_i)}}{1-\\hat{p(x_i)}} = \\exp(\\beta) . \\frac{p(x_i)}{1-p(x_i)} $ , soit : \\\n",
"$p(x_i) = {\\frac{\\frac{\\hat{p(x_i)}}{1-\\hat{p(x_i)}}}{B+\\frac{\\hat{p(x_i)}}{1-\\hat{p(x_i)}}}}$ \\\n",
"Ce qu'on appelle biais et qu'on estime dans le code par la suite est : $B=\\exp(\\beta) $. Les probabilités ne sont donc pas biaisées si $B=1$. Il y a surestimation si $B>1$. \n",
"\n",
"On cherche le B qui permette d'ajuster les probabilités de telle sorte que la somme des scores soit égale à la somme des y_has_purchased. Cela revient à résoudre : \n",
"\n",
"\\begin{equation}\n",
"\\sum_{i}{\\frac{\\frac{\\hat{p(x_i)}}{1-\\hat{p(x_i)}}}{B+\\frac{\\hat{p(x_i)}}{1-\\hat{p(x_i)}}}} = \\sum_{i}{Y_i}\n",
"\\end{equation}\n",
"\n",
"C'est ce que fait la fonction find_bias. \n",
"\n",
"Note sur les notations : \\\n",
"$\\hat{p(x_i)}$ correspond à ce qu'on appelle le score et $p(x_i)$ à ce qu'on appellera le score adjusted"
]
},
{
"cell_type": "code",
"execution_count": 25,
"id": "f0379536-a6c5-4b16-bde5-d0319ec1b140",
"metadata": {},
"outputs": [],
"source": [
"# compute adjusted score from odd ratios (cf formula above)\n",
"def adjusted_score(odd_ratio, bias) :\n",
" adjusted_score = odd_ratio/(bias+odd_ratio)\n",
" return adjusted_score"
]
},
{
"cell_type": "code",
"execution_count": 26,
"id": "32a0dfd0-f49d-4785-a56f-706d381bfe41",
"metadata": {},
"outputs": [],
"source": [
"# when the score is 1 we cannot compute the odd ratio, so we adjust scores equal to 1\n",
"# we set the second best score instead\n",
"\n",
"def adjust_score_1(score) :\n",
" second_best_score = np.array([element for element in score if element !=1]).max()\n",
" new_score = np.array([element if element!=1 else second_best_score for element in score]) \n",
" return new_score"
]
},
{
"cell_type": "code",
"execution_count": 27,
"id": "2dff1def-02df-413e-afce-b4aeaf7752b6",
"metadata": {},
"outputs": [],
"source": [
"def odd_ratio(score) :\n",
" return score / (1 - score)"
]
},
{
"cell_type": "code",
"execution_count": 28,
"id": "683d71fc-7442-4028-869c-49c57592d6e9",
"metadata": {},
"outputs": [],
"source": [
"# definition of a function that automatically detects the bias\n",
"\n",
"def find_bias(odd_ratios, y_objective, initial_guess=6) :\n",
" \"\"\"\n",
" results = minimize(lambda bias : (sum([adjusted_score(element, bias) for element in list(odd_ratios)]) - y_objective)**2 ,\n",
" initial_guess , method = \"BFGS\")\n",
"\n",
" estimated_bias = results.x[0]\n",
" \"\"\"\n",
"\n",
" # faster method\n",
" bias_estimated = fsolve(lambda bias : sum([adjusted_score(element, bias) for element in list(odd_ratios)]) - y_objective, x0=6)\n",
" \n",
" return bias_estimated[0]"
]
},
{
"cell_type": "code",
"execution_count": 95,
"id": "f17dc6ca-7a48-441b-8c04-11c47b8b9741",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"0.3000275047453295 0.08797424180570736\n"
]
},
{
"data": {
"text/plain": [
"0.08763280798047211"
]
},
"execution_count": 95,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"print(X_test_segment[\"score\"].mean(), y_test[\"y_has_purchased\"].mean())\n",
"y_train[\"y_has_purchased\"].mean()"
]
},
{
"cell_type": "code",
"execution_count": 96,
"id": "781b0d40-c954-4c54-830a-e709c8667328",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"10.698758485840244"
]
},
"execution_count": 96,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# computation with the function defined\n",
"\n",
"bias_test_set = find_bias(odd_ratios = odd_ratio(adjust_score_1(X_test_segment[\"score\"])), \n",
" y_objective = y_test[\"y_has_purchased\"].sum(),\n",
" initial_guess=6)\n",
"bias_test_set"
]
},
{
"cell_type": "code",
"execution_count": 97,
"id": "248cb862-418e-4767-9933-70c4885ecf40",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"10.688693734338177"
]
},
"execution_count": 97,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# comparison with bias of the train set\n",
"X_train_score = model.predict_proba(X_train)[:, 1]\n",
"\n",
"bias_train_set = find_bias(odd_ratios = odd_ratio(adjust_score_1(X_train_score)), \n",
" y_objective = y_train[\"y_has_purchased\"].sum(),\n",
" initial_guess=10)\n",
"bias_train_set"
]
},
{
"cell_type": "code",
"execution_count": 98,
"id": "fff6cbe6-7bb3-4732-9b81-b9ac5383bbcf",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"betâ test - betâ train = 0.00094118290869078\n"
]
}
],
"source": [
"print(\"betâ test - betâ train = \",np.log(bias_test_set/bias_train_set))"
]
},
{
"cell_type": "code",
"execution_count": 99,
"id": "f506870d-4a8a-4b2c-8f0b-e0789080b20c",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"mean absolute erreur 4.674943825828751e-05\n"
]
}
],
"source": [
"# impact of considering a bias computed on train set instead of test set - totally neglectable\n",
"\n",
"score_adjusted_test = adjusted_score(odd_ratio(adjust_score_1(X_test_segment[\"score\"])), bias = bias_test_set)\n",
"score_adjusted_train = adjusted_score(odd_ratio(adjust_score_1(X_test_segment[\"score\"])), bias = bias_train_set)\n",
"\n",
"print(\"mean absolute erreur\",abs(score_adjusted_test-score_adjusted_train).mean())"
]
},
{
"cell_type": "code",
"execution_count": 100,
"id": "8213d0e4-063b-49fa-90b7-677fc34f4c01",
"metadata": {},
"outputs": [],
"source": [
"# adjust scores accordingly \n",
"\n",
"# X_test_segment[\"score_adjusted\"] = adjusted_score(odd_ratio(adjust_score_1(X_test_segment[\"score\"])), bias = bias_test_set)\n",
"\n",
"# actually, we are not supposed to have X_test, so the biais is estimated on X_train\n",
"# X_test_segment[\"score_adjusted\"] = adjusted_score(odd_ratio(adjust_score_1(X_test_segment[\"score\"])), bias = bias_train_set)\n",
"X_test_segment[\"score_adjusted\"] = score_adjusted_train"
]
},
{
"cell_type": "code",
"execution_count": 101,
"id": "834d3723-2e72-4c65-9c62-e2d595c69461",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"MSE for score : 0.12309116071575532\n",
"MSE for ajusted score : 0.05482346713233594\n",
"sum of y_has_purchased : 13361.0\n",
"sum of adjusted scores : 13368.100024185826\n"
]
}
],
"source": [
"# check \n",
"\n",
"MSE_score = ((X_test_segment[\"score\"]-X_test_segment[\"has_purchased\"])**2).mean()\n",
"MSE_ajusted_score = ((X_test_segment[\"score_adjusted\"]-X_test_segment[\"has_purchased\"])**2).mean()\n",
"print(f\"MSE for score : {MSE_score}\")\n",
"print(f\"MSE for ajusted score : {MSE_ajusted_score}\")\n",
"\n",
"print(\"sum of y_has_purchased :\",y_test[\"y_has_purchased\"].sum())\n",
"print(\"sum of adjusted scores :\", X_test_segment[\"score_adjusted\"].sum())"
]
},
{
"cell_type": "code",
"execution_count": 102,
"id": "9f30a4dd-a9d8-405a-a7d5-5324ae88cf70",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"MAE for score : 0.25695361997840177\n",
"MAE for adjusted score : 0.10450649550597542\n"
]
}
],
"source": [
"# mean absolute error - divided by 2 with out method\n",
"\n",
"MAE_score = abs(X_test_segment[\"score\"]-X_test_segment[\"has_purchased\"]).mean()\n",
"MAE_ajusted_score = abs(X_test_segment[\"score_adjusted\"]-X_test_segment[\"has_purchased\"]).mean()\n",
"print(f\"MAE for score : {MAE_score}\")\n",
"print(f\"MAE for adjusted score : {MAE_ajusted_score}\")"
]
},
{
"cell_type": "code",
"execution_count": 37,
"id": "6f9396db-e213-408c-a596-eaeec3bc79f3",
"metadata": {},
"outputs": [],
"source": [
"# visualization\n",
"\n",
"# histogramme des probas et des probas ajustées\n",
"\n",
"def plot_hist_scores(df, score, score_adjusted, type_of_activity) :\n",
"\n",
" plt.figure()\n",
" plt.hist(df[score], label = \"score\", alpha=0.6)\n",
" plt.hist(df[score_adjusted], label=\"adjusted score\", alpha=0.6)\n",
" plt.legend()\n",
" plt.xlabel(\"probability of a future purchase\")\n",
" plt.ylabel(\"count\")\n",
" plt.title(f\"Comparison between score and adjusted score for {type_of_activity} companies\")\n",
" # plt.show()"
]
},
{
"cell_type": "code",
"execution_count": 64,
"id": "def64c16-f4dd-493c-909c-d886d7f53947",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'projet-bdc2324-team1/Output_expected_CA/sport/hist_score_adjustedsport.png'"
]
},
"execution_count": 64,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"PATH + file_name + type_of_activity + \".png\""
]
},
{
"cell_type": "code",
"execution_count": 103,
"id": "b478d40d-9677-4204-87bd-16fb0bc1fe9a",
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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J+jq6iIoKKjC+7tgwQLcvHkTr732GkxNTWXzxowZg+zsbIwePfq56/niiy/w7rvvwtPTEx06dMAff/yB33//HYaGhmXOxdTUFJs3b8bPP/8MOzs7fPHFF1i2bFm59wkAli9fDhsbG3Tv3h0eHh6YPn069PT0pPkaGhpYvXo1vvnmG1hZWeGtt94CAIwePRojR47EiBEj4OLigqZNm6Jnz57P3V6bNm0QHh6OGzduoFu3bmjfvj3mzp0LS0vLUpd7/fXX8c4772DAgAFwdXWFvb09vv76a2l+RfNp0aIFQkNDcf78eXTq1AlOTk749ddfoaFRtsfpluV4zMvLw8SJE9GqVSv069cPtra2Uu5WVlY4ceIE8vLy4ObmBnt7e0yZMgVKpRJqavwoJCqkEGX94hERVbsePXqgXbt21fZLILXd9u3bMWXKFNy5c4cPTK4CAQEB2L17N399hegVxl8+IaJa7/Hjx4iPj0dgYCDGjRvHopCIqIpw/JyIar0lS5agXbt2MDc3x5w5c2o6HSKilxYvJRMRERERAI4YEhEREVEBFoZEREREBICFIREREREVYGFIRERERABYGBIRERFRARaGRERERASAhSERERERFWBhSEREREQAWBgSERERUYH/AxTvctmi4E3PAAAAAElFTkSuQmCC",
"text/plain": [
"<Figure size 640x480 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"plot_hist_scores(X_test_segment, score = \"score\", score_adjusted = \"score_adjusted\", type_of_activity = type_of_activity)"
]
},
{
"cell_type": "code",
"execution_count": 40,
"id": "add631d7-0757-45a5-bb5b-f7f4b4baa961",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"projet-bdc2324-team1/Output_expected_CA/sport/\n"
]
}
],
"source": [
"# define path so save graphics\n",
"\n",
"# define type of activity \n",
"type_of_activity = \"sport\"\n",
"PATH = f\"projet-bdc2324-team1/Output_expected_CA/{type_of_activity}/\"\n",
"print(PATH)"
]
},
{
"cell_type": "code",
"execution_count": 68,
"id": "3a5b5bd9-e033-4436-8c56-bf5fb61df87f",
"metadata": {},
"outputs": [],
"source": [
"# export png \n",
"\n",
"# plot adjusted scores and save (to be tested)\n",
"plot_hist_scores(X_test_segment, score = \"score\", score_adjusted = \"score_adjusted\", type_of_activity = type_of_activity)\n",
"\n",
"image_buffer = io.BytesIO()\n",
"plt.savefig(image_buffer, format='png')\n",
"image_buffer.seek(0)\n",
"file_name = \"hist_score_adjusted_\"\n",
"FILE_PATH_OUT_S3 = PATH + file_name + type_of_activity + \".png\"\n",
"with fs.open(FILE_PATH_OUT_S3, 'wb') as s3_file:\n",
" s3_file.write(image_buffer.read())\n",
"plt.close()"
]
},
{
"cell_type": "markdown",
"id": "e6fae260-fab8-4f51-90dc-9b6d7314c77b",
"metadata": {},
"source": [
"## Compute number of tickets and CA by segment with the recalibrated score"
]
},
{
"cell_type": "code",
"execution_count": 104,
"id": "90c4c2b5-0ede-4001-889f-749cfbd9df04",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>quartile</th>\n",
" <th>score (%)</th>\n",
" <th>score adjusted (%)</th>\n",
" <th>has purchased (%)</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>1</td>\n",
" <td>8.80</td>\n",
" <td>0.94</td>\n",
" <td>1.02</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>2</td>\n",
" <td>36.16</td>\n",
" <td>5.17</td>\n",
" <td>4.70</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>3</td>\n",
" <td>61.06</td>\n",
" <td>13.33</td>\n",
" <td>14.62</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>4</td>\n",
" <td>89.86</td>\n",
" <td>53.74</td>\n",
" <td>53.19</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" quartile score (%) score adjusted (%) has purchased (%)\n",
"0 1 8.80 0.94 1.02\n",
"1 2 36.16 5.17 4.70\n",
"2 3 61.06 13.33 14.62\n",
"3 4 89.86 53.74 53.19"
]
},
"execution_count": 104,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"X_test_table_adjusted_scores = (100 * X_test_segment.groupby(\"quartile\")[[\"score\",\"score_adjusted\", \"has_purchased\"]].mean()).round(2).reset_index()\n",
"X_test_table_adjusted_scores = X_test_table_adjusted_scores.rename(columns = {col : f\"{col.replace('_', ' ')} (%)\" for col in X_test_table_adjusted_scores.columns if col in [\"score\",\"score_adjusted\", \"has_purchased\"]})\n",
"X_test_table_adjusted_scores"
]
},
{
"cell_type": "code",
"execution_count": 162,
"id": "d0b8740c-cf48-4a3e-83cb-23d95059f62f",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'\\\\begin{tabular}{lrrr}\\n\\\\toprule\\nquartile & score (%) & score adjusted (%) & has purchased (%) \\\\\\\\\\n\\\\midrule\\n1 & 13.250000 & 2.510000 & 1.570000 \\\\\\\\\\n2 & 33.890000 & 8.000000 & 9.850000 \\\\\\\\\\n3 & 63.060000 & 22.580000 & 21.470000 \\\\\\\\\\n4 & 90.520000 & 66.200000 & 65.010000 \\\\\\\\\\n\\\\bottomrule\\n\\\\end{tabular}\\n'"
]
},
"execution_count": 162,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"X_test_table_adjusted_scores.to_latex(index=False)"
]
},
{
"cell_type": "code",
"execution_count": 43,
"id": "d6a04d3e-c454-43e4-ae4c-0746e928575b",
"metadata": {},
"outputs": [],
"source": [
"# comparison between score and adjusted score - export csv associated\n",
"\n",
"file_name = \"table_adjusted_score_\"\n",
"FILE_PATH_OUT_S3 = PATH + file_name + type_of_activity + \".csv\"\n",
"with fs.open(FILE_PATH_OUT_S3, 'w') as file_out:\n",
" X_test_table_adjusted_scores.to_csv(file_out, index = False)"
]
},
{
"cell_type": "code",
"execution_count": 40,
"id": "a974589f-7952-4db2-bebf-7b69c6b09372",
"metadata": {},
"outputs": [],
"source": [
"def project_tickets_CA (df, nb_purchases, nb_tickets, total_amount, score_adjusted, duration_ref, duration_projection) :\n",
" \n",
" duration_ratio = duration_ref/duration_projection\n",
"\n",
" df_output = df\n",
"\n",
" df_output.loc[:,\"nb_tickets_projected\"] = df_output.loc[:,nb_tickets] / duration_ratio\n",
" df_output.loc[:,\"total_amount_projected\"] = df_output.loc[:,total_amount] / duration_ratio\n",
" \n",
" df_output.loc[:,\"nb_tickets_expected\"] = df_output.loc[:,score_adjusted] * df_output.loc[:,\"nb_tickets_projected\"]\n",
" df_output.loc[:,\"total_amount_expected\"] = df_output.loc[:,score_adjusted] * df_output.loc[:,\"total_amount_projected\"]\n",
"\n",
" df_output.loc[:,\"pace_purchase\"] = (duration_ref/df_output.loc[:,nb_purchases]).apply(lambda x : np.nan if x==np.inf else x)\n",
" \n",
" return df_output\n"
]
},
{
"cell_type": "code",
"execution_count": 41,
"id": "dd8a52e1-d06e-4790-8687-8e58e3e6b84e",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>customer_id</th>\n",
" <th>street_id</th>\n",
" <th>structure_id</th>\n",
" <th>mcp_contact_id</th>\n",
" <th>fidelity</th>\n",
" <th>tenant_id</th>\n",
" <th>is_partner</th>\n",
" <th>deleted_at</th>\n",
" <th>is_email_true</th>\n",
" <th>opt_in</th>\n",
" <th>...</th>\n",
" <th>has_purchased</th>\n",
" <th>has_purchased_estim</th>\n",
" <th>score</th>\n",
" <th>quartile</th>\n",
" <th>score_adjusted</th>\n",
" <th>nb_tickets_projected</th>\n",
" <th>total_amount_projected</th>\n",
" <th>nb_tickets_expected</th>\n",
" <th>total_amount_expected</th>\n",
" <th>pace_purchase</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>1_8191</td>\n",
" <td>8114</td>\n",
" <td>NaN</td>\n",
" <td>834.0</td>\n",
" <td>0</td>\n",
" <td>1311</td>\n",
" <td>False</td>\n",
" <td>NaN</td>\n",
" <td>True</td>\n",
" <td>1</td>\n",
" <td>...</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.408546</td>\n",
" <td>2</td>\n",
" <td>0.027066</td>\n",
" <td>0.000000</td>\n",
" <td>0.000000</td>\n",
" <td>0.000000</td>\n",
" <td>0.000000</td>\n",
" <td>NaN</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>1_14792</td>\n",
" <td>2</td>\n",
" <td>NaN</td>\n",
" <td>251178.0</td>\n",
" <td>0</td>\n",
" <td>1311</td>\n",
" <td>False</td>\n",
" <td>NaN</td>\n",
" <td>True</td>\n",
" <td>1</td>\n",
" <td>...</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.027046</td>\n",
" <td>1</td>\n",
" <td>0.001118</td>\n",
" <td>0.000000</td>\n",
" <td>0.000000</td>\n",
" <td>0.000000</td>\n",
" <td>0.000000</td>\n",
" <td>NaN</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>1_30466</td>\n",
" <td>2</td>\n",
" <td>NaN</td>\n",
" <td>2355.0</td>\n",
" <td>0</td>\n",
" <td>1311</td>\n",
" <td>False</td>\n",
" <td>NaN</td>\n",
" <td>True</td>\n",
" <td>1</td>\n",
" <td>...</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.180851</td>\n",
" <td>1</td>\n",
" <td>0.008813</td>\n",
" <td>0.000000</td>\n",
" <td>0.000000</td>\n",
" <td>0.000000</td>\n",
" <td>0.000000</td>\n",
" <td>NaN</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>1_41898</td>\n",
" <td>20244</td>\n",
" <td>203714.0</td>\n",
" <td>97973.0</td>\n",
" <td>0</td>\n",
" <td>1311</td>\n",
" <td>False</td>\n",
" <td>NaN</td>\n",
" <td>True</td>\n",
" <td>1</td>\n",
" <td>...</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.220872</td>\n",
" <td>1</td>\n",
" <td>0.011288</td>\n",
" <td>0.000000</td>\n",
" <td>0.000000</td>\n",
" <td>0.000000</td>\n",
" <td>0.000000</td>\n",
" <td>NaN</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>1_58746</td>\n",
" <td>2</td>\n",
" <td>NaN</td>\n",
" <td>82026.0</td>\n",
" <td>1</td>\n",
" <td>1311</td>\n",
" <td>False</td>\n",
" <td>NaN</td>\n",
" <td>True</td>\n",
" <td>1</td>\n",
" <td>...</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.100951</td>\n",
" <td>1</td>\n",
" <td>0.004502</td>\n",
" <td>0.000000</td>\n",
" <td>0.000000</td>\n",
" <td>0.000000</td>\n",
" <td>0.000000</td>\n",
" <td>NaN</td>\n",
" </tr>\n",
" <tr>\n",
" <th>...</th>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" </tr>\n",
" <tr>\n",
" <th>186115</th>\n",
" <td>4_24295</td>\n",
" <td>103884</td>\n",
" <td>NaN</td>\n",
" <td>96913.0</td>\n",
" <td>0</td>\n",
" <td>1342</td>\n",
" <td>False</td>\n",
" <td>NaN</td>\n",
" <td>True</td>\n",
" <td>1</td>\n",
" <td>...</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.466644</td>\n",
" <td>2</td>\n",
" <td>0.034037</td>\n",
" <td>0.000000</td>\n",
" <td>0.000000</td>\n",
" <td>0.000000</td>\n",
" <td>0.000000</td>\n",
" <td>NaN</td>\n",
" </tr>\n",
" <tr>\n",
" <th>186116</th>\n",
" <td>4_44443</td>\n",
" <td>43315</td>\n",
" <td>NaN</td>\n",
" <td>234734.0</td>\n",
" <td>0</td>\n",
" <td>1342</td>\n",
" <td>False</td>\n",
" <td>NaN</td>\n",
" <td>True</td>\n",
" <td>0</td>\n",
" <td>...</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.427641</td>\n",
" <td>2</td>\n",
" <td>0.029211</td>\n",
" <td>0.000000</td>\n",
" <td>0.000000</td>\n",
" <td>0.000000</td>\n",
" <td>0.000000</td>\n",
" <td>NaN</td>\n",
" </tr>\n",
" <tr>\n",
" <th>186117</th>\n",
" <td>4_3343947</td>\n",
" <td>2</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>1</td>\n",
" <td>1342</td>\n",
" <td>False</td>\n",
" <td>NaN</td>\n",
" <td>True</td>\n",
" <td>0</td>\n",
" <td>...</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.468464</td>\n",
" <td>2</td>\n",
" <td>0.034278</td>\n",
" <td>0.705882</td>\n",
" <td>20.470588</td>\n",
" <td>0.024196</td>\n",
" <td>0.701686</td>\n",
" <td>17.0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>186118</th>\n",
" <td>4_47752</td>\n",
" <td>46460</td>\n",
" <td>NaN</td>\n",
" <td>89791.0</td>\n",
" <td>0</td>\n",
" <td>1342</td>\n",
" <td>False</td>\n",
" <td>NaN</td>\n",
" <td>True</td>\n",
" <td>1</td>\n",
" <td>...</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.360100</td>\n",
" <td>2</td>\n",
" <td>0.022161</td>\n",
" <td>0.000000</td>\n",
" <td>0.000000</td>\n",
" <td>0.000000</td>\n",
" <td>0.000000</td>\n",
" <td>NaN</td>\n",
" </tr>\n",
" <tr>\n",
" <th>186119</th>\n",
" <td>4_35449</td>\n",
" <td>34592</td>\n",
" <td>NaN</td>\n",
" <td>119197.0</td>\n",
" <td>0</td>\n",
" <td>1342</td>\n",
" <td>False</td>\n",
" <td>NaN</td>\n",
" <td>True</td>\n",
" <td>1</td>\n",
" <td>...</td>\n",
" <td>0.0</td>\n",
" <td>1.0</td>\n",
" <td>0.728907</td>\n",
" <td>3</td>\n",
" <td>0.097705</td>\n",
" <td>0.000000</td>\n",
" <td>0.000000</td>\n",
" <td>0.000000</td>\n",
" <td>0.000000</td>\n",
" <td>NaN</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"<p>186120 rows × 97 columns</p>\n",
"</div>"
],
"text/plain": [
" customer_id street_id structure_id mcp_contact_id fidelity \\\n",
"0 1_8191 8114 NaN 834.0 0 \n",
"1 1_14792 2 NaN 251178.0 0 \n",
"2 1_30466 2 NaN 2355.0 0 \n",
"3 1_41898 20244 203714.0 97973.0 0 \n",
"4 1_58746 2 NaN 82026.0 1 \n",
"... ... ... ... ... ... \n",
"186115 4_24295 103884 NaN 96913.0 0 \n",
"186116 4_44443 43315 NaN 234734.0 0 \n",
"186117 4_3343947 2 NaN NaN 1 \n",
"186118 4_47752 46460 NaN 89791.0 0 \n",
"186119 4_35449 34592 NaN 119197.0 0 \n",
"\n",
" tenant_id is_partner deleted_at is_email_true opt_in ... \\\n",
"0 1311 False NaN True 1 ... \n",
"1 1311 False NaN True 1 ... \n",
"2 1311 False NaN True 1 ... \n",
"3 1311 False NaN True 1 ... \n",
"4 1311 False NaN True 1 ... \n",
"... ... ... ... ... ... ... \n",
"186115 1342 False NaN True 1 ... \n",
"186116 1342 False NaN True 0 ... \n",
"186117 1342 False NaN True 0 ... \n",
"186118 1342 False NaN True 1 ... \n",
"186119 1342 False NaN True 1 ... \n",
"\n",
" has_purchased has_purchased_estim score quartile score_adjusted \\\n",
"0 0.0 0.0 0.408546 2 0.027066 \n",
"1 0.0 0.0 0.027046 1 0.001118 \n",
"2 0.0 0.0 0.180851 1 0.008813 \n",
"3 0.0 0.0 0.220872 1 0.011288 \n",
"4 0.0 0.0 0.100951 1 0.004502 \n",
"... ... ... ... ... ... \n",
"186115 0.0 0.0 0.466644 2 0.034037 \n",
"186116 0.0 0.0 0.427641 2 0.029211 \n",
"186117 0.0 0.0 0.468464 2 0.034278 \n",
"186118 0.0 0.0 0.360100 2 0.022161 \n",
"186119 0.0 1.0 0.728907 3 0.097705 \n",
"\n",
" nb_tickets_projected total_amount_projected nb_tickets_expected \\\n",
"0 0.000000 0.000000 0.000000 \n",
"1 0.000000 0.000000 0.000000 \n",
"2 0.000000 0.000000 0.000000 \n",
"3 0.000000 0.000000 0.000000 \n",
"4 0.000000 0.000000 0.000000 \n",
"... ... ... ... \n",
"186115 0.000000 0.000000 0.000000 \n",
"186116 0.000000 0.000000 0.000000 \n",
"186117 0.705882 20.470588 0.024196 \n",
"186118 0.000000 0.000000 0.000000 \n",
"186119 0.000000 0.000000 0.000000 \n",
"\n",
" total_amount_expected pace_purchase \n",
"0 0.000000 NaN \n",
"1 0.000000 NaN \n",
"2 0.000000 NaN \n",
"3 0.000000 NaN \n",
"4 0.000000 NaN \n",
"... ... ... \n",
"186115 0.000000 NaN \n",
"186116 0.000000 NaN \n",
"186117 0.701686 17.0 \n",
"186118 0.000000 NaN \n",
"186119 0.000000 NaN \n",
"\n",
"[186120 rows x 97 columns]"
]
},
"execution_count": 41,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"X_test_segment = project_tickets_CA (X_test_segment, \"nb_purchases\", \"nb_tickets\", \"total_amount\", \"score_adjusted\", \n",
" duration_ref=17, duration_projection=12)\n",
"X_test_segment"
]
},
{
"cell_type": "code",
"execution_count": 42,
"id": "cb66a8ea-65f7-460f-b3fc-ba76a3b91faa",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"quartile\n",
"1 16.722853\n",
"2 16.568788\n",
"3 15.765899\n",
"4 13.263500\n",
"Name: pace_purchase, dtype: float64"
]
},
"execution_count": 42,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"X_test_segment.groupby(\"quartile\")[\"pace_purchase\"].mean()"
]
},
{
"cell_type": "code",
"execution_count": 43,
"id": "f58f9151-2f91-45df-abb7-1ddcf0652adc",
"metadata": {},
"outputs": [],
"source": [
"# generalization with a function\n",
"\n",
"def summary_expected_CA(df, segment, nb_tickets_expected, total_amount_expected, total_amount, pace_purchase,\n",
" duration_ref=17, duration_projection=12) :\n",
" \n",
" # compute nb tickets estimated and total amount expected\n",
" df_expected_CA = df.groupby(segment)[[nb_tickets_expected, total_amount_expected]].sum().reset_index()\n",
" \n",
" # number of customers by segment\n",
" df_expected_CA.insert(1, \"size\", df.groupby(segment).size().values)\n",
" \n",
" # size in percent of all customers\n",
" df_expected_CA.insert(2, \"size_perct\", 100 * df_expected_CA[\"size\"]/df_expected_CA[\"size\"].sum())\n",
" \n",
" # compute share of CA recovered\n",
" duration_ratio=duration_ref/duration_projection\n",
" \n",
" df_expected_CA[\"revenue_recovered_perct\"] = 100 * duration_ratio * df_expected_CA[total_amount_expected] / \\\n",
" df.groupby(segment)[total_amount].sum().values\n",
"\n",
" df_drop_null_pace = df.dropna(subset=[pace_purchase])\n",
" df_expected_CA[\"pace_purchase\"] = df_drop_null_pace.groupby(segment)[pace_purchase].mean().values\n",
" \n",
" return df_expected_CA"
]
},
{
"cell_type": "code",
"execution_count": 44,
"id": "c8df6c80-43e8-4f00-9cd3-eb9022744313",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>quartile</th>\n",
" <th>size</th>\n",
" <th>size_perct</th>\n",
" <th>nb_tickets_expected</th>\n",
" <th>total_amount_expected</th>\n",
" <th>revenue_recovered_perct</th>\n",
" <th>pace_purchase</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>1</td>\n",
" <td>81622</td>\n",
" <td>43.85</td>\n",
" <td>263.12</td>\n",
" <td>3258.54</td>\n",
" <td>0.88</td>\n",
" <td>16.72</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>2</td>\n",
" <td>60811</td>\n",
" <td>32.67</td>\n",
" <td>1984.56</td>\n",
" <td>27052.82</td>\n",
" <td>2.47</td>\n",
" <td>16.57</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>3</td>\n",
" <td>28913</td>\n",
" <td>15.53</td>\n",
" <td>3476.63</td>\n",
" <td>43945.79</td>\n",
" <td>6.34</td>\n",
" <td>15.77</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>4</td>\n",
" <td>14774</td>\n",
" <td>7.94</td>\n",
" <td>58598.68</td>\n",
" <td>523568.93</td>\n",
" <td>60.03</td>\n",
" <td>13.26</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" quartile size size_perct nb_tickets_expected total_amount_expected \\\n",
"0 1 81622 43.85 263.12 3258.54 \n",
"1 2 60811 32.67 1984.56 27052.82 \n",
"2 3 28913 15.53 3476.63 43945.79 \n",
"3 4 14774 7.94 58598.68 523568.93 \n",
"\n",
" revenue_recovered_perct pace_purchase \n",
"0 0.88 16.72 \n",
"1 2.47 16.57 \n",
"2 6.34 15.77 \n",
"3 60.03 13.26 "
]
},
"execution_count": 44,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"X_test_expected_CA = round(summary_expected_CA(df=X_test_segment, segment=\"quartile\", \n",
" nb_tickets_expected=\"nb_tickets_expected\", total_amount_expected=\"total_amount_expected\", \n",
" total_amount=\"total_amount\", pace_purchase=\"pace_purchase\"),2)\n",
"\n",
"X_test_expected_CA"
]
},
{
"cell_type": "code",
"execution_count": 64,
"id": "ac706ed7-defa-4df1-82e1-06f12fc1b6ad",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'\\\\begin{tabular}{lrrrrrr}\\n\\\\toprule\\nquartile & size & size (%) & nb tickets expected & total amount expected & revenue recovered (%) & pace purchase \\\\\\\\\\n\\\\midrule\\n1 & 53626 & 35.310000 & 398.260000 & 13949.330000 & 2.350000 & 16.480000 \\\\\\\\\\n2 & 55974 & 36.860000 & 3113.770000 & 101639.450000 & 6.240000 & 16.470000 \\\\\\\\\\n3 & 30435 & 20.040000 & 6214.350000 & 208267.220000 & 14.270000 & 15.710000 \\\\\\\\\\n4 & 11839 & 7.800000 & 72929.460000 & 1835702.430000 & 75.380000 & 11.480000 \\\\\\\\\\n\\\\bottomrule\\n\\\\end{tabular}\\n'"
]
},
"execution_count": 64,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Création du dictionnaire de mapping pour les noms de colonnes\n",
"mapping_dict = {col: col.replace(\"perct\", \"(%)\").replace(\"_\", \" \") for col in X_test_expected_CA.columns}\n",
"\n",
"X_test_expected_CA.rename(columns=mapping_dict).to_latex(index=False)"
]
},
{
"cell_type": "code",
"execution_count": 122,
"id": "771da0cf-c49f-4e7e-b52f-ebcfb0fb2df3",
"metadata": {},
"outputs": [],
"source": [
"# export summary table to the MinIO storage\n",
"\n",
"file_name = \"table_expected_CA_\"\n",
"FILE_PATH_OUT_S3 = PATH + file_name + type_of_activity + \".csv\"\n",
"with fs.open(FILE_PATH_OUT_S3, 'w') as file_out:\n",
" X_test_expected_CA.to_csv(file_out, index = False)"
]
},
{
"cell_type": "code",
"execution_count": 53,
"id": "c805dc10-4d07-4f7d-a677-5461a92845d7",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'projet-bdc2324-team1/Output_expected_CA/musique/table_expected_CA_musique.csv'"
]
},
"execution_count": 53,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"PATH = f\"projet-bdc2324-team1/Output_expected_CA/{type_of_activity}/\"\n",
"file_name = \"table_expected_CA_\"\n",
"FILE_PATH_OUT_S3 = PATH + file_name + type_of_activity + \".csv\"\n",
"FILE_PATH_OUT_S3"
]
},
{
"cell_type": "markdown",
"id": "e35ccfff-1845-41f0-9bde-f09b09b67877",
"metadata": {},
"source": [
"## Test : vizu tables saved"
]
},
{
"cell_type": "code",
"execution_count": 66,
"id": "4e9e88e4-ea10-41f4-9bf1-20b55269a20d",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>quartile</th>\n",
" <th>score (%)</th>\n",
" <th>score adjusted (%)</th>\n",
" <th>has purchased (%)</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>1</td>\n",
" <td>13.25</td>\n",
" <td>2.51</td>\n",
" <td>1.57</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>2</td>\n",
" <td>33.89</td>\n",
" <td>8.00</td>\n",
" <td>9.85</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>3</td>\n",
" <td>63.06</td>\n",
" <td>22.58</td>\n",
" <td>21.47</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>4</td>\n",
" <td>90.52</td>\n",
" <td>66.20</td>\n",
" <td>65.01</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" quartile score (%) score adjusted (%) has purchased (%)\n",
"0 1 13.25 2.51 1.57\n",
"1 2 33.89 8.00 9.85\n",
"2 3 63.06 22.58 21.47\n",
"3 4 90.52 66.20 65.01"
]
},
"execution_count": 66,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"path = 'projet-bdc2324-team1/Output_expected_CA/sport/table_adjusted_scoresport.csv'\n",
"\n",
"with fs.open( path, mode=\"rb\") as file_in:\n",
" df = pd.read_csv(file_in, sep=\",\")\n",
"df"
]
},
{
"cell_type": "markdown",
"id": "9c471bdd-25c2-420a-a8a1-3add9f003cbc",
"metadata": {},
"source": [
"## Just to try, same computation with score instead of score adjusted\n",
"\n",
"seems overestimated : if only 14% of customers come back, how can we recover 22% of the revenue from the segment that is least likely to buy ?? ..."
]
},
{
"cell_type": "code",
"execution_count": 80,
"id": "53684a24-1809-465f-8e21-b9295e34582a",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>quartile</th>\n",
" <th>size</th>\n",
" <th>size_perct</th>\n",
" <th>nb_tickets_expected</th>\n",
" <th>total_amount_expected</th>\n",
" <th>perct_revenue_recovered</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>1</td>\n",
" <td>37410</td>\n",
" <td>38.93</td>\n",
" <td>419.76</td>\n",
" <td>9245.08</td>\n",
" <td>21.71</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>2</td>\n",
" <td>29517</td>\n",
" <td>30.72</td>\n",
" <td>11549.06</td>\n",
" <td>296522.02</td>\n",
" <td>39.24</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>3</td>\n",
" <td>20137</td>\n",
" <td>20.96</td>\n",
" <td>29997.85</td>\n",
" <td>954751.91</td>\n",
" <td>63.34</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>4</td>\n",
" <td>9032</td>\n",
" <td>9.40</td>\n",
" <td>244655.82</td>\n",
" <td>10736011.95</td>\n",
" <td>97.72</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" quartile size size_perct nb_tickets_expected total_amount_expected \\\n",
"0 1 37410 38.93 419.76 9245.08 \n",
"1 2 29517 30.72 11549.06 296522.02 \n",
"2 3 20137 20.96 29997.85 954751.91 \n",
"3 4 9032 9.40 244655.82 10736011.95 \n",
"\n",
" perct_revenue_recovered \n",
"0 21.71 \n",
"1 39.24 \n",
"2 63.34 \n",
"3 97.72 "
]
},
"execution_count": 80,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"X_test_segment_bis = project_tickets_CA (X_test_segment, \"nb_tickets\", \"total_amount\", \"score\", duration_ref=1.5, duration_projection=1)\n",
"\n",
"X_test_expected_CA_bis = round(summary_expected_CA(df=X_test_segment_bis, segment=\"quartile\", nb_tickets_expected=\"nb_tickets_expected\", \n",
" total_amount_expected=\"total_amount_expected\", total_amount=\"total_amount\"),2)\n",
"\n",
"X_test_expected_CA_bis"
]
},
{
"cell_type": "code",
"execution_count": 81,
"id": "7dc66d1e-da03-4513-96e4-d9a43ac0a2c8",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"overall share of revenue recovered : 90.26 %\n"
]
}
],
"source": [
"print(\"overall share of revenue recovered : \", round(100 * duration_ratio * X_test_expected_CA_bis[\"total_amount_expected\"].sum() / \\\n",
"X_test_segment_bis[\"total_amount\"].sum(),2), \"%\")"
]
},
{
"cell_type": "markdown",
"id": "673f2969-7b9a-44c1-abf5-5679fca877ce",
"metadata": {},
"source": [
"## Last pieces of analysis"
]
},
{
"cell_type": "code",
"execution_count": 161,
"id": "2365bb13-0f3f-49d5-bf91-52c92abebcee",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"overall share of revenue recovered : 77.64%\n"
]
}
],
"source": [
"# global revenue recovered\n",
"global_revenue_recovered = round(100 * duration_ratio * X_test_expected_CA[\"total_amount_expected\"].sum() / \\\n",
"X_test_segment[\"total_amount\"].sum(),2)\n",
"print(f\"overall share of revenue recovered : {global_revenue_recovered}%\")"
]
},
{
"cell_type": "code",
"execution_count": 163,
"id": "16b17f35-57dd-459a-8989-129143dc0952",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"0 0.018093\n",
"1 0.721519\n",
"2 3.336101\n",
"3 95.924287\n",
"Name: total_amount_expected, dtype: float64"
]
},
"execution_count": 163,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"100 * X_test_expected_CA[\"total_amount_expected\"]/X_test_expected_CA[\"total_amount_expected\"].sum()"
]
},
{
"cell_type": "code",
"execution_count": 166,
"id": "dee4a200-eefe-4377-8e80-59ad33edd3c0",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"quartile\n",
"1 0.320407\n",
"2 5.685020\n",
"3 11.339715\n",
"4 82.654858\n",
"Name: total_amount, dtype: float64"
]
},
"execution_count": 166,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# le segment 4 représente 83% du CA actuel et 96% du CA lié aux anciens clients pour l'année prochaine\n",
"100 * X_test_segment.groupby(\"quartile\")[\"total_amount\"].sum()/X_test_segment[\"total_amount\"].sum()"
]
},
{
"cell_type": "code",
"execution_count": 177,
"id": "c1e6f020-ef18-40b4-bfc1-19f98cb2796e",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"count 96096.000000\n",
"mean 207.475735\n",
"std 4720.046248\n",
"min -48831.800000\n",
"25% 0.000000\n",
"50% 0.000000\n",
"75% 60.000000\n",
"max 624890.000000\n",
"Name: total_amount, dtype: float64"
]
},
"execution_count": 177,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"X_test_segment[\"total_amount\"].describe() # total amount négatif ???\n"
]
},
{
"cell_type": "code",
"execution_count": 184,
"id": "d301a50e-7c68-40f0-9245-a4eea64c387b",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"0 -4.883180e+04\n",
"1 -6.483180e+04\n",
"2 -7.683860e+04\n",
"3 -8.683860e+04\n",
"4 -9.683860e+04\n",
" ... \n",
"96091 1.802247e+07\n",
"96092 1.839238e+07\n",
"96093 1.877219e+07\n",
"96094 1.931270e+07\n",
"96095 1.993759e+07\n",
"Name: total_amount, Length: 96096, dtype: float64"
]
},
"execution_count": 184,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"np.cumsum(X_test_segment[\"total_amount\"].sort_values()).reset_index()[\"total_amount\"]"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"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.11.6"
}
},
"nbformat": 4,
"nbformat_minor": 5
}