{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":7391,"databundleVersionId":44328,"sourceType":"competition"}],"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Analyse exploratoire des données (EDA)","metadata":{}},{"cell_type":"markdown","source":"L’analyse exploratoire des données constitue une étape essentielle du projet, car elle permet de comprendre la structure, la qualité et les principales caractéristiques des données disponibles. Elle vise à mettre en évidence les distributions des variables, les tendances temporelles, les relations entre les différentes variables ainsi que la présence éventuelle de valeurs manquantes ou aberrantes. Les enseignements issus de cette analyse orientent les choix méthodologiques ultérieurs, notamment en matière de prétraitement, de feature engineering et de modélisation.","metadata":{}},{"cell_type":"markdown","source":"## **1. Chargement et exploration des données brutes**","metadata":{}},{"cell_type":"code","source":"# Import du module os pour interagir avec le système de fichiers\nimport os\n\n# Définition du répertoire contenant les données brutes du projet\n# (chemin Kaggle où sont stockés les fichiers du dataset Favorita)\nDATA_DIR = \"/kaggle/input/favorita-grocery-sales-forecasting\"\n\n# Affichage des fichiers disponibles dans le répertoire de données\n# Cette étape permet de vérifier la présence et le nom des différentes bases\nprint(\"Fichiers disponibles :\")\nfor f in os.listdir(DATA_DIR):\n    print(f)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-13T14:08:48.238142Z","iopub.execute_input":"2026-01-13T14:08:48.238542Z","iopub.status.idle":"2026-01-13T14:08:48.271474Z","shell.execute_reply.started":"2026-01-13T14:08:48.238501Z","shell.execute_reply":"2026-01-13T14:08:48.270253Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## **2. Décompression et préparation des fichiers de données**","metadata":{}},{"cell_type":"code","source":"# Import du module os pour la gestion des fichiers et répertoires\nimport os\n\n# Import du module subprocess pour exécuter des commandes système\nimport subprocess\n\n# Définition du répertoire de destination pour les fichiers décompressés\n# Ce répertoire sera utilisé pour stocker les données extraites des archives\nEXTRACT_DIR = \"/kaggle/working/favorita_data\"\n\n# Création du répertoire de destination s'il n'existe pas déjà\nos.makedirs(EXTRACT_DIR, exist_ok=True)\n\n# Parcours des fichiers présents dans le répertoire de données brutes\nfor file in os.listdir(DATA_DIR):\n    \n    # Identification des fichiers compressés au format .7z\n    if file.endswith(\".7z\"):\n        \n        # Construction du chemin complet vers l'archive\n        archive_path = os.path.join(DATA_DIR, file)\n        \n        # Message informatif indiquant le début de la décompression\n        print(f\"Décompression de {file} ...\")\n        \n        # Exécution de la commande de décompression via l'outil 7z\n        # Les fichiers sont extraits dans le répertoire EXTRACT_DIR\n        # L'option -y permet de répondre automatiquement \"oui\" aux éventuelles demandes\n        # La sortie standard est masquée pour plus de lisibilité\n        subprocess.run(\n            [\"7z\", \"x\", archive_path, f\"-o{EXTRACT_DIR}\", \"-y\"],\n            stdout=subprocess.DEVNULL\n        )\n\n# Affichage du contenu du répertoire après décompression\n# Cette étape permet de vérifier que les fichiers ont été correctement extraits\nprint(\"\\nFichiers décompressés :\")\nprint(os.listdir(EXTRACT_DIR))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-13T14:08:52.463982Z","iopub.execute_input":"2026-01-13T14:08:52.465384Z","iopub.status.idle":"2026-01-13T14:10:09.555851Z","shell.execute_reply.started":"2026-01-13T14:08:52.465307Z","shell.execute_reply":"2026-01-13T14:10:09.554509Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## **3. Gestion des avertissements pour améliorer la lisibilité**","metadata":{}},{"cell_type":"code","source":"# Import du module warnings pour gérer les messages d'avertissement\nimport warnings\n\n# Désactivation des FutureWarning afin d'éviter l'affichage\n# de messages non critiques liés à des évolutions futures des bibliothèques\n# Cela permet d'améliorer la lisibilité des sorties du notebook\nwarnings.filterwarnings(\"ignore\", category=FutureWarning)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-13T14:10:31.048778Z","iopub.execute_input":"2026-01-13T14:10:31.049181Z","iopub.status.idle":"2026-01-13T14:10:31.056548Z","shell.execute_reply.started":"2026-01-13T14:10:31.049155Z","shell.execute_reply":"2026-01-13T14:10:31.055082Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## **4. Analyse de la variable cible (unit_sales)** ","metadata":{}},{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport seaborn as sns\n\n# Définition du style graphique pour les visualisations\nplt.style.use(\"seaborn-v0_8\")\n\n# Affiche toutes les colonnes lors de l’affichage d’un DataFrame\npd.set_option(\"display.max_columns\", None)\n\n# Chemin vers le dossier contenant les données extraites\nDATA_PATH = \"/kaggle/working/favorita_data\"\n\n# Chargement du fichier train.csv avec sélection des colonnes utiles\ntrain = pd.read_csv(\n    f\"{DATA_PATH}/train.csv\",\n    usecols=[\"date\", \"store_nbr\", \"item_nbr\", \"unit_sales\", \"onpromotion\"],  # colonnes pertinentes\n    parse_dates=[\"date\"],   # conversion automatique de la colonne 'date' en format datetime\n    low_memory=False        # évite les problèmes de type lors du chargement de gros fichiers\n)\n\n# Affiche les 5 premières lignes du DataFrame pour inspection rapide\ntrain.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-13T14:10:35.227362Z","iopub.execute_input":"2026-01-13T14:10:35.227752Z","iopub.status.idle":"2026-01-13T14:12:28.497652Z","shell.execute_reply.started":"2026-01-13T14:10:35.227724Z","shell.execute_reply":"2026-01-13T14:12:28.496392Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### **Vue d'ensemble**","metadata":{}},{"cell_type":"code","source":"# Affiche des informations générales sur le DataFrame :\n# - Nombre de lignes et colonnes\n# - Noms des colonnes\n# - Types de données (int, float, object, datetime, etc.)\n# - Nombre de valeurs non nulles par colonne\ntrain.info()\n\n# Fournit des statistiques descriptives pour les colonnes numériques :\n# - count : nombre d’observations\n# - mean : moyenne\n# - std : écart-type\n# - min / max : valeurs extrêmes\n# - 25%, 50%, 75% : quartiles (distribution des données)\ntrain.describe()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-13T14:14:39.096407Z","iopub.execute_input":"2026-01-13T14:14:39.096764Z","iopub.status.idle":"2026-01-13T14:15:00.817248Z","shell.execute_reply.started":"2026-01-13T14:14:39.096736Z","shell.execute_reply":"2026-01-13T14:15:00.813159Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### **Valeurs négatives (retours)**","metadata":{}},{"cell_type":"code","source":"(train[\"unit_sales\"] < 0).mean()\n\n\ntrain[train[\"unit_sales\"] < 0].head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-13T14:15:06.736408Z","iopub.execute_input":"2026-01-13T14:15:06.736763Z","iopub.status.idle":"2026-01-13T14:15:07.243155Z","shell.execute_reply.started":"2026-01-13T14:15:06.736740Z","shell.execute_reply":"2026-01-13T14:15:07.241907Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### **Analyse descriptive de unit_sales**","metadata":{}},{"cell_type":"code","source":"train[\"unit_sales\"].describe()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-13T14:15:12.007073Z","iopub.execute_input":"2026-01-13T14:15:12.007425Z","iopub.status.idle":"2026-01-13T14:15:18.363126Z","shell.execute_reply.started":"2026-01-13T14:15:12.007399Z","shell.execute_reply":"2026-01-13T14:15:18.359883Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"La base contient 125 497 040 lignes couvrant la période du 01/01/2013 au 15/08/2017. On observe 54 magasins uniques et des articles dont les identifiants montent jusqu'à 2 127 114. La variable cible *unit_sales* présente une forte dispersion avec une moyenne de 8,55 bien supérieure à la médiane de 4,0, ainsi que des valeurs extrêmes allant de -15 372 (retours) à 89 440 (ventes massives). Enfin, la colonne onpromotion est actuellement stockée sous le type object, ce qui nécessite une conversion pour permettre l'analyse statistique.","metadata":{}},{"cell_type":"markdown","source":"### **Distribution brute des ventes**","metadata":{}},{"cell_type":"code","source":"train_sample = train.sample(2_000_000, random_state=42)\n\nprint(train_sample.shape)\ntrain_sample[\"unit_sales\"].describe()\n\ntrain_sample[\"unit_sales\"].clip(0, 200).hist(bins=100)\nplt.title(\"Distribution des ventes (cappée à 200)\")\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-13T14:15:44.578846Z","iopub.execute_input":"2026-01-13T14:15:44.579214Z","iopub.status.idle":"2026-01-13T14:15:53.506517Z","shell.execute_reply.started":"2026-01-13T14:15:44.579188Z","shell.execute_reply":"2026-01-13T14:15:53.505365Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Cet histogramme confirme visuellement la forte asymétrie à droite de la variable cible : l'immense majorité des ventes se concentre entre 0 et 10 unités, avec une chute brutale de la fréquence dès que l'on dépasse ce seuil. Le pic massif tout à gauche indique que les petits volumes d'achats quotidiens constituent le cœur de l'activité, tandis que la \"longue traîne\" s'étirant vers 200 (ton plafond de clip) montre que les ventes importantes sont extrêmement rares. Cette forme en \"L\" justifie mathématiquement l'utilisation d'une transformation logarithmique ou d'un modèle robuste aux valeurs aberrantes, car l'écart entre les volumes courants et les pics de vente est disproportionné.","metadata":{}},{"cell_type":"markdown","source":"### **Transformation logarithmique**","metadata":{}},{"cell_type":"code","source":"train_sample[\"unit_sales_clean\"] = train_sample[\"unit_sales\"].clip(1, 200)\ntrain_sample[\"log_unit_sales\"] = np.log1p(train_sample[\"unit_sales_clean\"])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-13T14:18:21.102849Z","iopub.execute_input":"2026-01-13T14:18:21.103518Z","iopub.status.idle":"2026-01-13T14:18:21.224713Z","shell.execute_reply.started":"2026-01-13T14:18:21.103480Z","shell.execute_reply":"2026-01-13T14:18:21.223281Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_sample[\"unit_sales_clean\"] = train_sample[\"unit_sales\"].clip(1, 200)\ntrain_sample[\"log_unit_sales\"] = np.log1p(train_sample[\"unit_sales_clean\"])","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"fig, ax = plt.subplots(1,2, figsize=(14,5))\n\nsns.histplot(train_sample[\"unit_sales_clean\"], bins=100, ax=ax[0])\nax[0].set_title(\"Avant transformation log\")\n\nsns.histplot(train_sample[\"log_unit_sales\"], bins=100, ax=ax[1])\nax[1].set_title(\"Après transformation log\")\n\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-13T14:18:37.483533Z","iopub.execute_input":"2026-01-13T14:18:37.484002Z","iopub.status.idle":"2026-01-13T14:18:40.891426Z","shell.execute_reply.started":"2026-01-13T14:18:37.483971Z","shell.execute_reply":"2026-01-13T14:18:40.890071Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### **Valeurs aberrantes**","metadata":{}},{"cell_type":"code","source":"train_sample[\"unit_sales_clean\"].quantile([0.95, 0.99, 0.999])\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-13T14:20:59.587256Z","iopub.execute_input":"2026-01-13T14:20:59.587652Z","iopub.status.idle":"2026-01-13T14:20:59.649798Z","shell.execute_reply.started":"2026-01-13T14:20:59.587625Z","shell.execute_reply":"2026-01-13T14:20:59.648125Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"sns.boxplot(x=train_sample[\"log_unit_sales\"])\nplt.title(\"Boxplot des ventes log-transformées\")\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-13T14:21:03.184594Z","iopub.execute_input":"2026-01-13T14:21:03.185100Z","iopub.status.idle":"2026-01-13T14:21:03.498846Z","shell.execute_reply.started":"2026-01-13T14:21:03.185050Z","shell.execute_reply":"2026-01-13T14:21:03.497545Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Même après la transformation log, les données restent dominées par des valeurs extrêmes significatives. La boîte du boxplot est très étroite par rapport à la longueur de la \"moustache\" droite, ce qui signifie que le comportement d'achat standard est très régulier, mais que les événements exceptionnels (pics de vente) sont très nombreux et dispersés.","metadata":{}},{"cell_type":"markdown","source":"### **Analyse temporelle**\n","metadata":{}},{"cell_type":"markdown","source":"### **Evolution globale**","metadata":{}},{"cell_type":"code","source":"daily_sales = (\n    train_sample.groupby(\"date\")[\"unit_sales\"]\n    .sum()\n    .reset_index()\n)\n\nplt.figure(figsize=(14,5))\nplt.plot(daily_sales[\"date\"], daily_sales[\"unit_sales\"])\nplt.title(\"Évolution globale des ventes\")\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-13T14:21:29.314897Z","iopub.execute_input":"2026-01-13T14:21:29.315285Z","iopub.status.idle":"2026-01-13T14:21:29.651425Z","shell.execute_reply.started":"2026-01-13T14:21:29.315260Z","shell.execute_reply":"2026-01-13T14:21:29.649894Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Le graphique de l'Évolution globale des ventes montre une tendance générale à la hausse progressive entre *2013* et *2017*, indiquant une croissance constante de l'activité. On observe une saisonnalité marquée avec des pics de ventes répétitifs, ainsi que des chutes brutales et systématiques à chaque début d'année (le 1er janvier), correspondant probablement aux fermetures de magasins. La volatilité semble s'accentuer au fil du temps, avec des pics d'activité de plus en plus hauts (dépassant les 20 000 unités en 2016), ce qui suggère que le modèle devra impérativement prendre en compte cette composante de croissance pour ne pas sous-estimer les ventes futures.","metadata":{}},{"cell_type":"markdown","source":"### **Saisonnalité annuelle**","metadata":{}},{"cell_type":"code","source":"train_sample[\"year\"] = train_sample[\"date\"].dt.year\n\nyearly_sales = train_sample.groupby(\"year\")[\"unit_sales\"].sum()\n\nyearly_sales.plot(kind=\"bar\", figsize=(8,4))\nplt.title(\"Ventes annuelles\")\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-13T14:21:43.447771Z","iopub.execute_input":"2026-01-13T14:21:43.448145Z","iopub.status.idle":"2026-01-13T14:21:43.782095Z","shell.execute_reply.started":"2026-01-13T14:21:43.448123Z","shell.execute_reply":"2026-01-13T14:21:43.780900Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Le graphique montre une croissance constante et vigoureuse du volume des ventes entre 2013 et 2016, passant d'environ 2,2 millions à plus de 4,5 millions d'unités, soit un doublement de l'activité en trois ans. L'année 2017 semble en net retrait avec environ 3 millions d'unités, mais cela s'explique par le fait que tes données s'arrêtent au 15 août 2017 ; cette année est donc incomplète et ne représente pas une chute réelle de performance. 1. ","metadata":{}},{"cell_type":"markdown","source":"### **Saisonnalité mensuelle**","metadata":{}},{"cell_type":"code","source":"train_sample[\"month\"] = train_sample[\"date\"].dt.month\n\nmonthly_sales = train_sample.groupby(\"month\")[\"unit_sales\"].mean()\n\nmonthly_sales.plot(kind=\"bar\", figsize=(8,4))\nplt.title(\"Ventes moyennes par mois\")\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-13T13:13:33.444582Z","iopub.execute_input":"2026-01-13T13:13:33.445040Z","iopub.status.idle":"2026-01-13T13:13:33.801609Z","shell.execute_reply.started":"2026-01-13T13:13:33.445014Z","shell.execute_reply":"2026-01-13T13:13:33.800550Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"On constate une saisonnalité mensuelle marquée par un pic majeur en décembre, où la moyenne des ventes atteint son niveau le plus haut (environ 10 unités), confirmant l'impact massif des fêtes de fin d'année. On observe également un niveau soutenu en janvier, probablement lié aux soldes ou à la continuité des achats de vacances, suivi d'une relative stabilité le reste de l'année avec des moyennes oscillant entre 8 et 8,5 unités. Le mois d'août semble être le point le plus bas du cycle annuel.","metadata":{}},{"cell_type":"markdown","source":"### **Effet jour de la semaine**","metadata":{}},{"cell_type":"code","source":"train_sample[\"weekday\"] = train_sample[\"date\"].dt.day_name()\n\nweekday_sales = train_sample.groupby(\"weekday\")[\"unit_sales\"].mean()\n\nweekday_sales.loc[\n    [\"Monday\",\"Tuesday\",\"Wednesday\",\"Thursday\",\"Friday\",\"Saturday\",\"Sunday\"]\n].plot(kind=\"bar\", figsize=(8,4))\n\nplt.title(\"Ventes moyennes par jour\")\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-13T13:13:47.971491Z","iopub.execute_input":"2026-01-13T13:13:47.971880Z","iopub.status.idle":"2026-01-13T13:13:49.212609Z","shell.execute_reply.started":"2026-01-13T13:13:47.971856Z","shell.execute_reply":"2026-01-13T13:13:49.211627Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Le graphique met en évidence une saisonnalité hebdomadaire très marquée, avec une augmentation significative de l'activité durant le week-end. Les ventes moyennes grimpent le samedi (environ 9,8 unités) pour atteindre leur point culminant le dimanche (plus de 10,5 unités), ce qui suggère un comportement d'achat de masse durant les jours de repos. À l'inverse, le jeudi apparaît comme le jour le plus calme de la semaine avec le niveau de ventes le plus bas (environ 7 unités).","metadata":{}},{"cell_type":"markdown","source":"## **5. Produits (items.csv)**","metadata":{}},{"cell_type":"markdown","source":"### **Chargement et fusion**","metadata":{}},{"cell_type":"markdown","source":"La variable **famille** est dans la base **items** et la variable **vente**=unit_sale est dans la base train, donc pour etudier les ventes par familles, il faut un merge des deux bases.","metadata":{}},{"cell_type":"code","source":"import pandas as pd\nitems = pd.read_csv(f\"{DATA_PATH}/items.csv\")\n\ntrain_items = train_sample.merge(items, on=\"item_nbr\", how=\"left\")\n\nitems.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-13T13:14:33.724401Z","iopub.execute_input":"2026-01-13T13:14:33.724836Z","iopub.status.idle":"2026-01-13T13:14:34.356289Z","shell.execute_reply.started":"2026-01-13T13:14:33.724790Z","shell.execute_reply":"2026-01-13T13:14:34.355005Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### **Vente par famille**","metadata":{}},{"cell_type":"code","source":"family_sales = (\n    train_items.groupby(\"family\")[\"unit_sales\"]\n    .sum()\n    .sort_values(ascending=False)\n)\n\nfamily_sales.head(10).plot(kind=\"bar\", figsize=(10,4))\nplt.title(\"Top familles de produits\")\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-13T13:14:43.985937Z","iopub.execute_input":"2026-01-13T13:14:43.986941Z","iopub.status.idle":"2026-01-13T13:14:44.368961Z","shell.execute_reply.started":"2026-01-13T13:14:43.986904Z","shell.execute_reply":"2026-01-13T13:14:44.367716Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Le graphique montre une domination écrasante de la catégorie **GROCERY I**, qui génère plus de 5,5 millions de ventes, suivie par les **BEVERAGES** (boissons) avec environ 3,5 millions. Ces deux catégories représentent le moteur principal du volume de ventes. Les produits frais comme **PRODUCE** et les produits d'entretien **CLEANING** suivent avec des volumes significatifs (entre 1,5 et 2 millions), tandis que les familles comme **MEATS**, **PERSONAL CARE ou DELI** occupent une part beaucoup plus restreinte du catalogue en termes de transactions.","metadata":{}},{"cell_type":"markdown","source":"### **Produits périssables**","metadata":{}},{"cell_type":"code","source":"train_items.groupby(\"perishable\")[\"unit_sales\"].mean()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-13T13:15:08.663525Z","iopub.execute_input":"2026-01-13T13:15:08.663902Z","iopub.status.idle":"2026-01-13T13:15:08.715950Z","shell.execute_reply.started":"2026-01-13T13:15:08.663878Z","shell.execute_reply":"2026-01-13T13:15:08.714949Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"sns.boxplot(\n    x=\"perishable\",\n    y=\"unit_sales\",\n    data=train_items\n)\nplt.title(\"Périssable vs Non périssable\")\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-13T13:15:22.893666Z","iopub.execute_input":"2026-01-13T13:15:22.894708Z","iopub.status.idle":"2026-01-13T13:15:23.724432Z","shell.execute_reply.started":"2026-01-13T13:15:22.894672Z","shell.execute_reply":"2026-01-13T13:15:23.723221Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"L'analyse des boîtes à moustaches (boxplots) révèle que les produits périssables (en vert) affichent une médiane et des quartiles légèrement plus élevés que les produits non périssables. Cela signifie qu'en moyenne, un article frais se vend en plus grande quantité par transaction qu'un article d'épicerie sèche ou de bazar. On remarque également que les deux catégories possèdent une quantité massive de valeurs aberrantes (outliers) grimpant jusqu'à **8** en échelle logarithmique, mais la distribution des produits frais est globalement plus \"étirée\" vers le haut.","metadata":{}},{"cell_type":"markdown","source":"## **6. Magasins (stores.csv)**","metadata":{}},{"cell_type":"markdown","source":"### **Chargement et fusion**","metadata":{}},{"cell_type":"code","source":"stores = pd.read_csv(f\"{DATA_PATH}/stores.csv\")\n\ntrain_stores = train_sample.merge(stores, on=\"store_nbr\", how=\"left\")\n\nstores.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-13T13:16:01.656928Z","iopub.execute_input":"2026-01-13T13:16:01.657285Z","iopub.status.idle":"2026-01-13T13:16:02.224083Z","shell.execute_reply.started":"2026-01-13T13:16:01.657261Z","shell.execute_reply":"2026-01-13T13:16:02.223132Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### **Ventes par type de magasin**","metadata":{}},{"cell_type":"code","source":"type_sales = (\n    train_stores.groupby(\"type\")[\"unit_sales\"]\n    .sum()\n)\n\ntype_sales.plot(kind=\"bar\", figsize=(8,4))\nplt.title(\"Ventes par type de magasin\")\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-13T13:16:11.502843Z","iopub.execute_input":"2026-01-13T13:16:11.503242Z","iopub.status.idle":"2026-01-13T13:16:11.813846Z","shell.execute_reply.started":"2026-01-13T13:16:11.503192Z","shell.execute_reply":"2026-01-13T13:16:11.812795Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Le graphique montre une domination claire des magasins de type **A et D**, qui génèrent chacun plus de **5,5 millions** de ventes, s'affirmant comme les piliers commerciaux de l'enseigne. Les types B et C affichent des performances intermédiaires avec des volumes compris entre 2 et 3 millions, tandis que le type E se situe nettement en retrait avec moins de 1 million de ventes totales.","metadata":{}},{"cell_type":"markdown","source":"### **Clusters**","metadata":{}},{"cell_type":"code","source":"cluster_sales = (\n    train_stores.groupby(\"cluster\")[\"unit_sales\"]\n    .mean()\n)\n\ncluster_sales.plot(kind=\"bar\", figsize=(8,4))\nplt.title(\"Ventes moyennes par cluster\")\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-13T13:17:23.215235Z","iopub.execute_input":"2026-01-13T13:17:23.215650Z","iopub.status.idle":"2026-01-13T13:17:23.498026Z","shell.execute_reply.started":"2026-01-13T13:17:23.215624Z","shell.execute_reply":"2026-01-13T13:17:23.497016Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Le graphique révèle une hétérogénéité frappante entre les **17 clusters** de magasins, avec le cluster 5 qui survole les autres en atteignant une moyenne de 18 unités vendues, soit plus du double de la majorité des autres groupes. Les clusters **11, 8, 14 et 17** se distinguent également avec des performances solides dépassant les 11 unités, tandis que les clusters 7, 9, 15 et 16 ferment la marche avec les moyennes les plus basses (autour de 6 unités).","metadata":{}},{"cell_type":"markdown","source":"## **7. Promotion**","metadata":{}},{"cell_type":"code","source":"train_sample.groupby(\"onpromotion\")[\"unit_sales\"].mean()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-13T13:17:33.497870Z","iopub.execute_input":"2026-01-13T13:17:33.498890Z","iopub.status.idle":"2026-01-13T13:17:33.674894Z","shell.execute_reply.started":"2026-01-13T13:17:33.498847Z","shell.execute_reply":"2026-01-13T13:17:33.673737Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import matplotlib.pyplot as plt\n\n# 1. Calcul des moyennes (basé sur ton image)\npromo_means = train_sample.groupby(\"onpromotion\")[\"unit_sales\"].mean()\n\n# 2. Préparation des données\nlabels = ['Sans Promotion', 'Avec Promotion']\ncolors = ['#ff9999','#66b3ff']\nexplode = (0, 0.1)  # On détache la part \"Promotion\" pour l'effet visuel\n\n# 3. Création du graphique\nplt.figure(figsize=(8, 6))\nplt.pie(\n    promo_means, \n    labels=labels, \n    autopct='%1.1f%%', \n    startangle=140, \n    colors=colors, \n    explode=explode,\n    shadow=True\n)\n\nplt.title(\"Répartition des Moyennes de Ventes\\n(Impact des Promotions)\")\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-13T13:17:42.241293Z","iopub.execute_input":"2026-01-13T13:17:42.242119Z","iopub.status.idle":"2026-01-13T13:17:42.592393Z","shell.execute_reply.started":"2026-01-13T13:17:42.242083Z","shell.execute_reply":"2026-01-13T13:17:42.591378Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"L'analyse du camembert révèle que les promotions constituent le levier de performance le plus puissant de votre activité, capturant **62,5 %** de la part relative des ventes moyennes contre seulement **37,5 %** pour les produits vendus au prix normal. Ce déséquilibre visuel marqué traduit une réalité mathématique frappante : le passage en promotion fait bondir la moyenne des ventes de **8,09 à 13,49** unités, soit une croissance fulgurante de **67 %** par article. Contrairement à l'effet des jours fériés qui reste modéré, la promotion provoque une rupture de tendance majeure dans le comportement d'achat, ce qui en fait la variable prédictive indispensable pour garantir la disponibilité des stocks et optimiser la précision de votre futur modèle.","metadata":{}},{"cell_type":"code","source":"train_sample1 = train_sample[\n    np.isfinite(train_sample[\"unit_sales\"])\n]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-13T13:19:22.074377Z","iopub.execute_input":"2026-01-13T13:19:22.074830Z","iopub.status.idle":"2026-01-13T13:19:22.424144Z","shell.execute_reply.started":"2026-01-13T13:19:22.074780Z","shell.execute_reply":"2026-01-13T13:19:22.422904Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_sample[\"onpromotion\"] = (\n    train_sample[\"onpromotion\"]\n    .fillna(False)\n    .astype(bool)\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-13T13:19:26.354383Z","iopub.execute_input":"2026-01-13T13:19:26.354722Z","iopub.status.idle":"2026-01-13T13:19:26.557615Z","shell.execute_reply.started":"2026-01-13T13:19:26.354699Z","shell.execute_reply":"2026-01-13T13:19:26.556635Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import seaborn as sns\nimport matplotlib.pyplot as plt\n\nplt.figure(figsize=(7,5))\nsns.boxplot(\n    x=\"onpromotion\",\n    y=\"unit_sales\",\n    data=train_sample1\n)\nplt.title(\"Impact des promotions sur les ventes\")\nplt.xlabel(\"Produit en promotion\")\nplt.ylabel(\"log(1 + unit_sales)\")\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-13T13:40:41.555990Z","iopub.execute_input":"2026-01-13T13:40:41.557952Z","iopub.status.idle":"2026-01-13T13:40:44.360999Z","shell.execute_reply.started":"2026-01-13T13:40:41.557887Z","shell.execute_reply":"2026-01-13T13:40:44.359713Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Le graphique démontre visuellement que les promotions ont un effet positif direct sur le volume des transactions. La boîte correspondant aux produits en promotion (True, en vert) est globalement plus haute que celle des produits hors promotion (False, en bleu) : la médiane passe d'environ **1,6 à 2,0** en échelle logarithmique. Cela signifie qu'un produit vendu sous promotion a tendance à s'écouler en plus grande quantité par transaction. Bien que les deux groupes présentent de nombreuses valeurs extrêmes, la distribution des promotions est plus resserrée vers le haut.","metadata":{}},{"cell_type":"markdown","source":"## **8. Jours fériés (holidays_events.csv)**","metadata":{}},{"cell_type":"markdown","source":"### **Chargement et fusion**","metadata":{}},{"cell_type":"code","source":"holidays = pd.read_csv(\n    f\"{DATA_PATH}/holidays_events.csv\",\n    parse_dates=[\"date\"]\n)\n\nholidays.head()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-13T13:20:01.855931Z","iopub.execute_input":"2026-01-13T13:20:01.856856Z","iopub.status.idle":"2026-01-13T13:20:01.877187Z","shell.execute_reply.started":"2026-01-13T13:20:01.856789Z","shell.execute_reply":"2026-01-13T13:20:01.876204Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_holidays = train_sample.merge(\n    holidays[[\"date\",\"type\",\"transferred\"]],\n    on=\"date\",\n    how=\"left\"\n)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-13T13:23:21.380501Z","iopub.execute_input":"2026-01-13T13:23:21.381022Z","iopub.status.idle":"2026-01-13T13:23:22.156677Z","shell.execute_reply.started":"2026-01-13T13:23:21.380991Z","shell.execute_reply":"2026-01-13T13:23:22.155784Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_holidays.columns\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-13T13:20:13.279179Z","iopub.execute_input":"2026-01-13T13:20:13.279549Z","iopub.status.idle":"2026-01-13T13:20:13.287020Z","shell.execute_reply.started":"2026-01-13T13:20:13.279523Z","shell.execute_reply":"2026-01-13T13:20:13.285864Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### **Impact des jours fériés**","metadata":{}},{"cell_type":"code","source":"train_holidays[\"is_holiday\"] = train_holidays[\"type\"].notna()\n\n\n\n#train_holidays.groupby(\"is_holiday\")[\"unit_sales_clean\"].mean()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-13T13:23:31.753750Z","iopub.execute_input":"2026-01-13T13:23:31.754518Z","iopub.status.idle":"2026-01-13T13:23:31.823865Z","shell.execute_reply.started":"2026-01-13T13:23:31.754431Z","shell.execute_reply":"2026-01-13T13:23:31.822606Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import matplotlib.pyplot as plt\n\n# 1. Calcul des moyennes directement depuis ton DataFrame\n# On groupe par 'is_holiday' et on calcule la moyenne de 'unit_sales_clean'\nholiday_means = train_holidays.groupby(\"is_holiday\")[\"unit_sales\"].mean()\n\n# 2. Préparation des étiquettes (False -> Jours Normaux, True -> Jours Fériés)\nlabels = ['Jours Normaux', 'Jours Fériés']\ncolors = ['#aec7e8', '#ff7f0e']\nexplode = (0, 0.05) \n\n# 3. Génération du camembert\nplt.figure(figsize=(8, 6))\nplt.pie(\n    holiday_means, \n    labels=labels, \n    autopct='%1.1f%%', \n    startangle=140, \n    colors=colors, \n    explode=explode,\n    shadow=True\n)\n\nplt.title(\"Impact des Jours Fériés sur la Moyenne des Ventes\")\nplt.show()\n\n# Affichage des valeurs exactes calculées pour vérification\nprint(\"Moyennes calculées :\")\nprint(holiday_means)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-13T13:23:42.529659Z","iopub.execute_input":"2026-01-13T13:23:42.530042Z","iopub.status.idle":"2026-01-13T13:23:42.699885Z","shell.execute_reply.started":"2026-01-13T13:23:42.530018Z","shell.execute_reply":"2026-01-13T13:23:42.698932Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"L'analyse du camembert révèle que les jours fériés agissent comme un catalyseur de consommation, capturant **51,7 %** de la part relative des ventes moyennes contre **48,3 %** pour les jours normaux. Bien que la division du graphique semble équilibrée, cette légère prédominance confirme que le volume de transactions par article augmente systématiquement lors des festivités, passant de **8,48 à 9,06** unités. À l'échelle d'une chaîne de magasins traitant des millions de lignes, cet écart de **0,58** unité représente un surplus de stock critique à anticiper.","metadata":{}},{"cell_type":"markdown","source":"## **9. Transactions**","metadata":{}},{"cell_type":"code","source":"transactions = pd.read_csv(\n    f\"{DATA_PATH}/transactions.csv\",\n    parse_dates=[\"date\"]\n)\n\ntrain_trans = train_sample.merge(\n    transactions,\n    on=[\"date\",\"store_nbr\"],\n    how=\"left\"\n)\ntransactions.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-13T13:24:23.722244Z","iopub.execute_input":"2026-01-13T13:24:23.723291Z","iopub.status.idle":"2026-01-13T13:24:24.107077Z","shell.execute_reply.started":"2026-01-13T13:24:23.723084Z","shell.execute_reply":"2026-01-13T13:24:24.105699Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_trans[[\"unit_sales\",\"transactions\"]].corr()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-13T13:24:32.806901Z","iopub.execute_input":"2026-01-13T13:24:32.807274Z","iopub.status.idle":"2026-01-13T13:24:32.895514Z","shell.execute_reply.started":"2026-01-13T13:24:32.807249Z","shell.execute_reply":"2026-01-13T13:24:32.894354Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"L'analyse de la matrice de corrélation révèle un coefficient de **0,158** entre les ventes nettes (unit_sales_clean) et le volume total de transactions par magasin (transactions). Ce chiffre indique une corrélation positive mais très faible, ce qui signifie que l'affluence globale d'un point de vente n'est pas le moteur principal de l'achat d'un article spécifique. Mathématiquement, cela suggère que le comportement d'achat est davantage dicté par des facteurs propres au produit — tels que sa catégorie (Épicerie, Boissons), sa nature périssable ou sa présence en promotion — plutôt que par la simple densité de clients en magasin","metadata":{}},{"cell_type":"markdown","source":"## **10. Pétrole**","metadata":{}},{"cell_type":"code","source":"oil = pd.read_csv(\n    f\"{DATA_PATH}/oil.csv\",\n    parse_dates=[\"date\"]\n)\n\ntrain_oil = train_sample.merge(oil, on=\"date\", how=\"left\")\n\noil.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-13T13:24:49.586843Z","iopub.execute_input":"2026-01-13T13:24:49.587221Z","iopub.status.idle":"2026-01-13T13:24:49.794063Z","shell.execute_reply.started":"2026-01-13T13:24:49.587196Z","shell.execute_reply":"2026-01-13T13:24:49.792729Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_oil[[\"unit_sales\",\"dcoilwtico\"]].corr()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-13T13:24:57.672585Z","iopub.execute_input":"2026-01-13T13:24:57.672959Z","iopub.status.idle":"2026-01-13T13:24:57.762624Z","shell.execute_reply.started":"2026-01-13T13:24:57.672924Z","shell.execute_reply":"2026-01-13T13:24:57.761585Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"L'analyse finale de la matrice de corrélation révèle une absence quasi totale de relation linéaire entre les ventes nettes (unit_sales_clean) et le prix du pétrole brut (dcoilwtico), avec un coefficient extrêmement faible de **0,0079**. Ce résultat est crucial car il démontre que les fluctuations du marché pétrolier, bien qu'essentielles à l'économie nationale, n'influencent pas directement les comportements d'achat quotidiens des consommateurs en magasin. Contrairement aux leviers opérationnels comme les promotions ou la typologie des magasins, le prix du pétrole agit ici comme un bruit de fond macroéconomique sans pouvoir prédictif sur les quantités vendues à l'article. Pour l'optimisation du modèle de Machine Learning, cette découverte suggère qu'il est préférable d'écarter cette variable au profit d'indicateurs plus impactants tels que le calendrier des jours fériés ou les stratégies de remises.","metadata":{}},{"cell_type":"markdown","source":"## **11. Choix de l'année d'étude** \n### Pour bien mener l'étude, nous allons nous focaliser sur l'année 2017","metadata":{}},{"cell_type":"markdown","source":"### **Fonction filtre**","metadata":{}},{"cell_type":"code","source":"#convertir date en datetime\nfor df in [train, oil, transactions, holidays]:\n    df[\"date\"] = pd.to_datetime(df[\"date\"])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-13T13:26:11.257318Z","iopub.execute_input":"2026-01-13T13:26:11.258535Z","iopub.status.idle":"2026-01-13T13:26:15.199356Z","shell.execute_reply.started":"2026-01-13T13:26:11.258489Z","shell.execute_reply":"2026-01-13T13:26:15.196786Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#Filtre générique année 2017 (bonne pratique)\n\n\n\ndef filter_year(df, year=2017):\n    return df[df[\"date\"].dt.year == year].copy()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-13T13:26:36.075388Z","iopub.execute_input":"2026-01-13T13:26:36.075736Z","iopub.status.idle":"2026-01-13T13:26:36.083118Z","shell.execute_reply.started":"2026-01-13T13:26:36.075713Z","shell.execute_reply":"2026-01-13T13:26:36.080709Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### **Extraction des données**","metadata":{}},{"cell_type":"code","source":"train_2017 = filter_year(train, 2017)\n\nprint(train_2017.shape)\ntrain_2017.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-13T13:26:41.969847Z","iopub.execute_input":"2026-01-13T13:26:41.970374Z","iopub.status.idle":"2026-01-13T13:26:48.915433Z","shell.execute_reply.started":"2026-01-13T13:26:41.970343Z","shell.execute_reply":"2026-01-13T13:26:48.913876Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Pour les transactions\ntransactions_2017 = filter_year(transactions, 2017)\n#pour les items et stores\nitems_2017 = items.copy()\nstores_2017 = stores.copy()\n#holiday et oil\nholidays_2017 = filter_year(holidays, 2017)\nholidays_2017[\"is_holiday\"] = 1\n\noil_2017 = filter_year(oil, 2017)\n\n# Recommandé : propagation des valeurs manquantes\noil_2017[\"dcoilwtico\"] = oil_2017[\"dcoilwtico\"].ffill()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-13T13:27:08.371735Z","iopub.execute_input":"2026-01-13T13:27:08.372895Z","iopub.status.idle":"2026-01-13T13:27:08.386086Z","shell.execute_reply.started":"2026-01-13T13:27:08.372862Z","shell.execute_reply":"2026-01-13T13:27:08.384974Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### **Merges**","metadata":{}},{"cell_type":"code","source":"df = train_2017.merge(\n    items_2017,\n    on=\"item_nbr\",\n    how=\"left\"\n)\ndf = df.merge(\n    stores_2017,\n    on=\"store_nbr\",\n    how=\"left\"\n)\ndf = df.merge(\n    transactions_2017,\n    on=[\"date\", \"store_nbr\"],\n    how=\"left\"\n)\ndf = df.merge(\n    oil_2017,\n    on=\"date\",\n    how=\"left\"\n)\n\ndf = df.merge(\n    holidays_2017,\n    on=\"date\",\n    how=\"left\"\n)\n\ndf[\"is_holiday\"] = df[\"is_holiday\"].fillna(0).astype(int)\n\nBase_merge_2017=df","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-13T13:27:13.112715Z","iopub.execute_input":"2026-01-13T13:27:13.113082Z","iopub.status.idle":"2026-01-13T13:27:57.032507Z","shell.execute_reply.started":"2026-01-13T13:27:13.113045Z","shell.execute_reply":"2026-01-13T13:27:57.031583Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"Base_merge_2017.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-13T13:28:03.724629Z","iopub.execute_input":"2026-01-13T13:28:03.725717Z","iopub.status.idle":"2026-01-13T13:28:03.744891Z","shell.execute_reply.started":"2026-01-13T13:28:03.725682Z","shell.execute_reply":"2026-01-13T13:28:03.743661Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Generate descriptive statistics\nBase_merge_2017.describe(include=\"all\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-13T13:28:12.256919Z","iopub.execute_input":"2026-01-13T13:28:12.257251Z","iopub.status.idle":"2026-01-13T13:28:38.258984Z","shell.execute_reply.started":"2026-01-13T13:28:12.257228Z","shell.execute_reply":"2026-01-13T13:28:38.258023Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"L'analyse descriptive de l'année *2017* révèle une dynamique commerciale intense et contrastée, où la moyenne des ventes se stabilise à **8,17** unités par produit malgré une volatilité extrême illustrée par un écart-type de **24,39**. Cette période est marquée par une influence massive des promotions, qui concernent au moins **25 %** des transactions (75ème percentile à 1.0) et expliquent la part prédominante de **62,1 %** observée précédemment sur l'impact des moyennes. Tandis que le prix du pétrole reste relativement stable autour de **49,41 $** sans affecter directement la consommation, l'activité se concentre fortement dans les zones urbaines comme Quito et sur les magasins de *type D*, avec des pics d'affluence dépassant les *6 300 transactions* journalières. Ces statistiques confirment que pour 2017, la précision des prévisions reposera sur la gestion des valeurs extrêmes (allant jusqu'à 17 146 unités) et l'intégration rigoureuse du calendrier promotionnel et des **12,8 %** de jours fériés recensés.","metadata":{}},{"cell_type":"code","source":"from pathlib import Path\n\nimport matplotlib.pyplot as plt\nimport missingno as msno\nimport numpy as np\nimport pandas as pd\nimport plotly.express as px\nimport plotly.graph_objs as go\nimport seaborn as sns\nimport shap\nfrom plotly.subplots import make_subplots\nfrom ydata_profiling import ProfileReport\nfrom yellowbrick.regressor import ResidualsPlot","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-13T13:31:43.029788Z","iopub.execute_input":"2026-01-13T13:31:43.030829Z","iopub.status.idle":"2026-01-13T13:31:57.855654Z","shell.execute_reply.started":"2026-01-13T13:31:43.030765Z","shell.execute_reply":"2026-01-13T13:31:57.854229Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# A bar chart visualization of the nullity of the given DataFrame.\nmsno.bar(Base_merge_2017, figsize=(10, 5));","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-13T13:32:31.671347Z","iopub.execute_input":"2026-01-13T13:32:31.672955Z","iopub.status.idle":"2026-01-13T13:32:43.064835Z","shell.execute_reply.started":"2026-01-13T13:32:31.672918Z","shell.execute_reply":"2026-01-13T13:32:43.063668Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"L'analyse du graphique de complétude des données révèle une base de données globalement robuste, mais marquée par des disparités importantes de remplissage selon l'origine des variables fusionnées. Si le bloc principal des transactions et des caractéristiques produits (ventes, promotions, familles) est parfaitement complet avec **24 244 110** entrées, on observe une chute de densité significative pour les colonnes liées au prix du pétrole (dcoilwtico), qui ne couvre qu'environ **69 %** du jeu de données (16 745 961 entrées), et surtout pour les variables liées au calendrier des jours fériés (type_y, locale, etc.) qui ne sont renseignées que pour **12,8 %** des lignes. Ces zones de vide ne constituent pas des erreurs, mais reflètent la nature spécifique de ces informations : le pétrole n'est pas coté tous les jours (week-ends) et la majorité des journées de l'année ne sont pas fériées. Pour la suite du projet, il sera donc crucial de traiter ces valeurs manquantes, notamment en utilisant une interpolation pour le pétrole et un remplissage par une valeur par défaut pour les variables calendaires, afin d'exploiter pleinement leur potentiel prédictif sans perdre de données lors de l'entraînement.","metadata":{}},{"cell_type":"markdown","source":"### **Seaborn**","metadata":{}},{"cell_type":"code","source":"fig, axes = plt.subplots(1, 2, figsize=(15, 4))\n\n# 1️⃣ Distribution des ventes brutes (cappées)\nsns.histplot(\n    Base_merge_2017[\"unit_sales\"].clip(lower=0, upper=200),\n    bins=50,\n    ax=axes[0]\n)\naxes[0].set_title(\"Distribution - unit_sales (cap à 200)\")\n\n# 2️⃣ Distribution après transformation logarithmique\nsns.histplot(\n    np.log1p(Base_merge_2017[\"unit_sales\"].clip(lower=0)),\n    bins=50,\n    ax=axes[1]\n)\naxes[1].set_title(\"Distribution - log(1 + unit_sales)\")\n\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-13T13:32:52.821112Z","iopub.execute_input":"2026-01-13T13:32:52.822146Z","iopub.status.idle":"2026-01-13T13:33:26.150952Z","shell.execute_reply.started":"2026-01-13T13:32:52.822111Z","shell.execute_reply":"2026-01-13T13:33:26.149789Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Avant transformation, la quasi-totalité des observations est concentrée entre **0 et 25**unités, rendant le modèle aveugle aux variations de volume et trop sensible aux valeurs aberrantes. Après passage au logarithme, la distribution s'étale de manière plus uniforme sur une échelle de 0 à 10, ce qui réduit la variance et permet aux algorithmes de mieux capturer les relations linéaires avec les prédicteurs clés comme les promotions. Cette normalisation est particulièrement critique pour traiter les pics de ventes massifs observés en 2017, garantissant ainsi que ces événements ponctuels ne biaisent pas l'apprentissage global du modèle.","metadata":{}},{"cell_type":"code","source":"sns.boxplot(\n    Base_merge_2017[\"unit_sales\"].clip(0, 200),\n    orient=\"h\"\n)\nplt.title(\"Boxplot des ventes (cap à 200)\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-13T13:34:20.825175Z","iopub.execute_input":"2026-01-13T13:34:20.825568Z","iopub.status.idle":"2026-01-13T13:34:32.706127Z","shell.execute_reply.started":"2026-01-13T13:34:20.825542Z","shell.execute_reply":"2026-01-13T13:34:32.705013Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"cols = [\n    \"unit_sales\",\n    \"transactions\",\n    \"dcoilwtico\",\n    \"cluster\",\n    \"is_holiday\"\n]\n\n#sns.pairplot(\n   # Base_merge_2017[cols],\n    #hue=\"is_holiday\",\n    #palette=\"Paired\",\n    #corner=True,\n    #diag_kind=\"kde\"\n#)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-13T13:34:33.148557Z","iopub.execute_input":"2026-01-13T13:34:33.148935Z","iopub.status.idle":"2026-01-13T13:34:33.155145Z","shell.execute_reply.started":"2026-01-13T13:34:33.148911Z","shell.execute_reply":"2026-01-13T13:34:33.153533Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### **Correlation**","metadata":{}},{"cell_type":"code","source":"eda_corr = Base_merge_2017[\n    [\n        \"unit_sales\",\n        \"transactions\",\n        \"dcoilwtico\",\n        \"onpromotion\",\n        \"perishable\",\n        \"is_holiday\"\n    ]\n].copy()\n\n# Conversion bool → int\nfor col in [\"onpromotion\", \"perishable\", \"is_holiday\"]:\n    eda_corr[col] = eda_corr[col].astype(int)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-13T13:34:40.399596Z","iopub.execute_input":"2026-01-13T13:34:40.400926Z","iopub.status.idle":"2026-01-13T13:34:43.301374Z","shell.execute_reply.started":"2026-01-13T13:34:40.400879Z","shell.execute_reply":"2026-01-13T13:34:43.300198Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"corr = eda_corr.corr(method=\"pearson\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-13T13:34:47.433969Z","iopub.execute_input":"2026-01-13T13:34:47.435028Z","iopub.status.idle":"2026-01-13T13:34:51.739627Z","shell.execute_reply.started":"2026-01-13T13:34:47.434993Z","shell.execute_reply":"2026-01-13T13:34:51.738058Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plt.figure(figsize=(10, 7))\n\nmask = np.triu(np.ones_like(corr, dtype=bool))\n\nsns.heatmap(\n    corr,\n    cmap=\"Greens\",\n    annot=True,\n    fmt=\".3f\",\n    square=True,\n    mask=mask,\n    cbar=True,\n    vmin=-1,\n    vmax=1\n)\n\nplt.title(\"Matrice de corrélation – Variables clés des ventes (2017)\")\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-13T13:34:57.558382Z","iopub.execute_input":"2026-01-13T13:34:57.558782Z","iopub.status.idle":"2026-01-13T13:34:57.892290Z","shell.execute_reply.started":"2026-01-13T13:34:57.558755Z","shell.execute_reply":"2026-01-13T13:34:57.891047Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"L'analyse de la matrice de corrélation pour l'année *2017* confirme avec précision la hiérarchie des facteurs d'influence identifiés lors des étapes précédentes. Le moteur principal des ventes reste la variable **onpromotion**, qui présente la corrélation positive la plus significative avec les ventes **(0,082)**, corroborant visuellement le bond de performance de **67 %** observé sur le camembert des promotions. Les transactions globales affichent également un lien notable avec un coefficient de **0,134**, indiquant que l'affluence en magasin en 2017 a un impact plus marqué sur le volume de vente que sur l'ensemble du jeu de données historique. À l'inverse, le prix du pétrole (dcoilwtico) maintient une corrélation quasi nulle de **0,002** avec les ventes, validant définitivement son caractère non prédictif pour les transactions quotidiennes au détail. Enfin, la faible corrélation des jours fériés (0,007) suggère que leur impact, bien que réel sur la moyenne globale, est très dilué lorsqu'il est mesuré de manière linéaire sur l'ensemble des articles de 2017.","metadata":{}},{"cell_type":"markdown","source":"### **Vente globale en 2017**","metadata":{}},{"cell_type":"code","source":"daily_sales = (\n    Base_merge_2017.groupby(\"date\")[\"unit_sales\"]\n    .sum()\n    .reset_index()\n)\n\nplt.figure(figsize=(14,5))\nplt.plot(daily_sales[\"date\"], daily_sales[\"unit_sales\"])\nplt.title(\"Évolution globale des ventes\")\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-13T13:35:09.106039Z","iopub.execute_input":"2026-01-13T13:35:09.107157Z","iopub.status.idle":"2026-01-13T13:35:10.028049Z","shell.execute_reply.started":"2026-01-13T13:35:09.107120Z","shell.execute_reply":"2026-01-13T13:35:10.026781Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"L'analyse temporelle de l'année 2017 montre une activité cyclique stable ponctuée par des pics de demande exceptionnels qui redéfinissent la structure des ventes. On observe une saisonnalité hebdomadaire régulière où les ventes oscillent généralement entre **0,5 et 1,5 million d'unités**, mais le graphique est dominé par une explosion spectaculaire fin juin 2017, atteignant un record de plus de **3 millions** d'unités en une seule journée. Ce pic massif, couplé à la volatilité extrême relevée dans les statistiques descriptives (maximum de 17 146 unités par ligne), confirme que des événements ponctuels — probablement des promotions agressives ou des préparations de jours fériés locaux — ont un impact bien plus déstabilisant sur la chaîne logistique que les tendances macroéconomiques comme le **pétrole**. Cette visualisation justifie pleinement l'utilisation de la transformation logarithmique effectuée précédemment pour \"écraser\" ces pics extrêmes et permettre au modèle de capturer la tendance de fond sans être aveuglé par ces anomalies de volume.","metadata":{}},{"cell_type":"markdown","source":"### **Reportage profil**","metadata":{}},{"cell_type":"code","source":"import ydata_profiling as prf\nprf.__version__","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-13T13:35:16.574198Z","iopub.execute_input":"2026-01-13T13:35:16.574526Z","iopub.status.idle":"2026-01-13T13:35:16.581546Z","shell.execute_reply.started":"2026-01-13T13:35:16.574504Z","shell.execute_reply":"2026-01-13T13:35:16.580318Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Choix d'un echantillon**","metadata":{}},{"cell_type":"code","source":"Base_sample_2017 = Base_merge_2017.sample(2_000_000, random_state=42)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-13T13:35:23.606934Z","iopub.execute_input":"2026-01-13T13:35:23.607355Z","iopub.status.idle":"2026-01-13T13:35:25.869354Z","shell.execute_reply.started":"2026-01-13T13:35:23.607321Z","shell.execute_reply":"2026-01-13T13:35:25.867775Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Generate a profile report from a Dataset stored as a pandas `DataFrame`.\nprofile = ProfileReport(Base_sample_2017, title=\"Profiling Report\")\nprofile.to_notebook_iframe()  # Used to output the HTML representation to a Jupyter notebook.\n# profile.to_file(\"data_report.html\")  # Write the report to a file.","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-13T13:35:41.858076Z","iopub.execute_input":"2026-01-13T13:35:41.858437Z","iopub.status.idle":"2026-01-13T13:38:04.519222Z","shell.execute_reply.started":"2026-01-13T13:35:41.858412Z","shell.execute_reply":"2026-01-13T13:38:04.517905Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Génération du rapport de profiling au format HTML\nprofile.to_file(\"profiling_report.html\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-13T13:52:56.201741Z","iopub.execute_input":"2026-01-13T13:52:56.203060Z","iopub.status.idle":"2026-01-13T13:52:56.244268Z","shell.execute_reply.started":"2026-01-13T13:52:56.203019Z","shell.execute_reply":"2026-01-13T13:52:56.242669Z"}},"outputs":[],"execution_count":null}]}