{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "title : \"Introduction aux structures de données pandas : les DataFrames (tableaux bidimensionnels)\"\n",
    "---"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Commençons par importer la bibliothèque Pandas (et la bibliotèque NumPy) avec les alias classiques `pd` et `np` :"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:17.193416Z",
     "iopub.status.busy": "2026-10-30T13:14:17.192332Z",
     "iopub.status.idle": "2026-10-30T13:14:17.511614Z",
     "shell.execute_reply": "2026-10-30T13:14:17.510670Z"
    }
   },
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "import numpy as np\n",
    "\n",
    "# Pour l'affichage dans les notebooks\n",
    "from IPython.display import display, Markdown"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "La deuxième structure fondamentale de Pandas est le `DataFrame`. Elle peut être considérée comme une généralisation d'une matrice NumPy, où les lignes et les colonnes sont identifiées par des étiquettes, ou encore comme un dictionnaire de Series partageant le même index."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Contrairement à une matrice, les colonnes peuvent être de types différents.\n",
    "\n",
    "Un DataFrame est composé des éléments suivants :\n",
    "\n",
    "- l’indice de la ligne ;\n",
    "- le nom de la colonne ;\n",
    "- la valeur de la donnée ;\n",
    "\n",
    "![DataFrame Pandas](pandasDF.png)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:17.514992Z",
     "iopub.status.busy": "2026-10-30T13:14:17.514635Z",
     "iopub.status.idle": "2026-10-30T13:14:17.518450Z",
     "shell.execute_reply": "2026-10-30T13:14:17.517726Z"
    }
   },
   "outputs": [],
   "source": [
    "# ?pd.DataFrame"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Création d'un DataFrame"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "On peut créer un DataFrame \n",
    "\n",
    "- à partir d'une matrice NumPy (en précisant éventuellement les noms des colonnes et des lignes)\n",
    "\n",
    "- à partir d'un fichier CSV, Excel, SQL, etc.\n",
    "\n",
    "- colonne par colonne à partir\n",
    "    - d'une seule Series \n",
    "    - d'un dictionnaire de Series ou d'un dictionnaire de listes (chaque élément du dictionnaire est une colonne)\n",
    "    \n",
    "- ligne par ligne à partir\n",
    "    - d'une liste de Series ou d'une liste de dictionnaires (chaque élément de la liste est une ligne)\n",
    "    "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## À partir d'un tableau NumPy à deux dimensions"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "On peut aussi créer un DataFrame à partir d'un tableau NumPy à deux dimensions. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:17.521160Z",
     "iopub.status.busy": "2026-10-30T13:14:17.520916Z",
     "iopub.status.idle": "2026-10-30T13:14:17.534533Z",
     "shell.execute_reply": "2026-10-30T13:14:17.533841Z"
    }
   },
   "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>0</th>\n",
       "      <th>1</th>\n",
       "      <th>2</th>\n",
       "      <th>3</th>\n",
       "      <th>4</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>100</td>\n",
       "      <td>50</td>\n",
       "      <td>20</td>\n",
       "      <td>100</td>\n",
       "      <td>50</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>1000</td>\n",
       "      <td>300</td>\n",
       "      <td>400</td>\n",
       "      <td>50</td>\n",
       "      <td>200</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>10</td>\n",
       "      <td>20</td>\n",
       "      <td>5</td>\n",
       "      <td>200</td>\n",
       "      <td>25</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "      0    1    2    3    4\n",
       "0   100   50   20  100   50\n",
       "1  1000  300  400   50  200\n",
       "2    10   20    5  200   25"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ARRAY = np.array([ [100,   50,  20, 100,  50], \n",
    "                   [1000, 300, 400,  50, 200], \n",
    "                   [10,    20,   5, 200,  25] ])\n",
    "\n",
    "df3 = pd.DataFrame( data=ARRAY )\n",
    "df3"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "On peut spécifier les noms des colonnes et des lignes :"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:17.570456Z",
     "iopub.status.busy": "2026-10-30T13:14:17.570274Z",
     "iopub.status.idle": "2026-10-30T13:14:17.576749Z",
     "shell.execute_reply": "2026-10-30T13:14:17.576192Z"
    }
   },
   "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>Facebook</th>\n",
       "      <th>Twitter</th>\n",
       "      <th>Instagram</th>\n",
       "      <th>Linkedin</th>\n",
       "      <th>Snapchat</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>Budget</th>\n",
       "      <td>100</td>\n",
       "      <td>50</td>\n",
       "      <td>20</td>\n",
       "      <td>100</td>\n",
       "      <td>50</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Audience</th>\n",
       "      <td>1000</td>\n",
       "      <td>300</td>\n",
       "      <td>400</td>\n",
       "      <td>50</td>\n",
       "      <td>200</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>CPM</th>\n",
       "      <td>10</td>\n",
       "      <td>20</td>\n",
       "      <td>5</td>\n",
       "      <td>200</td>\n",
       "      <td>25</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "          Facebook  Twitter  Instagram  Linkedin  Snapchat\n",
       "Budget         100       50         20       100        50\n",
       "Audience      1000      300        400        50       200\n",
       "CPM             10       20          5       200        25"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ARRAY = np.array([ [100,   50,  20, 100,  50], \n",
    "                   [1000, 300, 400,  50, 200], \n",
    "                   [10,    20,   5, 200,  25] ])\n",
    "columns = [\"Facebook\", \"Twitter\", \"Instagram\", \"Linkedin\", \"Snapchat\"]\n",
    "indices = [\"Budget\", \"Audience\", \"CPM\"]\n",
    "\n",
    "df3 = pd.DataFrame( data=ARRAY, \n",
    "                    columns=columns, \n",
    "                    index=indices\n",
    "                    )\n",
    "df3"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:17.578777Z",
     "iopub.status.busy": "2026-10-30T13:14:17.578266Z",
     "iopub.status.idle": "2026-10-30T13:14:17.583766Z",
     "shell.execute_reply": "2026-10-30T13:14:17.583274Z"
    }
   },
   "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>Budget</th>\n",
       "      <th>Audience</th>\n",
       "      <th>CPM</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>Facebook</th>\n",
       "      <td>100</td>\n",
       "      <td>1000</td>\n",
       "      <td>10</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Twitter</th>\n",
       "      <td>50</td>\n",
       "      <td>300</td>\n",
       "      <td>20</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Instagram</th>\n",
       "      <td>20</td>\n",
       "      <td>400</td>\n",
       "      <td>5</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Linkedin</th>\n",
       "      <td>100</td>\n",
       "      <td>50</td>\n",
       "      <td>200</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Snapchat</th>\n",
       "      <td>50</td>\n",
       "      <td>200</td>\n",
       "      <td>25</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "           Budget  Audience  CPM\n",
       "Facebook      100      1000   10\n",
       "Twitter        50       300   20\n",
       "Instagram      20       400    5\n",
       "Linkedin      100        50  200\n",
       "Snapchat       50       200   25"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df4 = df3.T\n",
    "df4 "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Colonne par colonne à partir ..."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### ... d'une seule Series"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:17.585566Z",
     "iopub.status.busy": "2026-10-30T13:14:17.585378Z",
     "iopub.status.idle": "2026-10-30T13:14:17.593091Z",
     "shell.execute_reply": "2026-10-30T13:14:17.592633Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Suisse       8.516\n",
       "France      67.060\n",
       "USA        328.200\n",
       "Chine     1386.000\n",
       "dtype: float64"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "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>Population</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>Suisse</th>\n",
       "      <td>8.516</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>France</th>\n",
       "      <td>67.060</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>USA</th>\n",
       "      <td>328.200</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Chine</th>\n",
       "      <td>1386.000</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "        Population\n",
       "Suisse       8.516\n",
       "France      67.060\n",
       "USA        328.200\n",
       "Chine     1386.000"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "population = pd.Series( data=[8.516, 67.06, 328.2, 1_386],           \n",
    "                        index=[\"Suisse\", \"France\", \"USA\", \"Chine\"] # les index seront les labels des lignes\n",
    "                        )\n",
    "\n",
    "display( population )\n",
    "# df0 = pd.DataFrame( data=population )\n",
    "df0 = pd.DataFrame( data=population, \n",
    "                    columns=[\"Population\"] \n",
    "                    )\n",
    "display(df0)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:17.594911Z",
     "iopub.status.busy": "2026-10-30T13:14:17.594735Z",
     "iopub.status.idle": "2026-10-30T13:14:17.603645Z",
     "shell.execute_reply": "2026-10-30T13:14:17.603114Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[   8.516],\n",
       "       [  67.06 ],\n",
       "       [ 328.2  ],\n",
       "       [1386.   ]])"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "Index(['Suisse', 'France', 'USA', 'Chine'], dtype='object')"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "Index(['Population'], dtype='object')"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "Suisse       8.516\n",
       "France      67.060\n",
       "USA        328.200\n",
       "Chine     1386.000\n",
       "Name: Population, dtype: float64"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "8.516"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "display(df0.values) # un array 2D numpy\n",
    "display(df0.index) # un objet de type Index\n",
    "display(df0.columns) # un objet de type Index\n",
    "display(df0[\"Population\"]) # une Series\n",
    "display(df0[\"Population\"][\"Suisse\"]) # DataFrame[\"colonne\"][\"ligne\"]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Si les éléments `index` et `columns` d’un DataFrame ont leurs attributs name définis, ceux-ci seront également affichés :"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:17.605476Z",
     "iopub.status.busy": "2026-10-30T13:14:17.605264Z",
     "iopub.status.idle": "2026-10-30T13:14:17.610318Z",
     "shell.execute_reply": "2026-10-30T13:14:17.609799Z"
    }
   },
   "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>Population</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>États</th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>Suisse</th>\n",
       "      <td>8.516</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>France</th>\n",
       "      <td>67.060</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>USA</th>\n",
       "      <td>328.200</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Chine</th>\n",
       "      <td>1386.000</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "        Population\n",
       "États             \n",
       "Suisse       8.516\n",
       "France      67.060\n",
       "USA        328.200\n",
       "Chine     1386.000"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df0.index.name = \"États\"\n",
    "df0"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:17.612208Z",
     "iopub.status.busy": "2026-10-30T13:14:17.611977Z",
     "iopub.status.idle": "2026-10-30T13:14:17.616895Z",
     "shell.execute_reply": "2026-10-30T13:14:17.616479Z"
    }
   },
   "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>Caractéristiques</th>\n",
       "      <th>Population</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>États</th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>Suisse</th>\n",
       "      <td>8.516</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>France</th>\n",
       "      <td>67.060</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>USA</th>\n",
       "      <td>328.200</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Chine</th>\n",
       "      <td>1386.000</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "Caractéristiques  Population\n",
       "États                       \n",
       "Suisse                 8.516\n",
       "France                67.060\n",
       "USA                  328.200\n",
       "Chine               1386.000"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df0.columns.name = \"Caractéristiques\"\n",
    "df0"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:17.618693Z",
     "iopub.status.busy": "2026-10-30T13:14:17.618508Z",
     "iopub.status.idle": "2026-10-30T13:14:17.624642Z",
     "shell.execute_reply": "2026-10-30T13:14:17.624165Z"
    }
   },
   "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>États</th>\n",
       "      <th>Suisse</th>\n",
       "      <th>France</th>\n",
       "      <th>USA</th>\n",
       "      <th>Chine</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Caractéristiques</th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>Population</th>\n",
       "      <td>8.516</td>\n",
       "      <td>67.06</td>\n",
       "      <td>328.2</td>\n",
       "      <td>1386.0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "États             Suisse  France    USA   Chine\n",
       "Caractéristiques                               \n",
       "Population         8.516   67.06  328.2  1386.0"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df0.T # transposée, comme en numpy"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### ... à partir d'un dictionnaire de Series `{label_1: serie_1, label_2: serie_2, etc}` ou de listes de valeurs `{label_1: [val_1, val_2, etc], label_2: [val_1, val_2, etc], etc}`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Chaque Series devient une **colonne** du DataFrame.\n",
    "\n",
    "Les clés du dictionnaire sont les noms des colonnes et les valeurs associées sont les Series ou listes de valeurs pour chaque colonne.   \n",
    "Les index peuvent être passés ou générés automatiquement.   \n",
    "Depuis Python 3.7, les dictionnaires conservent l'ordre d'insertion des clés : les colonnes du DataFrame apparaissent donc dans l'ordre où elles ont été écrites dans le dictionnaire."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:17.626422Z",
     "iopub.status.busy": "2026-10-30T13:14:17.626242Z",
     "iopub.status.idle": "2026-10-30T13:14:17.632690Z",
     "shell.execute_reply": "2026-10-30T13:14:17.632207Z"
    }
   },
   "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>Population</th>\n",
       "      <th>Area</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>Suisse</th>\n",
       "      <td>8.516</td>\n",
       "      <td>41285</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>France</th>\n",
       "      <td>67.060</td>\n",
       "      <td>551695</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>USA</th>\n",
       "      <td>328.200</td>\n",
       "      <td>3796742</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Chine</th>\n",
       "      <td>1386.000</td>\n",
       "      <td>9596961</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "        Population     Area\n",
       "Suisse       8.516    41285\n",
       "France      67.060   551695\n",
       "USA        328.200  3796742\n",
       "Chine     1386.000  9596961"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "population = pd.Series([8.516, 67.06, 328.2, 1_386],            index=[\"Suisse\", \"France\", \"USA\", \"Chine\"])\n",
    "area       = pd.Series([41_285, 551_695, 3_796_742, 9_596_961], index=[\"Suisse\", \"France\", \"USA\", \"Chine\"])\n",
    "\n",
    "df1 = pd.DataFrame( { \"Population\" : population, \n",
    "                      \"Area\" : area } )\n",
    "df1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:17.634459Z",
     "iopub.status.busy": "2026-10-30T13:14:17.634275Z",
     "iopub.status.idle": "2026-10-30T13:14:17.641484Z",
     "shell.execute_reply": "2026-10-30T13:14:17.640986Z"
    }
   },
   "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>RS</th>\n",
       "      <th>Budget</th>\n",
       "      <th>Audience</th>\n",
       "      <th>CPM</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>Facebook</td>\n",
       "      <td>100</td>\n",
       "      <td>1000</td>\n",
       "      <td>10</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>Twitter</td>\n",
       "      <td>50</td>\n",
       "      <td>300</td>\n",
       "      <td>20</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>Instagram</td>\n",
       "      <td>20</td>\n",
       "      <td>400</td>\n",
       "      <td>5</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>Linkedin</td>\n",
       "      <td>100</td>\n",
       "      <td>50</td>\n",
       "      <td>200</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>Snapchat</td>\n",
       "      <td>50</td>\n",
       "      <td>200</td>\n",
       "      <td>25</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "          RS  Budget  Audience  CPM\n",
       "0   Facebook     100      1000   10\n",
       "1    Twitter      50       300   20\n",
       "2  Instagram      20       400    5\n",
       "3   Linkedin     100        50  200\n",
       "4   Snapchat      50       200   25"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "dico = { \"RS\"      : [\"Facebook\", \"Twitter\", \"Instagram\", \"Linkedin\", \"Snapchat\"],\n",
    "         \"Budget\"  : [100, 50, 20, 100, 50],\n",
    "         \"Audience\": [1000, 300, 400, 50, 200],\n",
    "         \"CPM\"     : [10, 20, 5, 200, 25] }\n",
    "df1bis = pd.DataFrame(dico)\n",
    "df1bis"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:17.643799Z",
     "iopub.status.busy": "2026-10-30T13:14:17.643259Z",
     "iopub.status.idle": "2026-10-30T13:14:17.646778Z",
     "shell.execute_reply": "2026-10-30T13:14:17.646397Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Instagram'"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "display(df1bis[\"RS\"][2]) # dangereux : est-ce un label ou une position ?\n",
    "\n",
    "# solution : utiliser .loc pour les labels ou .iloc pour les positions, on retrouve\n",
    "# l'ordre habituel de numpy\n",
    "# display(df1bis.loc[2, \"RS\"])\n",
    "# display(df1bis.iloc[2, 0])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Ligne par ligne à partir ..."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### ... d'une liste de Series `[serie_1, serie_2, etc]` ou d'une liste de dictionnaires `[dico_1, dico_2, etc]`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Chaque Series/Dictionnaire devient une **ligne** du DataFrame."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:17.648575Z",
     "iopub.status.busy": "2026-10-30T13:14:17.648424Z",
     "iopub.status.idle": "2026-10-30T13:14:17.655817Z",
     "shell.execute_reply": "2026-10-30T13:14:17.655434Z"
    }
   },
   "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>Suisse</th>\n",
       "      <th>France</th>\n",
       "      <th>USA</th>\n",
       "      <th>Chine</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>Population</th>\n",
       "      <td>8.516</td>\n",
       "      <td>67.06</td>\n",
       "      <td>328.2</td>\n",
       "      <td>1386.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Area</th>\n",
       "      <td>41285.000</td>\n",
       "      <td>551695.00</td>\n",
       "      <td>3796742.0</td>\n",
       "      <td>9596961.0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "               Suisse     France        USA      Chine\n",
       "Population      8.516      67.06      328.2     1386.0\n",
       "Area        41285.000  551695.00  3796742.0  9596961.0"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "population = pd.Series(data=[8.516, 67.06, 328.2, 1_386],            index=[\"Suisse\", \"France\", \"USA\", \"Chine\"])\n",
    "area       = pd.Series(data=[41_285, 551_695, 3_796_742, 9_596_961], index=[\"Suisse\", \"France\", \"USA\", \"Chine\"])\n",
    "df1 = pd.DataFrame( data=[population, area], \n",
    "                    index=[\"Population\", \"Area\"])\n",
    "df1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:17.657388Z",
     "iopub.status.busy": "2026-10-30T13:14:17.657229Z",
     "iopub.status.idle": "2026-10-30T13:14:17.663662Z",
     "shell.execute_reply": "2026-10-30T13:14:17.663267Z"
    }
   },
   "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>Suisse</th>\n",
       "      <th>France</th>\n",
       "      <th>USA</th>\n",
       "      <th>Chine</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>Population</th>\n",
       "      <td>8.516</td>\n",
       "      <td>67.06</td>\n",
       "      <td>328.2</td>\n",
       "      <td>1386</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Area</th>\n",
       "      <td>41285.000</td>\n",
       "      <td>551695.00</td>\n",
       "      <td>3796742.0</td>\n",
       "      <td>9596961</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "               Suisse     France        USA    Chine\n",
       "Population      8.516      67.06      328.2     1386\n",
       "Area        41285.000  551695.00  3796742.0  9596961"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "population = {\"Suisse\":8.516, \"France\":67.06, \"USA\":328.2, \"Chine\":1_386}\n",
    "area       = {\"Suisse\":41_285, \"France\":551_695, \"USA\":3_796_742, \"Chine\":9_596_961}\n",
    "df2 = pd.DataFrame( data=[population, area], \n",
    "                    index=[\"Population\", \"Area\"])\n",
    "df2"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Si les deux dictionnaires ne contiennent pas les mêmes clés, les valeurs manquantes sont remplacées par `NaN` :"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:17.665248Z",
     "iopub.status.busy": "2026-10-30T13:14:17.665094Z",
     "iopub.status.idle": "2026-10-30T13:14:17.671696Z",
     "shell.execute_reply": "2026-10-30T13:14:17.671304Z"
    }
   },
   "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>Suisse</th>\n",
       "      <th>France</th>\n",
       "      <th>USA</th>\n",
       "      <th>Chine</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>Population</th>\n",
       "      <td>8.516</td>\n",
       "      <td>67.06</td>\n",
       "      <td>328.2</td>\n",
       "      <td>NaN</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Area</th>\n",
       "      <td>41285.000</td>\n",
       "      <td>551695.00</td>\n",
       "      <td>NaN</td>\n",
       "      <td>9596961.0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "               Suisse     France    USA      Chine\n",
       "Population      8.516      67.06  328.2        NaN\n",
       "Area        41285.000  551695.00    NaN  9596961.0"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "population = {\"Suisse\":8.516, \"France\":67.06, \"USA\":328.2}\n",
    "area       = {\"Suisse\":41_285, \"France\":551_695, \"Chine\":9_596_961}\n",
    "df2bis = pd.DataFrame( data=[population, area], \n",
    "                       index=[\"Population\", \"Area\"])\n",
    "df2bis"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:17.673265Z",
     "iopub.status.busy": "2026-10-30T13:14:17.673113Z",
     "iopub.status.idle": "2026-10-30T13:14:17.680379Z",
     "shell.execute_reply": "2026-10-30T13:14:17.679983Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[8.516000e+00, 6.706000e+01, 3.282000e+02,          nan],\n",
       "       [4.128500e+04, 5.516950e+05,          nan, 9.596961e+06]])"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "Index(['Population', 'Area'], dtype='object')"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "Index(['Suisse', 'France', 'USA', 'Chine'], dtype='object')"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "Population        67.06\n",
       "Area          551695.00\n",
       "Name: France, dtype: float64"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "67.06"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "display(df2bis.values) # tableau numpy à 2 dimensions\n",
    "display(df2bis.index) # objet de type Index\n",
    "display(df2bis.columns) # objet de type Index\n",
    "display(df2bis[\"France\"]) # DataFrame[\"colonne\"] renvoie une Series !!!! NB\n",
    "display(df2bis[\"France\"][\"Population\"]) # DataFrame[\"colonne\"][\"ligne\"] !!! NB l'ordre"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:17.682134Z",
     "iopub.status.busy": "2026-10-30T13:14:17.681944Z",
     "iopub.status.idle": "2026-10-30T13:14:17.687377Z",
     "shell.execute_reply": "2026-10-30T13:14:17.686968Z"
    }
   },
   "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>Population</th>\n",
       "      <th>Area</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>Suisse</th>\n",
       "      <td>8.516</td>\n",
       "      <td>41285.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>France</th>\n",
       "      <td>67.060</td>\n",
       "      <td>551695.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>USA</th>\n",
       "      <td>328.200</td>\n",
       "      <td>NaN</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Chine</th>\n",
       "      <td>NaN</td>\n",
       "      <td>9596961.0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "        Population       Area\n",
       "Suisse       8.516    41285.0\n",
       "France      67.060   551695.0\n",
       "USA        328.200        NaN\n",
       "Chine          NaN  9596961.0"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df2bis.T # transposée, comme en numpy"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## À partir d'un fichier : `read_csv`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Dans la pratique, on construit rarement un DataFrame à la main : on le **lit depuis un fichier**. Pandas sait lire de très nombreux formats : `read_excel`, `read_json`, `read_sql`, `read_parquet`, `read_html`… : et chacun a sa fonction d'écriture correspondante (`to_excel`, `to_json`, etc.). Nous nous limiterons au format le plus répandu, le **CSV**.\n",
    "\n",
    "Un fichier **CSV** (*Comma Separated Values*) est un simple fichier texte : une ligne par observation, des champs séparés par un caractère convenu. C'est à la fois son grand avantage : n'importe quel tableur, n'importe quel langage sait le lire : et sa grande faiblesse : **rien n'y est normalisé**, ni le séparateur, ni le marqueur décimal, ni l'encodage. Lire un CSV correctement, c'est donc d'abord regarder à quoi il ressemble.\n",
    "\n",
    "Fabriquons-en un pour l'exemple :"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:17.689170Z",
     "iopub.status.busy": "2026-10-30T13:14:17.688961Z",
     "iopub.status.idle": "2026-10-30T13:14:17.692758Z",
     "shell.execute_reply": "2026-10-30T13:14:17.692362Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'ville;population;superficie;recensement\\nToulon;176 198;42,84;2021-01-01\\nMarseille;873 076;240,62;2021-01-01\\nNice;342 669;71,92;2021-01-01\\nHyères;57 635;132,38;\\nLa Seyne-sur-Mer;65 691;22,02;2021-01-01\\n'"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "contenu = \"\"\"ville;population;superficie;recensement\n",
    "Toulon;176 198;42,84;2021-01-01\n",
    "Marseille;873 076;240,62;2021-01-01\n",
    "Nice;342 669;71,92;2021-01-01\n",
    "Hyères;57 635;132,38;\n",
    "La Seyne-sur-Mer;65 691;22,02;2021-01-01\n",
    "\"\"\"\n",
    "\n",
    "with open(\"villes_demo.csv\", \"w\", encoding=\"utf-8\") as f:\n",
    "    f.write(contenu)\n",
    "\n",
    "display(contenu)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ce petit fichier cumule les habitudes françaises : séparateur `;`, **virgule** décimale, **espace** comme séparateur de milliers, et une date manquante. Voyons ce que donne une lecture naïve."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:17.694758Z",
     "iopub.status.busy": "2026-10-30T13:14:17.694507Z",
     "iopub.status.idle": "2026-10-30T13:14:17.701378Z",
     "shell.execute_reply": "2026-10-30T13:14:17.700838Z"
    }
   },
   "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>ville;population;superficie;recensement</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>Toulon;176 198;42</th>\n",
       "      <td>84;2021-01-01</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Marseille;873 076;240</th>\n",
       "      <td>62;2021-01-01</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Nice;342 669;71</th>\n",
       "      <td>92;2021-01-01</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Hyères;57 635;132</th>\n",
       "      <td>38;</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>La Seyne-sur-Mer;65 691;22</th>\n",
       "      <td>02;2021-01-01</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                           ville;population;superficie;recensement\n",
       "Toulon;176 198;42                                    84;2021-01-01\n",
       "Marseille;873 076;240                                62;2021-01-01\n",
       "Nice;342 669;71                                      92;2021-01-01\n",
       "Hyères;57 635;132                                              38;\n",
       "La Seyne-sur-Mer;65 691;22                           02;2021-01-01"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pd.read_csv(\"villes_demo.csv\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Une seule colonne ! C'est normal : `read_csv` attend par défaut la **virgule** comme séparateur. On le corrige avec `sep` :"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:17.703096Z",
     "iopub.status.busy": "2026-10-30T13:14:17.702932Z",
     "iopub.status.idle": "2026-10-30T13:14:17.711012Z",
     "shell.execute_reply": "2026-10-30T13:14:17.710520Z"
    }
   },
   "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>ville</th>\n",
       "      <th>population</th>\n",
       "      <th>superficie</th>\n",
       "      <th>recensement</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>Toulon</td>\n",
       "      <td>176 198</td>\n",
       "      <td>42,84</td>\n",
       "      <td>2021-01-01</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>Marseille</td>\n",
       "      <td>873 076</td>\n",
       "      <td>240,62</td>\n",
       "      <td>2021-01-01</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>Nice</td>\n",
       "      <td>342 669</td>\n",
       "      <td>71,92</td>\n",
       "      <td>2021-01-01</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>Hyères</td>\n",
       "      <td>57 635</td>\n",
       "      <td>132,38</td>\n",
       "      <td>NaN</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>La Seyne-sur-Mer</td>\n",
       "      <td>65 691</td>\n",
       "      <td>22,02</td>\n",
       "      <td>2021-01-01</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "              ville population superficie recensement\n",
       "0            Toulon    176 198      42,84  2021-01-01\n",
       "1         Marseille    873 076     240,62  2021-01-01\n",
       "2              Nice    342 669      71,92  2021-01-01\n",
       "3            Hyères     57 635     132,38         NaN\n",
       "4  La Seyne-sur-Mer     65 691      22,02  2021-01-01"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "ville          object\n",
       "population     object\n",
       "superficie     object\n",
       "recensement    object\n",
       "dtype: object"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "villes_csv = pd.read_csv(\"villes_demo.csv\", sep=\";\")\n",
    "\n",
    "display(villes_csv)\n",
    "display(villes_csv.dtypes)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Le tableau a la bonne forme, mais regardez les types : `population` et `superficie` sont de type `object`, c'est-à-dire **du texte**. Impossible d'en calculer la moyenne. Deux raisons : l'espace des milliers et la virgule décimale, que pandas ne devine pas. On les déclare :"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:17.712691Z",
     "iopub.status.busy": "2026-10-30T13:14:17.712538Z",
     "iopub.status.idle": "2026-10-30T13:14:17.720970Z",
     "shell.execute_reply": "2026-10-30T13:14:17.720490Z"
    }
   },
   "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>ville</th>\n",
       "      <th>population</th>\n",
       "      <th>superficie</th>\n",
       "      <th>recensement</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>Toulon</td>\n",
       "      <td>176198</td>\n",
       "      <td>42.84</td>\n",
       "      <td>2021-01-01</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>Marseille</td>\n",
       "      <td>873076</td>\n",
       "      <td>240.62</td>\n",
       "      <td>2021-01-01</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>Nice</td>\n",
       "      <td>342669</td>\n",
       "      <td>71.92</td>\n",
       "      <td>2021-01-01</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>Hyères</td>\n",
       "      <td>57635</td>\n",
       "      <td>132.38</td>\n",
       "      <td>NaN</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>La Seyne-sur-Mer</td>\n",
       "      <td>65691</td>\n",
       "      <td>22.02</td>\n",
       "      <td>2021-01-01</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "              ville  population  superficie recensement\n",
       "0            Toulon      176198       42.84  2021-01-01\n",
       "1         Marseille      873076      240.62  2021-01-01\n",
       "2              Nice      342669       71.92  2021-01-01\n",
       "3            Hyères       57635      132.38         NaN\n",
       "4  La Seyne-sur-Mer       65691       22.02  2021-01-01"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "ville           object\n",
       "population       int64\n",
       "superficie     float64\n",
       "recensement     object\n",
       "dtype: object"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "villes_csv = pd.read_csv(\"villes_demo.csv\", sep=\";\", thousands=\" \", decimal=\",\")\n",
    "\n",
    "display(villes_csv)\n",
    "display(villes_csv.dtypes)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Il reste la colonne `recensement`, toujours lue comme du texte. `parse_dates` la convertit en véritables dates : et la valeur absente devient `NaT` (*Not a Time*), l'équivalent de `NaN` pour les dates :"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:17.722780Z",
     "iopub.status.busy": "2026-10-30T13:14:17.722626Z",
     "iopub.status.idle": "2026-10-30T13:14:17.732874Z",
     "shell.execute_reply": "2026-10-30T13:14:17.732342Z"
    }
   },
   "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>ville</th>\n",
       "      <th>population</th>\n",
       "      <th>superficie</th>\n",
       "      <th>recensement</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>Toulon</td>\n",
       "      <td>176198</td>\n",
       "      <td>42.84</td>\n",
       "      <td>2021-01-01</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>Marseille</td>\n",
       "      <td>873076</td>\n",
       "      <td>240.62</td>\n",
       "      <td>2021-01-01</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>Nice</td>\n",
       "      <td>342669</td>\n",
       "      <td>71.92</td>\n",
       "      <td>2021-01-01</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>Hyères</td>\n",
       "      <td>57635</td>\n",
       "      <td>132.38</td>\n",
       "      <td>NaT</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>La Seyne-sur-Mer</td>\n",
       "      <td>65691</td>\n",
       "      <td>22.02</td>\n",
       "      <td>2021-01-01</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "              ville  population  superficie recensement\n",
       "0            Toulon      176198       42.84  2021-01-01\n",
       "1         Marseille      873076      240.62  2021-01-01\n",
       "2              Nice      342669       71.92  2021-01-01\n",
       "3            Hyères       57635      132.38         NaT\n",
       "4  La Seyne-sur-Mer       65691       22.02  2021-01-01"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "ville                  object\n",
       "population              int64\n",
       "superficie            float64\n",
       "recensement    datetime64[ns]\n",
       "dtype: object"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "villes_csv = pd.read_csv(\"villes_demo.csv\", sep=\";\", thousands=\" \", decimal=\",\",\n",
    "                         parse_dates=[\"recensement\"])\n",
    "\n",
    "display(villes_csv)\n",
    "display(villes_csv.dtypes)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Enfin, `index_col` désigne directement la colonne qui servira d'index :"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:17.734552Z",
     "iopub.status.busy": "2026-10-30T13:14:17.734397Z",
     "iopub.status.idle": "2026-10-30T13:14:17.741933Z",
     "shell.execute_reply": "2026-10-30T13:14:17.741391Z"
    }
   },
   "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>population</th>\n",
       "      <th>superficie</th>\n",
       "      <th>recensement</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>ville</th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>Toulon</th>\n",
       "      <td>176198</td>\n",
       "      <td>42.84</td>\n",
       "      <td>2021-01-01</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Marseille</th>\n",
       "      <td>873076</td>\n",
       "      <td>240.62</td>\n",
       "      <td>2021-01-01</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Nice</th>\n",
       "      <td>342669</td>\n",
       "      <td>71.92</td>\n",
       "      <td>2021-01-01</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Hyères</th>\n",
       "      <td>57635</td>\n",
       "      <td>132.38</td>\n",
       "      <td>NaT</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>La Seyne-sur-Mer</th>\n",
       "      <td>65691</td>\n",
       "      <td>22.02</td>\n",
       "      <td>2021-01-01</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                  population  superficie recensement\n",
       "ville                                               \n",
       "Toulon                176198       42.84  2021-01-01\n",
       "Marseille             873076      240.62  2021-01-01\n",
       "Nice                  342669       71.92  2021-01-01\n",
       "Hyères                 57635      132.38         NaT\n",
       "La Seyne-sur-Mer       65691       22.02  2021-01-01"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "villes_csv = pd.read_csv(\"villes_demo.csv\", sep=\";\", thousands=\" \", decimal=\",\",\n",
    "                         parse_dates=[\"recensement\"], index_col=\"ville\")\n",
    "villes_csv"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Les options à connaître\n",
    "\n",
    "| Option | Rôle |\n",
    "|---|---|\n",
    "| `sep` | caractère séparant les champs (`,` par défaut, souvent `;` en France) |\n",
    "| `decimal` | marqueur décimal (`.` par défaut, `,` en France) |\n",
    "| `thousands` | séparateur de milliers à supprimer |\n",
    "| `encoding` | `\"utf-8\"` par défaut ; essayez `\"latin-1\"` sur un vieux fichier Windows |\n",
    "| `index_col` | colonne à utiliser comme index |\n",
    "| `usecols` | liste des colonnes à charger : précieux sur un gros fichier |\n",
    "| `na_values` | valeurs à interpréter comme manquantes (`\"N/A\"`, `\"-\"`, `\"inconnu\"`…) |\n",
    "| `parse_dates` | colonnes à convertir en dates |\n",
    "| `dtype` | forcer le type d'une ou plusieurs colonnes |\n",
    "| `nrows` | ne lire que les premières lignes, pour inspecter un gros fichier |\n",
    "| `skiprows` | ignorer des lignes d'en-tête parasites |\n",
    "| `header` | numéro de la ligne d'en-tête, ou `None` s'il n'y en a pas |"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div class=\"alert alert-block alert-danger\">\n",
    "\n",
    "**Le réflexe à prendre.** Ouvrez d'abord le fichier dans un éditeur de texte et regardez ses premières lignes ; écrivez l'appel à `read_csv` **ensuite**. Puis vérifiez systématiquement `.dtypes` : une colonne numérique lue comme `object` est de loin le symptôme le plus fréquent d'un mauvais paramétrage, et toutes vos statistiques en dépendent.\n",
    "\n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Un cas réel : l'espace insécable\n",
    "\n",
    "Les exports de l'INSEE séparent les milliers par un **espace insécable**, un caractère invisible qui ressemble à une espace mais n'en est pas une (son code Unicode est 160). Vous le rencontrerez dans l'exercice sur la population des communes."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:17.743544Z",
     "iopub.status.busy": "2026-10-30T13:14:17.743389Z",
     "iopub.status.idle": "2026-10-30T13:14:17.749921Z",
     "shell.execute_reply": "2026-10-30T13:14:17.749425Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "commune       object\n",
       "population    object\n",
       "dtype: object"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/markdown": [
       "valeur brute : `'176\\xa0198'`"
      ],
      "text/plain": [
       "<IPython.core.display.Markdown object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "NBSP = chr(160)   # l'espace insécable\n",
    "\n",
    "with open(\"insee_demo.csv\", \"w\", encoding=\"utf-8\") as f:\n",
    "    f.write(f\"commune;population\\nToulon;176{NBSP}198\\nNice;342{NBSP}669\\n\")\n",
    "\n",
    "brut = pd.read_csv(\"insee_demo.csv\", sep=\";\")\n",
    "\n",
    "display(brut.dtypes)\n",
    "display(Markdown(\"valeur brute : `\" + repr(brut[\"population\"][0]) + \"`\"))   # le \\xa0 est bien là"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:17.751681Z",
     "iopub.status.busy": "2026-10-30T13:14:17.751525Z",
     "iopub.status.idle": "2026-10-30T13:14:17.761591Z",
     "shell.execute_reply": "2026-10-30T13:14:17.761108Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "commune       object\n",
       "population    object\n",
       "dtype: object"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "commune       object\n",
       "population     int64\n",
       "dtype: object"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "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>commune</th>\n",
       "      <th>population</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>Toulon</td>\n",
       "      <td>176198</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>Nice</td>\n",
       "      <td>342669</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "  commune  population\n",
       "0  Toulon      176198\n",
       "1    Nice      342669"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# thousands ne connaît que l'espace ORDINAIRE : il ne règle pas le problème\n",
    "display(pd.read_csv(\"insee_demo.csv\", sep=\";\", thousands=\" \").dtypes)\n",
    "\n",
    "# il faut nettoyer la colonne à la main, puis convertir le type\n",
    "propre = pd.read_csv(\"insee_demo.csv\", sep=\";\")\n",
    "propre[\"population\"] = propre[\"population\"].str.replace(chr(160), \"\", regex=False).astype(int)\n",
    "\n",
    "display(propre.dtypes)\n",
    "display(propre)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exporter : `to_csv`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "L'écriture suit la même logique, avec les mêmes options :"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:17.763344Z",
     "iopub.status.busy": "2026-10-30T13:14:17.763190Z",
     "iopub.status.idle": "2026-10-30T13:14:17.767996Z",
     "shell.execute_reply": "2026-10-30T13:14:17.767542Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/markdown": [
       "```\n",
       "ville;population;superficie;recensement\n",
       "Toulon;176198;42,84;2021-01-01\n",
       "Marseille;873076;240,62;2021-01-01\n",
       "Nice;342669;71,92;2021-01-01\n",
       "Hyères;57635;132,38;\n",
       "La Seyne-sur-Mer;65691;22,02;2021-01-01\n",
       "\n",
       "```"
      ],
      "text/plain": [
       "<IPython.core.display.Markdown object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "villes_csv.to_csv(\"villes_export.csv\", sep=\";\", decimal=\",\", encoding=\"utf-8\")\n",
    "\n",
    "display(Markdown(\"```\\n\" + open(\"villes_export.csv\", encoding=\"utf-8\").read() + \"\\n```\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Par défaut l'index est écrit dans le fichier. Si c'est une simple numérotation sans signification, on l'omet avec `index=False` :"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:17.769864Z",
     "iopub.status.busy": "2026-10-30T13:14:17.769679Z",
     "iopub.status.idle": "2026-10-30T13:14:17.774817Z",
     "shell.execute_reply": "2026-10-30T13:14:17.774426Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/markdown": [
       "```\n",
       "ville;population;superficie;recensement\n",
       "Toulon;176198;42,84;2021-01-01\n",
       "Marseille;873076;240,62;2021-01-01\n",
       "Nice;342669;71,92;2021-01-01\n",
       "Hyères;57635;132,38;\n",
       "La Seyne-sur-Mer;65691;22,02;2021-01-01\n",
       "\n",
       "```"
      ],
      "text/plain": [
       "<IPython.core.display.Markdown object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "villes_csv.reset_index().to_csv(\"villes_export.csv\", sep=\";\", decimal=\",\", index=False)\n",
    "\n",
    "display(Markdown(\"```\\n\" + open(\"villes_export.csv\", encoding=\"utf-8\").read() + \"\\n```\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Bonus : manipuler des dates"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div class=\"alert alert-block alert-warning\">\n",
    "\n",
    "**Bonus : hors séance.** Les dates sont omniprésentes dans les jeux de données ouverts ; cette section vous servira dès que vous en rencontrerez.\n",
    "\n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`parse_dates` convertit les dates **au moment de la lecture**. Quand la colonne a déjà été chargée comme du texte, `pd.to_datetime` fait la conversion après coup.\n",
    "\n",
    "Une fois la colonne de type `datetime64`, l'accesseur **`.dt`** donne accès à l'année, au mois, au jour de la semaine : exactement comme `.str` donne accès aux méthodes de chaînes."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:17.776447Z",
     "iopub.status.busy": "2026-10-30T13:14:17.776295Z",
     "iopub.status.idle": "2026-10-30T13:14:17.781869Z",
     "shell.execute_reply": "2026-10-30T13:14:17.781490Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "dtype('O')"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "dtype('<M8[ns]')"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "0   2021-01-15\n",
       "1   2021-06-30\n",
       "2   2022-03-08\n",
       "dtype: datetime64[ns]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "inscriptions = pd.Series([\"2021-01-15\", \"2021-06-30\", \"2022-03-08\"])\n",
    "display(inscriptions.dtype)      # object : c'est du texte\n",
    "\n",
    "inscriptions = pd.to_datetime(inscriptions)\n",
    "display(inscriptions.dtype)      # datetime64[ns]\n",
    "display(inscriptions)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:17.783440Z",
     "iopub.status.busy": "2026-10-30T13:14:17.783290Z",
     "iopub.status.idle": "2026-10-30T13:14:17.789250Z",
     "shell.execute_reply": "2026-10-30T13:14:17.788763Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0    2021\n",
       "1    2021\n",
       "2    2022\n",
       "dtype: int32"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "0    1\n",
       "1    6\n",
       "2    3\n",
       "dtype: int32"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "0       Friday\n",
       "1    Wednesday\n",
       "2      Tuesday\n",
       "dtype: object"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "display(inscriptions.dt.year)\n",
    "display(inscriptions.dt.month)\n",
    "display(inscriptions.dt.day_name())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Si le format n'est pas reconnu automatiquement : typiquement le format français `jj/mm/aaaa` : on le précise avec `format` :"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:17.790966Z",
     "iopub.status.busy": "2026-10-30T13:14:17.790813Z",
     "iopub.status.idle": "2026-10-30T13:14:17.794947Z",
     "shell.execute_reply": "2026-10-30T13:14:17.794543Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0   2021-01-15\n",
       "1   2021-06-30\n",
       "dtype: datetime64[ns]"
      ]
     },
     "execution_count": 31,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pd.to_datetime(pd.Series([\"15/01/2021\", \"30/06/2021\"]), format=\"%d/%m/%Y\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "On peut alors filtrer et regrouper sur le temps :"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:17.796612Z",
     "iopub.status.busy": "2026-10-30T13:14:17.796461Z",
     "iopub.status.idle": "2026-10-30T13:14:17.804545Z",
     "shell.execute_reply": "2026-10-30T13:14:17.804015Z"
    }
   },
   "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>date</th>\n",
       "      <th>montant</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>2021-06-30</td>\n",
       "      <td>25</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>2022-03-08</td>\n",
       "      <td>40</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "        date  montant\n",
       "1 2021-06-30       25\n",
       "2 2022-03-08       40"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "date\n",
       "2021    35\n",
       "2022    40\n",
       "Name: montant, dtype: int64"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "df_dates = pd.DataFrame({ \"date\"    : inscriptions,\n",
    "                          \"montant\" : [10, 25, 40] })\n",
    "\n",
    "display(df_dates[ df_dates[\"date\"] >= \"2021-06-01\" ])\n",
    "display(df_dates.groupby(df_dates[\"date\"].dt.year)[\"montant\"].sum())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Accés aux éléments"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:17.806398Z",
     "iopub.status.busy": "2026-10-30T13:14:17.806141Z",
     "iopub.status.idle": "2026-10-30T13:14:17.809485Z",
     "shell.execute_reply": "2026-10-30T13:14:17.808960Z"
    }
   },
   "outputs": [],
   "source": [
    "population = pd.Series([8.516, 67.06, 328.2, 1_386],            index=[\"Suisse\", \"France\", \"USA\", \"Chine\"])\n",
    "area       = pd.Series([41_285, 551_695, 3_796_742, 9_596_961], index=[\"Suisse\", \"France\", \"USA\", \"Chine\"])\n",
    "\n",
    "df1 = pd.DataFrame( { \"Population\" : population, \n",
    "                      \"Area\" : area } )\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:17.811273Z",
     "iopub.status.busy": "2026-10-30T13:14:17.811127Z",
     "iopub.status.idle": "2026-10-30T13:14:17.815781Z",
     "shell.execute_reply": "2026-10-30T13:14:17.815316Z"
    }
   },
   "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>Population</th>\n",
       "      <th>Area</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>Suisse</th>\n",
       "      <td>8.516</td>\n",
       "      <td>41285</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>France</th>\n",
       "      <td>67.060</td>\n",
       "      <td>551695</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>USA</th>\n",
       "      <td>328.200</td>\n",
       "      <td>3796742</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Chine</th>\n",
       "      <td>1386.000</td>\n",
       "      <td>9596961</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "        Population     Area\n",
       "Suisse       8.516    41285\n",
       "France      67.060   551695\n",
       "USA        328.200  3796742\n",
       "Chine     1386.000  9596961"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "display(df1) "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:17.817462Z",
     "iopub.status.busy": "2026-10-30T13:14:17.817250Z",
     "iopub.status.idle": "2026-10-30T13:14:17.822204Z",
     "shell.execute_reply": "2026-10-30T13:14:17.821795Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[8.516000e+00, 4.128500e+04],\n",
       "       [6.706000e+01, 5.516950e+05],\n",
       "       [3.282000e+02, 3.796742e+06],\n",
       "       [1.386000e+03, 9.596961e+06]])"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "Index(['Suisse', 'France', 'USA', 'Chine'], dtype='object')"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "Index(['Population', 'Area'], dtype='object')"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "display(df1.values)   # tableau numpy à 2 dimensions\n",
    "display(df1.index)    # objet de type Index\n",
    "display(df1.columns)  # objet de type Index"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Selectionner une colonne : `df['nom_colonne']` ou `df.nom_colonne`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Pour séléctionner la colonne `col1` d'un DataFrame on peut utiliser `df.col1`, mais on préfèrera généralement `df[\"col1\"]`. En effet, la première syntaxe ne fonctionne pas si le nom de la colonne contient des espaces ou des caractères spéciaux, ou si le nom de la colonne est identique à une méthode ou un attribut du DataFrame.  \n",
    "Le résultat est une Series."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:17.823817Z",
     "iopub.status.busy": "2026-10-30T13:14:17.823668Z",
     "iopub.status.idle": "2026-10-30T13:14:17.830427Z",
     "shell.execute_reply": "2026-10-30T13:14:17.829813Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Suisse       8.516\n",
       "France      67.060\n",
       "USA        328.200\n",
       "Chine     1386.000\n",
       "Name: Population, dtype: float64"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "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>Population</th>\n",
       "      <th>Area</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>Suisse</th>\n",
       "      <td>8.516</td>\n",
       "      <td>41285</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>France</th>\n",
       "      <td>67.060</td>\n",
       "      <td>551695</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>USA</th>\n",
       "      <td>328.200</td>\n",
       "      <td>3796742</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Chine</th>\n",
       "      <td>1386.000</td>\n",
       "      <td>9596961</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "        Population     Area\n",
       "Suisse       8.516    41285\n",
       "France      67.060   551695\n",
       "USA        328.200  3796742\n",
       "Chine     1386.000  9596961"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# La colonne \"Population\" : c'est une Series\n",
    "display(df1[\"Population\"])\n",
    "\n",
    "# Les colonnes \"Population\" et \"Area\" : c'est un DataFrame\n",
    "display(df1[[\"Population\", \"Area\"]])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div class=\"alert alert-block alert-danger\">\n",
    "\n",
    "**Attention :**  \n",
    "\n",
    "- dans un tableau **numpy** à 2 dimensions, `A[0]` renvoi la première **ligne** du tableau `A`\n",
    "- pour un objet **DataFrame**, l'écriture `A[0]` renvoi la première **colonne**. \n",
    "\n",
    "C'est pourquoi il vaut mieux considérer un objet Dataframe comme un dictionnaire dont les clés sont les labels des colonnes plutôt que comme un tableau NumPy.\n",
    "\n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Une fois une colonne selectionnée (c'est une série), on peut selecionner un élément :\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:17.832144Z",
     "iopub.status.busy": "2026-10-30T13:14:17.831984Z",
     "iopub.status.idle": "2026-10-30T13:14:17.836904Z",
     "shell.execute_reply": "2026-10-30T13:14:17.836498Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "67.06"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "67.06"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "67.06"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# La valeur de la cellule \"Population\" de la ligne \"France\"\n",
    "col1 = \"Population\"\n",
    "row1 = \"France\"\n",
    "num_row1 = 1\n",
    "\n",
    "display( df1[col1][row1] )\n",
    "# display( df1[col1][num_row1] ) # deprecated\n",
    "display( df1[col1].loc[row1] )\n",
    "display( df1[col1].iloc[num_row1] )"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Puisque les colonnes sont des Series, on peut utiliser les méthodes de Series sur les colonnes d'un DataFrame. En particulier, tout ce que l'on a dit sur le slicing des Series est valable pour les colonnes d'un DataFrame."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:17.838548Z",
     "iopub.status.busy": "2026-10-30T13:14:17.838391Z",
     "iopub.status.idle": "2026-10-30T13:14:17.844476Z",
     "shell.execute_reply": "2026-10-30T13:14:17.843989Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "France     67.06\n",
       "USA       328.20\n",
       "Name: Population, dtype: float64"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "Suisse       8.516\n",
       "France      67.060\n",
       "USA        328.200\n",
       "Chine     1386.000\n",
       "Name: Population, dtype: float64"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "#  SLICING\n",
    "# =========\n",
    "\n",
    "# Slicing sur les lignes: les valeurs de la colonne \"Population\" des lignes de \"France\" à \"USA\" inclus\n",
    "display(df1[\"Population\"][\"France\":\"USA\"])\n",
    "\n",
    "# La colonne \"Population\", toutes les lignes\n",
    "display(df1[\"Population\"][:]) # idem que df1[\"Population\"]\n",
    "\n",
    "# Les valeurs de la ligne France\n",
    "# ERROR : on ne peut pas extraire une ligne avec [:] !!!\n",
    "# display(df1[:][\"France\"]) "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Selectionner une ligne : `loc` et `iloc`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Si on veut selectionner une ligne, on utilise \n",
    "\n",
    "- soit la méthode `.loc[label]` qui  utilise les labels et est la méthode recommandée ; \n",
    "- soit la méthode `.iloc[indice]` qui utilise les indices de position (de $0$ à $N$ où $N$ est égal à `df.shape[0]`) et qui est deconsillée car les indices peuvent changer si on modifie le DataFrame. \n",
    "\n",
    "Dans les deux cas, on obtient une Series dont les index sont les noms des colonnes. Ensuite, on pourra utiliser les méthodes de Series pour accéder aux éléments de la ligne.\n",
    "\n",
    "**NB** Avec `loc` ou `iloc`, on peut accéder à un élément d'un DataFrame avec la syntaxe `df.loc[label_ligne, nom_colonne]` ou `df.iloc[indice_ligne, indice_colonne]` et retrouver ainsi l'ordre d'indexation des lignes et des colonnes de numpy : `.iloc[i,j]` renvoi l'élément de la ligne `i` et de la colonne `j`. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:17.846081Z",
     "iopub.status.busy": "2026-10-30T13:14:17.845915Z",
     "iopub.status.idle": "2026-10-30T13:14:17.850575Z",
     "shell.execute_reply": "2026-10-30T13:14:17.850170Z"
    }
   },
   "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>Population</th>\n",
       "      <th>Area</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>Suisse</th>\n",
       "      <td>8.516</td>\n",
       "      <td>41285</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>France</th>\n",
       "      <td>67.060</td>\n",
       "      <td>551695</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>USA</th>\n",
       "      <td>328.200</td>\n",
       "      <td>3796742</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Chine</th>\n",
       "      <td>1386.000</td>\n",
       "      <td>9596961</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "        Population     Area\n",
       "Suisse       8.516    41285\n",
       "France      67.060   551695\n",
       "USA        328.200  3796742\n",
       "Chine     1386.000  9596961"
      ]
     },
     "execution_count": 39,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:17.852285Z",
     "iopub.status.busy": "2026-10-30T13:14:17.852128Z",
     "iopub.status.idle": "2026-10-30T13:14:17.864284Z",
     "shell.execute_reply": "2026-10-30T13:14:17.863871Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Population        67.06\n",
       "Area          551695.00\n",
       "Name: France, dtype: float64"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "67.06"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "France     67.06\n",
       "USA       328.20\n",
       "Name: Population, dtype: float64"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "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>Population</th>\n",
       "      <th>Area</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>France</th>\n",
       "      <td>67.06</td>\n",
       "      <td>551695</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>USA</th>\n",
       "      <td>328.20</td>\n",
       "      <td>3796742</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "        Population     Area\n",
       "France       67.06   551695\n",
       "USA         328.20  3796742"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "Suisse       8.516\n",
       "France      67.060\n",
       "USA        328.200\n",
       "Chine     1386.000\n",
       "Name: Population, dtype: float64"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "Suisse       8.516\n",
       "France      67.060\n",
       "USA        328.200\n",
       "Chine     1386.000\n",
       "Name: Population, dtype: float64"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Selectionner avec .loc (label de ligne)\n",
    "# ========================================================\n",
    "\n",
    "# Une ligne\n",
    "display(df1.loc[\"France\"]) # la ligne \"France\"\n",
    "\n",
    "# Une cellule\n",
    "display(df1.loc[\"France\", \"Population\"]) # la valeur de la cellule \"Population\" de la ligne \"France\"\n",
    "\n",
    "# Un slicing de lignes, une colonne\n",
    "display(df1.loc[\"France\":\"USA\", \"Population\"]) # les valeurs de la colonne \"Population\" des lignes de \"France\" à \"USA\" inclus\n",
    "\n",
    "# Un slicing de lignes, plusieurs colonnes\n",
    "display(df1.loc[\"France\":\"USA\", [\"Population\", \"Area\"]]) # les valeurs des colonnes \"Population\" et \"Area\" des lignes de \"France\" à \"USA\" inclus\n",
    "\n",
    "# Toutes les valeurs d'une colonne\n",
    "display(df1.loc[:, \"Population\"]) # toutes les valeurs de la colonne \"Population\", idem que df1[\"Population\"]\n",
    "display(df1.iloc[:, 0]) # toutes les valeurs de la colonne \"Population\", idem que df1[\"Population\"]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:17.865949Z",
     "iopub.status.busy": "2026-10-30T13:14:17.865792Z",
     "iopub.status.idle": "2026-10-30T13:14:17.877461Z",
     "shell.execute_reply": "2026-10-30T13:14:17.877073Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Population        67.06\n",
       "Area          551695.00\n",
       "Name: France, dtype: float64"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "Population        67.06\n",
       "Area          551695.00\n",
       "Name: France, dtype: float64"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "67.06"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "France     67.06\n",
       "USA       328.20\n",
       "Name: Population, dtype: float64"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "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>Population</th>\n",
       "      <th>Area</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>France</th>\n",
       "      <td>67.06</td>\n",
       "      <td>551695</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>USA</th>\n",
       "      <td>328.20</td>\n",
       "      <td>3796742</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "        Population     Area\n",
       "France       67.06   551695\n",
       "USA         328.20  3796742"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "Suisse       8.516\n",
       "France      67.060\n",
       "USA        328.200\n",
       "Chine     1386.000\n",
       "Name: Population, dtype: float64"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Selectionner avec .iloc (indices de position)\n",
    "# ========================================================\n",
    "\n",
    "# Une ligne\n",
    "display(df1.iloc[1])   # la ligne d'indice 1, toutes les colonnes\n",
    "display(df1.iloc[1,:]) # la ligne d'indice 1, toutes les colonnes\n",
    "\n",
    "# Une cellule\n",
    "display(df1.iloc[1, 0]) # la valeur de la cellule d'indice 1, 0\n",
    "\n",
    "# Un slicing de lignes, une colonne\n",
    "display(df1.iloc[1:3, 0]) # les valeurs de la colonne \"Population\" des lignes d'indice 1 à 3 exclus\n",
    "\n",
    "# Un slicing de lignes, plusieurs colonnes\n",
    "display(df1.iloc[1:3, [0, 1]]) # les valeurs des colonnes \"Population\" et \"Area\" des lignes d'indice 1 à 3 exclus\n",
    "\n",
    "# Toutes les valeurs d'une colonne\n",
    "display(df1.iloc[:, 0]) # toutes les valeurs de la colonne \"Population\"  idem que df1[\"Population\"]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "On peut alors utiliser les masques de numpy etc."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:17.879202Z",
     "iopub.status.busy": "2026-10-30T13:14:17.879045Z",
     "iopub.status.idle": "2026-10-30T13:14:17.887715Z",
     "shell.execute_reply": "2026-10-30T13:14:17.887320Z"
    }
   },
   "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>Population</th>\n",
       "      <th>Area</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>Suisse</th>\n",
       "      <td>8.516</td>\n",
       "      <td>41285</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>France</th>\n",
       "      <td>67.060</td>\n",
       "      <td>551695</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>USA</th>\n",
       "      <td>328.200</td>\n",
       "      <td>3796742</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Chine</th>\n",
       "      <td>1386.000</td>\n",
       "      <td>9596961</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "        Population     Area\n",
       "Suisse       8.516    41285\n",
       "France      67.060   551695\n",
       "USA        328.200  3796742\n",
       "Chine     1386.000  9596961"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "Suisse    False\n",
       "France    False\n",
       "USA        True\n",
       "Chine      True\n",
       "Name: Population, dtype: bool"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "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>Population</th>\n",
       "      <th>Area</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>USA</th>\n",
       "      <td>328.2</td>\n",
       "      <td>3796742</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Chine</th>\n",
       "      <td>1386.0</td>\n",
       "      <td>9596961</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "       Population     Area\n",
       "USA         328.2  3796742\n",
       "Chine      1386.0  9596961"
      ]
     },
     "execution_count": 42,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "display(df1)\n",
    "\n",
    "# les lignes dont la Population est supérieure à 100, les colonnes \"Population\" et \"Area\"\n",
    "# df1.loc[ df1[\"Population\"] > 100, [\"Population\", \"Area\"] ] \n",
    "mask = df1[\"Population\"] > 100\n",
    "display(mask) # une Series de booléens\n",
    "df1.loc[ mask, : ] "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Tableau comparatif entre `numpy.ndarray` et `pandas.DataFrame`\n",
    "\n",
    "| Aspect | `numpy.ndarray` | `pandas.DataFrame` |\n",
    "|-------|------------------|--------------------|\n",
    "| Nature | Tableau N-D homogène | Tableau 2D étiqueté (lignes + colonnes) |\n",
    "| Types des données | ✔️ Un seul type par array | ✔️ Un type **par colonne** |\n",
    "| Ordonnancement | ✔️ Ordonné | ✔️ Ordonné (index + colonnes) |\n",
    "| Labels | ❌ Aucun | ✔️ Noms de colonnes + index |\n",
    "| Accès positionnel | ✔️ `arr[i, j]` | ✔️ `df.iloc[i, j]` |\n",
    "| Accès par label | ❌ | ✔️ `df.loc[row_label, col_label]` ou `df.at[row_label, col_label]` |\n",
    "| Accès par colonne | ❌ | ✔️ `df[\"nom_colonne\"]` (retourne une Series) |\n",
    "| Accès par ligne | ❌ | ✔️ `df.iloc[i]` (par position), `df.loc[row_label]` (par label) |\n",
    "| Slicing lignes | ✔️ `arr[1:4]` | ✔️ `df.iloc[1:4]` ou `df.loc[\"y\":\"z\"]` |\n",
    "| Slicing colonnes | ✔️ `arr[:, 1:3]` | ✔️ `df[[\"col1\",\"col2\"]]` ou `df.loc[:, \"col1\":\"col3\"]` |\n",
    "| Vectorisation | ✔️ Très rapide | ✔️ Repose sur NumPy |\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Résumé des principales syntaxes d'accès aux éléments d'un DataFrame :\n",
    "\n",
    "\n",
    "| Syntaxe                | Description                                                                                               |\n",
    "|------------------------|-----------------------------------------------------------------------------------------------------------|\n",
    "| `df[eti_col]`              | Sélectionne une colonne ou une séquence de colonnes. Peut aussi servir pour filtres booléens ou slices.    |\n",
    "| `df.loc[eti_ligne]`    | Sélectionne une ou plusieurs lignes par leur **étiquette de ligne** (`eti_ligne`).                         |\n",
    "| `df.loc[:, eti_col]`   | Sélectionne une ou plusieurs colonnes par leurs **étiquettes de colonnes** (`eti_col`).                    |\n",
    "| `df.loc[eti_ligne, eti_col]` | Sélectionne lignes (`eti_ligne`) et colonnes (`eti_col`) par leurs **étiquettes**.                   |\n",
    "| `df.iloc[pos]`         | Sélectionne une ou plusieurs lignes par leur **position entière** (`pos`).                                |\n",
    "| `df.iloc[:, pos]`      | Sélectionne une ou plusieurs colonnes par leur **position entière** (`pos`).                              |\n",
    "| `df.iloc[pos_i, pos_j]`| Sélectionne lignes (`pos_i`) et colonnes (`pos_j`) par leurs **positions entières**.                       |\n",
    "| `df.at[eti_ligne, eti_col]` | Accède à une **valeur scalaire unique** par étiquette de ligne (`eti_ligne`) et de colonne (`eti_col`). |\n",
    "| `df.iat[i, j]`         | Accède à une **valeur scalaire unique** par position entière de ligne (`i`) et de colonne (`j`).           |\n",
    "| Méthode `reindex`      | Réorganise ou sélectionne lignes/colonnes par leurs **étiquettes** (`eti_ligne` et/ou `eti_col`).          |\n",
    "| Méthodes `get_value` / `set_value` | Accède ou modifie une valeur unique par étiquette (**obsolètes**, utilisez `at`/`iat`).         |\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Ajout de lignes/colonnes"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Ajout d'une colonne"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "On ajoute une colonne à un DataFrame en utilisant la notation `df['nom_colonne'] = serie`, eventuellement en utilisant une colonne existante pour calculer la nouvelle colonne."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:17.889462Z",
     "iopub.status.busy": "2026-10-30T13:14:17.889308Z",
     "iopub.status.idle": "2026-10-30T13:14:17.894504Z",
     "shell.execute_reply": "2026-10-30T13:14:17.894121Z"
    }
   },
   "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>Population</th>\n",
       "      <th>Area</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>Suisse</th>\n",
       "      <td>8.516</td>\n",
       "      <td>41285</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>France</th>\n",
       "      <td>67.060</td>\n",
       "      <td>551695</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>USA</th>\n",
       "      <td>328.200</td>\n",
       "      <td>3796742</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Chine</th>\n",
       "      <td>1386.000</td>\n",
       "      <td>9596961</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "        Population     Area\n",
       "Suisse       8.516    41285\n",
       "France      67.060   551695\n",
       "USA        328.200  3796742\n",
       "Chine     1386.000  9596961"
      ]
     },
     "execution_count": 43,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df1 = pd.DataFrame( { \"Population\" : population, \n",
    "                      \"Area\" : area } )\n",
    "df1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:17.896204Z",
     "iopub.status.busy": "2026-10-30T13:14:17.896018Z",
     "iopub.status.idle": "2026-10-30T13:14:17.901688Z",
     "shell.execute_reply": "2026-10-30T13:14:17.901298Z"
    }
   },
   "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>Population</th>\n",
       "      <th>Area</th>\n",
       "      <th>Density</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>Suisse</th>\n",
       "      <td>8.516</td>\n",
       "      <td>41285</td>\n",
       "      <td>0.000206</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>France</th>\n",
       "      <td>67.060</td>\n",
       "      <td>551695</td>\n",
       "      <td>0.000122</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>USA</th>\n",
       "      <td>328.200</td>\n",
       "      <td>3796742</td>\n",
       "      <td>0.000086</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Chine</th>\n",
       "      <td>1386.000</td>\n",
       "      <td>9596961</td>\n",
       "      <td>0.000144</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "        Population     Area   Density\n",
       "Suisse       8.516    41285  0.000206\n",
       "France      67.060   551695  0.000122\n",
       "USA        328.200  3796742  0.000086\n",
       "Chine     1386.000  9596961  0.000144"
      ]
     },
     "execution_count": 44,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df1[\"Density\"] = df1[\"Population\"] / df1[\"Area\"]\n",
    "df1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Les colonnes sont toujours ajoutées à la fin du DataFrame. Si on veut spécifier une position (et décaler les autres colonnes) on utilise la méthode `.insert()` :"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:17.903346Z",
     "iopub.status.busy": "2026-10-30T13:14:17.903197Z",
     "iopub.status.idle": "2026-10-30T13:14:17.909347Z",
     "shell.execute_reply": "2026-10-30T13:14:17.908879Z"
    }
   },
   "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>DensityBIS</th>\n",
       "      <th>Population</th>\n",
       "      <th>Area</th>\n",
       "      <th>Density</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>Suisse</th>\n",
       "      <td>0.000206</td>\n",
       "      <td>8.516</td>\n",
       "      <td>41285</td>\n",
       "      <td>0.000206</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>France</th>\n",
       "      <td>0.000122</td>\n",
       "      <td>67.060</td>\n",
       "      <td>551695</td>\n",
       "      <td>0.000122</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>USA</th>\n",
       "      <td>0.000086</td>\n",
       "      <td>328.200</td>\n",
       "      <td>3796742</td>\n",
       "      <td>0.000086</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Chine</th>\n",
       "      <td>0.000144</td>\n",
       "      <td>1386.000</td>\n",
       "      <td>9596961</td>\n",
       "      <td>0.000144</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "        DensityBIS  Population     Area   Density\n",
       "Suisse    0.000206       8.516    41285  0.000206\n",
       "France    0.000122      67.060   551695  0.000122\n",
       "USA       0.000086     328.200  3796742  0.000086\n",
       "Chine     0.000144    1386.000  9596961  0.000144"
      ]
     },
     "execution_count": 45,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# ajout d'une colonne à une position donnée\n",
    "df1.insert( 0, \"DensityBIS\", df1[\"Population\"] / df1[\"Area\"] )\n",
    "df1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Ajout d'une ligne"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:17.911238Z",
     "iopub.status.busy": "2026-10-30T13:14:17.911054Z",
     "iopub.status.idle": "2026-10-30T13:14:17.916436Z",
     "shell.execute_reply": "2026-10-30T13:14:17.915918Z"
    }
   },
   "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>Population</th>\n",
       "      <th>Area</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>Suisse</th>\n",
       "      <td>8.516</td>\n",
       "      <td>41285</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>France</th>\n",
       "      <td>67.060</td>\n",
       "      <td>551695</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>USA</th>\n",
       "      <td>328.200</td>\n",
       "      <td>3796742</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Chine</th>\n",
       "      <td>1386.000</td>\n",
       "      <td>9596961</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "        Population     Area\n",
       "Suisse       8.516    41285\n",
       "France      67.060   551695\n",
       "USA        328.200  3796742\n",
       "Chine     1386.000  9596961"
      ]
     },
     "execution_count": 46,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df1 = pd.DataFrame( { \"Population\" : population, \n",
    "                      \"Area\" : area } )\n",
    "df1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Pour ajouter une ligne à un DataFrame, on utilise la méthode `.loc[label] = serie/liste/dict` :"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:17.918113Z",
     "iopub.status.busy": "2026-10-30T13:14:17.917954Z",
     "iopub.status.idle": "2026-10-30T13:14:17.924509Z",
     "shell.execute_reply": "2026-10-30T13:14:17.924022Z"
    }
   },
   "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>Population</th>\n",
       "      <th>Area</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>Suisse</th>\n",
       "      <td>8.516</td>\n",
       "      <td>41285.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>France</th>\n",
       "      <td>67.060</td>\n",
       "      <td>551695.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>USA</th>\n",
       "      <td>328.200</td>\n",
       "      <td>3796742.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Chine</th>\n",
       "      <td>1386.000</td>\n",
       "      <td>9596961.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Italie</th>\n",
       "      <td>60.360</td>\n",
       "      <td>301338.0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "        Population       Area\n",
       "Suisse       8.516    41285.0\n",
       "France      67.060   551695.0\n",
       "USA        328.200  3796742.0\n",
       "Chine     1386.000  9596961.0\n",
       "Italie      60.360   301338.0"
      ]
     },
     "execution_count": 47,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df1.loc[\"Italie\"] = [60.36, 301338]\n",
    "df1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Réordonner les observations"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "La méthode `sort_values` permet de réordonner les observations d’un DataFrame, en laissant l’ordre des colonnes identiques. On peut trier selon une ou plusieurs colonnes, dans l’ordre croissant ou décroissant."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:17.926210Z",
     "iopub.status.busy": "2026-10-30T13:14:17.926060Z",
     "iopub.status.idle": "2026-10-30T13:14:17.931649Z",
     "shell.execute_reply": "2026-10-30T13:14:17.931216Z"
    }
   },
   "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>Population</th>\n",
       "      <th>Area</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>Suisse</th>\n",
       "      <td>8.516</td>\n",
       "      <td>41285.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Italie</th>\n",
       "      <td>60.360</td>\n",
       "      <td>301338.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>France</th>\n",
       "      <td>67.060</td>\n",
       "      <td>551695.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>USA</th>\n",
       "      <td>328.200</td>\n",
       "      <td>3796742.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Chine</th>\n",
       "      <td>1386.000</td>\n",
       "      <td>9596961.0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "        Population       Area\n",
       "Suisse       8.516    41285.0\n",
       "Italie      60.360   301338.0\n",
       "France      67.060   551695.0\n",
       "USA        328.200  3796742.0\n",
       "Chine     1386.000  9596961.0"
      ]
     },
     "execution_count": 48,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df1_sorted_pop = df1.sort_values(by=\"Population\")\n",
    "df1_sorted_pop"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:17.933234Z",
     "iopub.status.busy": "2026-10-30T13:14:17.933082Z",
     "iopub.status.idle": "2026-10-30T13:14:17.938503Z",
     "shell.execute_reply": "2026-10-30T13:14:17.938019Z"
    }
   },
   "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>Population</th>\n",
       "      <th>Area</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>Chine</th>\n",
       "      <td>1386.000</td>\n",
       "      <td>9596961.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>USA</th>\n",
       "      <td>328.200</td>\n",
       "      <td>3796742.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>France</th>\n",
       "      <td>67.060</td>\n",
       "      <td>551695.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Italie</th>\n",
       "      <td>60.360</td>\n",
       "      <td>301338.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Suisse</th>\n",
       "      <td>8.516</td>\n",
       "      <td>41285.0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "        Population       Area\n",
       "Chine     1386.000  9596961.0\n",
       "USA        328.200  3796742.0\n",
       "France      67.060   551695.0\n",
       "Italie      60.360   301338.0\n",
       "Suisse       8.516    41285.0"
      ]
     },
     "execution_count": 49,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df1_sorted_area = df1.sort_values(by=\"Area\", ascending=False)\n",
    "df1_sorted_area"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Suppression de lignes/colonnes"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Pour supprimer une ligne ou une colonne on utilise la méthode `.drop()` :\n",
    "\n",
    "- `df.drop( nom_ligne, axis=0)` ou `df.drop(labels = nom_ligne)` pour supprimer une ligne\n",
    "- `df.drop( nom_colonne, axis=1)` ou `df.drop( columns = nom_colonne)` pour supprimer une colonne \n",
    "\n",
    "On ajoute l'option `inplace=True` pour modifier le DataFrame, `inplace=False` pour renvoyer un nouveau DataFrame sans modifier l'original.\n",
    "\n",
    "Attention, si la la ligne ou la colonne n'existe pas, une erreur sera levée. Pour éviter cela, on peut utiliser l'option `errors='ignore'`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:17.940210Z",
     "iopub.status.busy": "2026-10-30T13:14:17.940061Z",
     "iopub.status.idle": "2026-10-30T13:14:17.948502Z",
     "shell.execute_reply": "2026-10-30T13:14:17.948018Z"
    }
   },
   "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>DensityBIS</th>\n",
       "      <th>Population</th>\n",
       "      <th>Area</th>\n",
       "      <th>Density</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>Suisse</th>\n",
       "      <td>0.000206</td>\n",
       "      <td>8.516</td>\n",
       "      <td>41285.0</td>\n",
       "      <td>0.000206</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>France</th>\n",
       "      <td>0.000122</td>\n",
       "      <td>67.060</td>\n",
       "      <td>551695.0</td>\n",
       "      <td>0.000122</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>USA</th>\n",
       "      <td>0.000086</td>\n",
       "      <td>328.200</td>\n",
       "      <td>3796742.0</td>\n",
       "      <td>0.000086</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Chine</th>\n",
       "      <td>0.000144</td>\n",
       "      <td>1386.000</td>\n",
       "      <td>9596961.0</td>\n",
       "      <td>0.000144</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Italie</th>\n",
       "      <td>0.000200</td>\n",
       "      <td>60.360</td>\n",
       "      <td>301338.0</td>\n",
       "      <td>0.000200</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "        DensityBIS  Population       Area   Density\n",
       "Suisse    0.000206       8.516    41285.0  0.000206\n",
       "France    0.000122      67.060   551695.0  0.000122\n",
       "USA       0.000086     328.200  3796742.0  0.000086\n",
       "Chine     0.000144    1386.000  9596961.0  0.000144\n",
       "Italie    0.000200      60.360   301338.0  0.000200"
      ]
     },
     "execution_count": 50,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Création du DataFrame\n",
    "\n",
    "df1 = pd.DataFrame( { \"Population\" : population, \n",
    "                      \"Area\" : area } )\n",
    "df1.loc[\"Italie\"] = [ 60.36, 301338]\n",
    "\n",
    "df1[\"Density\"] = df1[\"Population\"] / df1[\"Area\"]\n",
    "df1.insert( 0, \"DensityBIS\", df1[\"Population\"] / df1[\"Area\"] )\n",
    "\n",
    "df1\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:17.950154Z",
     "iopub.status.busy": "2026-10-30T13:14:17.949982Z",
     "iopub.status.idle": "2026-10-30T13:14:17.956078Z",
     "shell.execute_reply": "2026-10-30T13:14:17.955741Z"
    }
   },
   "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>Population</th>\n",
       "      <th>Area</th>\n",
       "      <th>Density</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>Suisse</th>\n",
       "      <td>8.516</td>\n",
       "      <td>41285.0</td>\n",
       "      <td>0.000206</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>France</th>\n",
       "      <td>67.060</td>\n",
       "      <td>551695.0</td>\n",
       "      <td>0.000122</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>USA</th>\n",
       "      <td>328.200</td>\n",
       "      <td>3796742.0</td>\n",
       "      <td>0.000086</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Chine</th>\n",
       "      <td>1386.000</td>\n",
       "      <td>9596961.0</td>\n",
       "      <td>0.000144</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Italie</th>\n",
       "      <td>60.360</td>\n",
       "      <td>301338.0</td>\n",
       "      <td>0.000200</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "        Population       Area   Density\n",
       "Suisse       8.516    41285.0  0.000206\n",
       "France      67.060   551695.0  0.000122\n",
       "USA        328.200  3796742.0  0.000086\n",
       "Chine     1386.000  9596961.0  0.000144\n",
       "Italie      60.360   301338.0  0.000200"
      ]
     },
     "execution_count": 51,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Supprimoons une colonne\n",
    "\n",
    "df1.drop( columns=\"DensityBIS\", inplace=True ) # suppression d'une colonne\n",
    "# del df1[\"Density\"]  # autre méthode pour supprimer une colonne\n",
    "\n",
    "df1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:17.957777Z",
     "iopub.status.busy": "2026-10-30T13:14:17.957622Z",
     "iopub.status.idle": "2026-10-30T13:14:17.963603Z",
     "shell.execute_reply": "2026-10-30T13:14:17.963119Z"
    }
   },
   "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>Population</th>\n",
       "      <th>Area</th>\n",
       "      <th>Density</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>Suisse</th>\n",
       "      <td>8.516</td>\n",
       "      <td>41285.0</td>\n",
       "      <td>0.000206</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>France</th>\n",
       "      <td>67.060</td>\n",
       "      <td>551695.0</td>\n",
       "      <td>0.000122</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>USA</th>\n",
       "      <td>328.200</td>\n",
       "      <td>3796742.0</td>\n",
       "      <td>0.000086</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Italie</th>\n",
       "      <td>60.360</td>\n",
       "      <td>301338.0</td>\n",
       "      <td>0.000200</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "        Population       Area   Density\n",
       "Suisse       8.516    41285.0  0.000206\n",
       "France      67.060   551695.0  0.000122\n",
       "USA        328.200  3796742.0  0.000086\n",
       "Italie      60.360   301338.0  0.000200"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Supprimons une ligne\n",
    "\n",
    "df1.drop(labels=[\"Chine\"], inplace=True) # suppression d'une ligne\n",
    "display(df1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Aperçu des données"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Créons d'abord un DataFrame un peu plus richement rempli (il pourrait tout aussi bien provenir d'un fichier CSV, comme vu plus haut) :"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:17.965386Z",
     "iopub.status.busy": "2026-10-30T13:14:17.965232Z",
     "iopub.status.idle": "2026-10-30T13:14:17.971261Z",
     "shell.execute_reply": "2026-10-30T13:14:17.970768Z"
    }
   },
   "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>foo</th>\n",
       "      <th>bar</th>\n",
       "      <th>baz</th>\n",
       "      <th>zoo</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>one</td>\n",
       "      <td>A</td>\n",
       "      <td>1</td>\n",
       "      <td>x</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>one</td>\n",
       "      <td>A</td>\n",
       "      <td>2</td>\n",
       "      <td>y</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>one</td>\n",
       "      <td>B</td>\n",
       "      <td>3</td>\n",
       "      <td>z</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>two</td>\n",
       "      <td>A</td>\n",
       "      <td>4</td>\n",
       "      <td>q</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>two</td>\n",
       "      <td>B</td>\n",
       "      <td>5</td>\n",
       "      <td>w</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>two</td>\n",
       "      <td>B</td>\n",
       "      <td>6</td>\n",
       "      <td>t</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   foo bar  baz zoo\n",
       "0  one   A    1   x\n",
       "1  one   A    2   y\n",
       "2  one   B    3   z\n",
       "3  two   A    4   q\n",
       "4  two   B    5   w\n",
       "5  two   B    6   t"
      ]
     },
     "execution_count": 53,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data = pd.DataFrame( {  \"foo\" : [\"one\", \"one\", \"one\", \"two\", \"two\", \"two\"],\n",
    "                        \"bar\" : [\"A\", \"A\", \"B\", \"A\", \"B\", \"B\"],\n",
    "                        \"baz\" : [1, 2, 3, 4, 5, 6], \n",
    "                        \"zoo\" : [\"x\", \"y\", \"z\", \"q\", \"w\", 't'] } )\n",
    "\n",
    "data"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Afficher les premières/dernières lignes"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Un bon réflexe à adopter après la création/l’importation d'un DataFrale, et après toute transformation importante, est de visualiser le jeu de données, ou du moins quelques lignes, afin de vérifier que tout s’est correctement déroulé. Pour cela, il existe deux méthodes principales :\n",
    "\n",
    "- la méthode  `.head()`  permettant de sélectionner par défaut les 5 premières lignes du data frame. Il est possible de préciser entre parenthèses le nombre de lignes à afficher ;\n",
    "- la méthode  `.tail()`  permettant de sélectionner par défaut les 5 dernières lignes du data frame. Il est également possible de préciser entre parenthèses le nombre de lignes à afficher.\n",
    "\n",
    "Il n’est pas possible (par défaut) d’afficher plus de 60 lignes d’un data frame, afin de ne pas surcharger inutilement le notebook. De façon plus globale, chercher à visualiser l’ensemble d’un data frame n’est généralement pas une bonne pratique. Si cela est tout à fait envisageable avec quelques dizaines de lignes, ça devient vite impossible avec plusieurs millions de lignes !\n",
    "Si vous cherchez à afficher plus de 60 lignes, vous aurez finalement comme résultat les 5 premières et dernières lignes du data frame."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 54,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:17.972907Z",
     "iopub.status.busy": "2026-10-30T13:14:17.972752Z",
     "iopub.status.idle": "2026-10-30T13:14:17.977963Z",
     "shell.execute_reply": "2026-10-30T13:14:17.977523Z"
    }
   },
   "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>foo</th>\n",
       "      <th>bar</th>\n",
       "      <th>baz</th>\n",
       "      <th>zoo</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>one</td>\n",
       "      <td>A</td>\n",
       "      <td>1</td>\n",
       "      <td>x</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>one</td>\n",
       "      <td>A</td>\n",
       "      <td>2</td>\n",
       "      <td>y</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>one</td>\n",
       "      <td>B</td>\n",
       "      <td>3</td>\n",
       "      <td>z</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>two</td>\n",
       "      <td>A</td>\n",
       "      <td>4</td>\n",
       "      <td>q</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>two</td>\n",
       "      <td>B</td>\n",
       "      <td>5</td>\n",
       "      <td>w</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   foo bar  baz zoo\n",
       "0  one   A    1   x\n",
       "1  one   A    2   y\n",
       "2  one   B    3   z\n",
       "3  two   A    4   q\n",
       "4  two   B    5   w"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# afficher les 5 premières lignes\n",
    "display(data.head())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 55,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:17.979587Z",
     "iopub.status.busy": "2026-10-30T13:14:17.979440Z",
     "iopub.status.idle": "2026-10-30T13:14:17.984576Z",
     "shell.execute_reply": "2026-10-30T13:14:17.984098Z"
    }
   },
   "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>foo</th>\n",
       "      <th>bar</th>\n",
       "      <th>baz</th>\n",
       "      <th>zoo</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>two</td>\n",
       "      <td>B</td>\n",
       "      <td>5</td>\n",
       "      <td>w</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>two</td>\n",
       "      <td>B</td>\n",
       "      <td>6</td>\n",
       "      <td>t</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   foo bar  baz zoo\n",
       "4  two   B    5   w\n",
       "5  two   B    6   t"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# afficher les 2 dernières lignes\n",
    "display(data.tail(2))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Attributs d'un DataFrame"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Combien de lignes comporte le DataFrame ? Et combien de colonnes ? Tout comme avec les arrays NumPy, il est possible de répondre à ces questions via l’attribut  `.shape`. Le résultat est un tuple : le premier élément correspond au nombre de lignes, et le second au nombre de colonnes. On peut naturellement stocker le résultat de cet attribut dans une variable pour réutiliser ces éléments ultérieurement."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 56,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:17.986221Z",
     "iopub.status.busy": "2026-10-30T13:14:17.986071Z",
     "iopub.status.idle": "2026-10-30T13:14:17.997632Z",
     "shell.execute_reply": "2026-10-30T13:14:17.997156Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/markdown": [
       "**`ndim` : nombre de dimensions (2 pour un DataFrame)**"
      ],
      "text/plain": [
       "<IPython.core.display.Markdown object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "2"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/markdown": [
       "**`shape` : (nombre de lignes, nombre de colonnes)**"
      ],
      "text/plain": [
       "<IPython.core.display.Markdown object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "(6, 4)"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/markdown": [
       "**`size` : nombre total d'éléments (lignes * colonnes)**"
      ],
      "text/plain": [
       "<IPython.core.display.Markdown object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "24"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/markdown": [
       "**`axes` : liste des axes (index des lignes, index des colonnes)**"
      ],
      "text/plain": [
       "<IPython.core.display.Markdown object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "[RangeIndex(start=0, stop=6, step=1),\n",
       " Index(['foo', 'bar', 'baz', 'zoo'], dtype='object')]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/markdown": [
       "**`index`, `columns`, `values` : accès aux composants du DataFrame**"
      ],
      "text/plain": [
       "<IPython.core.display.Markdown object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "RangeIndex(start=0, stop=6, step=1)"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "Index(['foo', 'bar', 'baz', 'zoo'], dtype='object')"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "array([['one', 'A', 1, 'x'],\n",
       "       ['one', 'A', 2, 'y'],\n",
       "       ['one', 'B', 3, 'z'],\n",
       "       ['two', 'A', 4, 'q'],\n",
       "       ['two', 'B', 5, 'w'],\n",
       "       ['two', 'B', 6, 't']], dtype=object)"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "display(Markdown(\"**`ndim` : nombre de dimensions (2 pour un DataFrame)**\"))\n",
    "display( data.ndim)\n",
    "\n",
    "display(Markdown(\"**`shape` : (nombre de lignes, nombre de colonnes)**\"))\n",
    "display( data.shape )\n",
    "\n",
    "display(Markdown(\"**`size` : nombre total d'éléments (lignes * colonnes)**\"))\n",
    "display( data.size )\n",
    "\n",
    "display(Markdown(\"**`axes` : liste des axes (index des lignes, index des colonnes)**\"))\n",
    "display( data.axes)\n",
    "\n",
    "display(Markdown(\"**`index`, `columns`, `values` : accès aux composants du DataFrame**\"))\n",
    "display( data.index ) \n",
    "display( data.columns ) \n",
    "display( data.values ) # tableau numpy 2D des valeurs\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "On peut avoir envie de connaître les types de chacune de nos variables (un type par colonne). On peut accéder à cela très simplement à partir de l’attribut  `.dtypes`. On obtient un objet Series :les index de cette série sont les étiquettes des colonnes, et les valeurs de cette série sont les types de données. Vous noterez que le type de `foo` est `objet`, alors que nous avons pourtant des chaînes de caractères. C’est une chose à connaître, mais le type objet de Pandas correspond en fait à une colonne de type chaîne de caractères. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 57,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:17.999276Z",
     "iopub.status.busy": "2026-10-30T13:14:17.999127Z",
     "iopub.status.idle": "2026-10-30T13:14:18.002863Z",
     "shell.execute_reply": "2026-10-30T13:14:18.002395Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "foo    object\n",
       "bar    object\n",
       "baz     int64\n",
       "zoo    object\n",
       "dtype: object"
      ]
     },
     "execution_count": 57,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data.dtypes"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Les jeux de données réels sont rarement complets. Les valeurs manquantes sont affichées `NaN` et repérées par la méthode `.isnull()`, qui renvoie un tableau de booléens de même forme que le DataFrame.\n",
    "\n",
    "Ici, le DataFrame `data` est complet : toutes les cases valent `False`. Nous verrons un peu plus bas, dans la section [Les valeurs manquantes](#les-valeurs-manquantes), comment les compter, les supprimer ou les remplacer."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 58,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:18.004453Z",
     "iopub.status.busy": "2026-10-30T13:14:18.004308Z",
     "iopub.status.idle": "2026-10-30T13:14:18.009803Z",
     "shell.execute_reply": "2026-10-30T13:14:18.009359Z"
    }
   },
   "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>foo</th>\n",
       "      <th>bar</th>\n",
       "      <th>baz</th>\n",
       "      <th>zoo</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>False</td>\n",
       "      <td>False</td>\n",
       "      <td>False</td>\n",
       "      <td>False</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>False</td>\n",
       "      <td>False</td>\n",
       "      <td>False</td>\n",
       "      <td>False</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>False</td>\n",
       "      <td>False</td>\n",
       "      <td>False</td>\n",
       "      <td>False</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>False</td>\n",
       "      <td>False</td>\n",
       "      <td>False</td>\n",
       "      <td>False</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>False</td>\n",
       "      <td>False</td>\n",
       "      <td>False</td>\n",
       "      <td>False</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>False</td>\n",
       "      <td>False</td>\n",
       "      <td>False</td>\n",
       "      <td>False</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "     foo    bar    baz    zoo\n",
       "0  False  False  False  False\n",
       "1  False  False  False  False\n",
       "2  False  False  False  False\n",
       "3  False  False  False  False\n",
       "4  False  False  False  False\n",
       "5  False  False  False  False"
      ]
     },
     "execution_count": 58,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data.isnull()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ces informations peuvent être combinées pour obtenir un résumé complet des données avec la méthode `.info()`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 59,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:18.011518Z",
     "iopub.status.busy": "2026-10-30T13:14:18.011363Z",
     "iopub.status.idle": "2026-10-30T13:14:18.017245Z",
     "shell.execute_reply": "2026-10-30T13:14:18.016757Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<class 'pandas.core.frame.DataFrame'>\n",
      "RangeIndex: 6 entries, 0 to 5\n",
      "Data columns (total 4 columns):\n",
      " #   Column  Non-Null Count  Dtype \n",
      "---  ------  --------------  ----- \n",
      " 0   foo     6 non-null      object\n",
      " 1   bar     6 non-null      object\n",
      " 2   baz     6 non-null      int64 \n",
      " 3   zoo     6 non-null      object\n",
      "dtypes: int64(1), object(3)\n",
      "memory usage: 324.0+ bytes\n"
     ]
    }
   ],
   "source": [
    "data.info()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 60,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:18.018914Z",
     "iopub.status.busy": "2026-10-30T13:14:18.018760Z",
     "iopub.status.idle": "2026-10-30T13:14:18.022071Z",
     "shell.execute_reply": "2026-10-30T13:14:18.021665Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3.5"
      ]
     },
     "execution_count": 60,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data['baz'].mean()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Les valeurs manquantes"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Les jeux de données réels sont rarement complets. Une valeur manquante peut recouvrir des réalités très différentes : information non collectée, question sans objet pour cet individu, erreur de saisie, perte lors d'un transfert. Pandas les représente toutes de la même façon, par `NaN` (*Not a Number*).\n",
    "\n",
    "Le DataFrame `data` utilisé jusqu'ici n'en contient aucune. Construisons-en un qui en contient :"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 61,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:18.023664Z",
     "iopub.status.busy": "2026-10-30T13:14:18.023516Z",
     "iopub.status.idle": "2026-10-30T13:14:18.030401Z",
     "shell.execute_reply": "2026-10-30T13:14:18.030016Z"
    }
   },
   "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>station</th>\n",
       "      <th>temperature</th>\n",
       "      <th>humidite</th>\n",
       "      <th>vent</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>Toulon</td>\n",
       "      <td>18.2</td>\n",
       "      <td>65.0</td>\n",
       "      <td>12.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>Nice</td>\n",
       "      <td>NaN</td>\n",
       "      <td>70.0</td>\n",
       "      <td>8.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>Marseille</td>\n",
       "      <td>21.0</td>\n",
       "      <td>NaN</td>\n",
       "      <td>15.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>Ajaccio</td>\n",
       "      <td>19.5</td>\n",
       "      <td>58.0</td>\n",
       "      <td>NaN</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>Bastia</td>\n",
       "      <td>NaN</td>\n",
       "      <td>62.0</td>\n",
       "      <td>20.0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "     station  temperature  humidite  vent\n",
       "0     Toulon         18.2      65.0  12.0\n",
       "1       Nice          NaN      70.0   8.0\n",
       "2  Marseille         21.0       NaN  15.0\n",
       "3    Ajaccio         19.5      58.0   NaN\n",
       "4     Bastia          NaN      62.0  20.0"
      ]
     },
     "execution_count": 61,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "releve = pd.DataFrame({\n",
    "    \"station\"     : [\"Toulon\", \"Nice\", \"Marseille\", \"Ajaccio\", \"Bastia\"],\n",
    "    \"temperature\" : [18.2, np.nan, 21.0, 19.5, np.nan],\n",
    "    \"humidite\"    : [65, 70, np.nan, 58, 62],\n",
    "    \"vent\"        : [12, 8, 15, np.nan, 20] })\n",
    "\n",
    "releve"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Repérer les valeurs manquantes"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`.isnull()` (ou son alias `.isna()`) renvoie un DataFrame de booléens de même forme. Combiné à `.sum()` il donne le **nombre** de valeurs manquantes par colonne ; combiné à `.mean()`, leur **proportion**."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 62,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:18.032077Z",
     "iopub.status.busy": "2026-10-30T13:14:18.031893Z",
     "iopub.status.idle": "2026-10-30T13:14:18.045944Z",
     "shell.execute_reply": "2026-10-30T13:14:18.045586Z"
    }
   },
   "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>station</th>\n",
       "      <th>temperature</th>\n",
       "      <th>humidite</th>\n",
       "      <th>vent</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>False</td>\n",
       "      <td>False</td>\n",
       "      <td>False</td>\n",
       "      <td>False</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>False</td>\n",
       "      <td>True</td>\n",
       "      <td>False</td>\n",
       "      <td>False</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>False</td>\n",
       "      <td>False</td>\n",
       "      <td>True</td>\n",
       "      <td>False</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>False</td>\n",
       "      <td>False</td>\n",
       "      <td>False</td>\n",
       "      <td>True</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>False</td>\n",
       "      <td>True</td>\n",
       "      <td>False</td>\n",
       "      <td>False</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   station  temperature  humidite   vent\n",
       "0    False        False     False  False\n",
       "1    False         True     False  False\n",
       "2    False        False      True  False\n",
       "3    False        False     False   True\n",
       "4    False         True     False  False"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "station        0\n",
       "temperature    2\n",
       "humidite       1\n",
       "vent           1\n",
       "dtype: int64"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "station         0.0\n",
       "temperature    40.0\n",
       "humidite       20.0\n",
       "vent           20.0\n",
       "dtype: float64"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "display(releve.isnull())\n",
    "display(releve.isnull().sum())\n",
    "display((releve.isnull().mean() * 100).round(1))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`.info()` donne la même information sous une autre forme : le nombre de valeurs **non nulles** de chaque colonne."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 63,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:18.047638Z",
     "iopub.status.busy": "2026-10-30T13:14:18.047475Z",
     "iopub.status.idle": "2026-10-30T13:14:18.053458Z",
     "shell.execute_reply": "2026-10-30T13:14:18.052942Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<class 'pandas.core.frame.DataFrame'>\n",
      "RangeIndex: 5 entries, 0 to 4\n",
      "Data columns (total 4 columns):\n",
      " #   Column       Non-Null Count  Dtype  \n",
      "---  ------       --------------  -----  \n",
      " 0   station      5 non-null      object \n",
      " 1   temperature  3 non-null      float64\n",
      " 2   humidite     4 non-null      float64\n",
      " 3   vent         4 non-null      float64\n",
      "dtypes: float64(3), object(1)\n",
      "memory usage: 292.0+ bytes\n"
     ]
    }
   ],
   "source": [
    "releve.info()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Et pour examiner les lignes incomplètes :"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 64,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:18.055303Z",
     "iopub.status.busy": "2026-10-30T13:14:18.055141Z",
     "iopub.status.idle": "2026-10-30T13:14:18.062006Z",
     "shell.execute_reply": "2026-10-30T13:14:18.061647Z"
    }
   },
   "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>station</th>\n",
       "      <th>temperature</th>\n",
       "      <th>humidite</th>\n",
       "      <th>vent</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>Nice</td>\n",
       "      <td>NaN</td>\n",
       "      <td>70.0</td>\n",
       "      <td>8.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>Marseille</td>\n",
       "      <td>21.0</td>\n",
       "      <td>NaN</td>\n",
       "      <td>15.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>Ajaccio</td>\n",
       "      <td>19.5</td>\n",
       "      <td>58.0</td>\n",
       "      <td>NaN</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>Bastia</td>\n",
       "      <td>NaN</td>\n",
       "      <td>62.0</td>\n",
       "      <td>20.0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "     station  temperature  humidite  vent\n",
       "1       Nice          NaN      70.0   8.0\n",
       "2  Marseille         21.0       NaN  15.0\n",
       "3    Ajaccio         19.5      58.0   NaN\n",
       "4     Bastia          NaN      62.0  20.0"
      ]
     },
     "execution_count": 64,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "masque = releve.isnull().any(axis=1)   # au moins une valeur manquante sur la ligne\n",
    "releve[masque]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div class=\"alert alert-block alert-danger\">\n",
    "\n",
    "**`NaN` n'est égal à rien, pas même à lui-même.** Un test `== np.nan` renvoie toujours `False`, et ne détecte donc jamais rien. Utilisez toujours `.isnull()` / `.isna()`.\n",
    "\n",
    "</div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 65,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:18.063690Z",
     "iopub.status.busy": "2026-10-30T13:14:18.063530Z",
     "iopub.status.idle": "2026-10-30T13:14:18.069746Z",
     "shell.execute_reply": "2026-10-30T13:14:18.069235Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/markdown": [
       "np.nan == np.nan : **False**"
      ],
      "text/plain": [
       "<IPython.core.display.Markdown object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "0    False\n",
       "1    False\n",
       "2    False\n",
       "3    False\n",
       "4    False\n",
       "Name: temperature, dtype: bool"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "0    False\n",
       "1     True\n",
       "2    False\n",
       "3    False\n",
       "4     True\n",
       "Name: temperature, dtype: bool"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "display(Markdown(f\"np.nan == np.nan : **{np.nan == np.nan}**\"))\n",
    "\n",
    "display(releve[\"temperature\"] == np.nan)   # que des False : inutilisable\n",
    "display(releve[\"temperature\"].isnull())    # la bonne façon de faire"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Comment les calculs les traitent"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Les fonctions d'agrégation **ignorent** les valeurs manquantes. Ce n'est pas anodin : la moyenne est calculée sur les seules valeurs disponibles, et le dénominateur n'est donc pas celui que vous croyez."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 66,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:18.071516Z",
     "iopub.status.busy": "2026-10-30T13:14:18.071351Z",
     "iopub.status.idle": "2026-10-30T13:14:18.076842Z",
     "shell.execute_reply": "2026-10-30T13:14:18.076395Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "19.566666666666666"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "58.7"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "3"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "display(releve[\"temperature\"].mean())    # moyenne sur 3 valeurs, pas sur 5\n",
    "display(releve[\"temperature\"].sum())     # somme des 3 valeurs présentes\n",
    "display(releve[\"temperature\"].count())   # nombre de valeurs NON manquantes"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "En revanche, une opération arithmétique **propage** le `NaN` : dès qu'une des deux valeurs manque, le résultat manque."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 67,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:18.078611Z",
     "iopub.status.busy": "2026-10-30T13:14:18.078449Z",
     "iopub.status.idle": "2026-10-30T13:14:18.082646Z",
     "shell.execute_reply": "2026-10-30T13:14:18.082105Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0    30.2\n",
       "1     NaN\n",
       "2    36.0\n",
       "3     NaN\n",
       "4     NaN\n",
       "dtype: float64"
      ]
     },
     "execution_count": 67,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "releve[\"temperature\"] + releve[\"vent\"]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Supprimer : `dropna`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Par défaut, `.dropna()` supprime toute **ligne** comportant au moins une valeur manquante. Les arguments utiles :\n",
    "\n",
    "| Argument | Effet |\n",
    "|---|---|\n",
    "| `axis=1` | supprimer les **colonnes** plutôt que les lignes |\n",
    "| `how=\"all\"` | ne supprimer que si **toutes** les valeurs manquent |\n",
    "| `subset=[...]` | n'examiner que certaines colonnes |\n",
    "| `thresh=n` | conserver les lignes ayant au moins `n` valeurs renseignées |"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 68,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:18.084598Z",
     "iopub.status.busy": "2026-10-30T13:14:18.084396Z",
     "iopub.status.idle": "2026-10-30T13:14:18.098358Z",
     "shell.execute_reply": "2026-10-30T13:14:18.097868Z"
    }
   },
   "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>station</th>\n",
       "      <th>temperature</th>\n",
       "      <th>humidite</th>\n",
       "      <th>vent</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>Toulon</td>\n",
       "      <td>18.2</td>\n",
       "      <td>65.0</td>\n",
       "      <td>12.0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "  station  temperature  humidite  vent\n",
       "0  Toulon         18.2      65.0  12.0"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "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>station</th>\n",
       "      <th>temperature</th>\n",
       "      <th>humidite</th>\n",
       "      <th>vent</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>Toulon</td>\n",
       "      <td>18.2</td>\n",
       "      <td>65.0</td>\n",
       "      <td>12.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>Marseille</td>\n",
       "      <td>21.0</td>\n",
       "      <td>NaN</td>\n",
       "      <td>15.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>Ajaccio</td>\n",
       "      <td>19.5</td>\n",
       "      <td>58.0</td>\n",
       "      <td>NaN</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "     station  temperature  humidite  vent\n",
       "0     Toulon         18.2      65.0  12.0\n",
       "2  Marseille         21.0       NaN  15.0\n",
       "3    Ajaccio         19.5      58.0   NaN"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "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>station</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>Toulon</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>Nice</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>Marseille</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>Ajaccio</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>Bastia</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "     station\n",
       "0     Toulon\n",
       "1       Nice\n",
       "2  Marseille\n",
       "3    Ajaccio\n",
       "4     Bastia"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "display(releve.dropna())                         # ne reste que les lignes complètes\n",
    "display(releve.dropna(subset=[\"temperature\"]))   # on n'exige que la température\n",
    "display(releve.dropna(axis=1))                   # on supprime les colonnes incomplètes"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Remplacer : `fillna`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 69,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:18.100252Z",
     "iopub.status.busy": "2026-10-30T13:14:18.100032Z",
     "iopub.status.idle": "2026-10-30T13:14:18.111390Z",
     "shell.execute_reply": "2026-10-30T13:14:18.110920Z"
    }
   },
   "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>station</th>\n",
       "      <th>temperature</th>\n",
       "      <th>humidite</th>\n",
       "      <th>vent</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>Toulon</td>\n",
       "      <td>18.2</td>\n",
       "      <td>65.0</td>\n",
       "      <td>12.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>Nice</td>\n",
       "      <td>0.0</td>\n",
       "      <td>70.0</td>\n",
       "      <td>8.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>Marseille</td>\n",
       "      <td>21.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>15.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>Ajaccio</td>\n",
       "      <td>19.5</td>\n",
       "      <td>58.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>Bastia</td>\n",
       "      <td>0.0</td>\n",
       "      <td>62.0</td>\n",
       "      <td>20.0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "     station  temperature  humidite  vent\n",
       "0     Toulon         18.2      65.0  12.0\n",
       "1       Nice          0.0      70.0   8.0\n",
       "2  Marseille         21.0       0.0  15.0\n",
       "3    Ajaccio         19.5      58.0   0.0\n",
       "4     Bastia          0.0      62.0  20.0"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "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>station</th>\n",
       "      <th>temperature</th>\n",
       "      <th>humidite</th>\n",
       "      <th>vent</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>Toulon</td>\n",
       "      <td>18.200000</td>\n",
       "      <td>65.00</td>\n",
       "      <td>12.00</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>Nice</td>\n",
       "      <td>19.566667</td>\n",
       "      <td>70.00</td>\n",
       "      <td>8.00</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>Marseille</td>\n",
       "      <td>21.000000</td>\n",
       "      <td>63.75</td>\n",
       "      <td>15.00</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>Ajaccio</td>\n",
       "      <td>19.500000</td>\n",
       "      <td>58.00</td>\n",
       "      <td>13.75</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>Bastia</td>\n",
       "      <td>19.566667</td>\n",
       "      <td>62.00</td>\n",
       "      <td>20.00</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "     station  temperature  humidite   vent\n",
       "0     Toulon    18.200000     65.00  12.00\n",
       "1       Nice    19.566667     70.00   8.00\n",
       "2  Marseille    21.000000     63.75  15.00\n",
       "3    Ajaccio    19.500000     58.00  13.75\n",
       "4     Bastia    19.566667     62.00  20.00"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "display(releve.fillna(0))                                   # par une constante\n",
    "display(releve.fillna(releve.mean(numeric_only=True)))      # par la moyenne de chaque colonne"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Pour des données ordonnées (une série temporelle, par exemple), on peut propager la dernière valeur connue avec `.ffill()`, ou la suivante avec `.bfill()` :"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 70,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:18.113224Z",
     "iopub.status.busy": "2026-10-30T13:14:18.112991Z",
     "iopub.status.idle": "2026-10-30T13:14:18.124121Z",
     "shell.execute_reply": "2026-10-30T13:14:18.123700Z"
    }
   },
   "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>station</th>\n",
       "      <th>temperature</th>\n",
       "      <th>humidite</th>\n",
       "      <th>vent</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>Toulon</td>\n",
       "      <td>18.2</td>\n",
       "      <td>65.0</td>\n",
       "      <td>12.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>Nice</td>\n",
       "      <td>18.2</td>\n",
       "      <td>70.0</td>\n",
       "      <td>8.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>Marseille</td>\n",
       "      <td>21.0</td>\n",
       "      <td>70.0</td>\n",
       "      <td>15.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>Ajaccio</td>\n",
       "      <td>19.5</td>\n",
       "      <td>58.0</td>\n",
       "      <td>15.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>Bastia</td>\n",
       "      <td>19.5</td>\n",
       "      <td>62.0</td>\n",
       "      <td>20.0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "     station  temperature  humidite  vent\n",
       "0     Toulon         18.2      65.0  12.0\n",
       "1       Nice         18.2      70.0   8.0\n",
       "2  Marseille         21.0      70.0  15.0\n",
       "3    Ajaccio         19.5      58.0  15.0\n",
       "4     Bastia         19.5      62.0  20.0"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "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>station</th>\n",
       "      <th>temperature</th>\n",
       "      <th>humidite</th>\n",
       "      <th>vent</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>Toulon</td>\n",
       "      <td>18.2</td>\n",
       "      <td>65.0</td>\n",
       "      <td>12.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>Nice</td>\n",
       "      <td>21.0</td>\n",
       "      <td>70.0</td>\n",
       "      <td>8.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>Marseille</td>\n",
       "      <td>21.0</td>\n",
       "      <td>58.0</td>\n",
       "      <td>15.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>Ajaccio</td>\n",
       "      <td>19.5</td>\n",
       "      <td>58.0</td>\n",
       "      <td>20.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>Bastia</td>\n",
       "      <td>NaN</td>\n",
       "      <td>62.0</td>\n",
       "      <td>20.0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "     station  temperature  humidite  vent\n",
       "0     Toulon         18.2      65.0  12.0\n",
       "1       Nice         21.0      70.0   8.0\n",
       "2  Marseille         21.0      58.0  15.0\n",
       "3    Ajaccio         19.5      58.0  20.0\n",
       "4     Bastia          NaN      62.0  20.0"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "display(releve.ffill())   # forward fill  : on reprend la valeur du dessus\n",
    "display(releve.bfill())   # backward fill : on reprend la valeur du dessous"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div class=\"alert alert-block alert-danger\">\n",
    "\n",
    "**Traiter les valeurs manquantes n'est pas un choix technique, c'est un choix méthodologique.**\n",
    "\n",
    "- **Supprimer** les lignes incomplètes biaise l'échantillon dès que les données ne manquent pas « au hasard » : si les personnes à hauts revenus refusent plus souvent de répondre, les supprimer fait baisser artificiellement le revenu moyen.\n",
    "- **Remplacer par la moyenne** préserve cette moyenne, mais écrase la variance et invente des observations qui n'ont jamais été faites.\n",
    "\n",
    "Il n'existe pas de bonne réponse générique. La seule règle vraiment solide : **regarder combien de lignes sont concernées avant de décider**, puis **dire explicitement ce que vous avez fait et pourquoi**.\n",
    "\n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Statistiques descriptives"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "On peut calculer la somme, la plus petite/grande valeur, la moyenne, la médiane, l'écart-type des élements de chaque colonne avec les méthodes `.sum()`, `.min()`, `.max()`, `.mean()`, `.median()` et `.std()`.   \n",
    "Rappel : la somme entre chaînes de caractères est une concaténation."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 71,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:18.125912Z",
     "iopub.status.busy": "2026-10-30T13:14:18.125744Z",
     "iopub.status.idle": "2026-10-30T13:14:18.147357Z",
     "shell.execute_reply": "2026-10-30T13:14:18.146869Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/markdown": [
       "**Opérations sur les DataFrames**"
      ],
      "text/plain": [
       "<IPython.core.display.Markdown object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/markdown": [
       "**Somme**"
      ],
      "text/plain": [
       "<IPython.core.display.Markdown object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "foo    oneoneonetwotwotwo\n",
       "bar                AABABB\n",
       "baz                    21\n",
       "zoo                xyzqwt\n",
       "dtype: object"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/markdown": [
       "**Minimum**"
      ],
      "text/plain": [
       "<IPython.core.display.Markdown object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "foo    one\n",
       "bar      A\n",
       "baz      1\n",
       "zoo      q\n",
       "dtype: object"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/markdown": [
       "**Maximum**"
      ],
      "text/plain": [
       "<IPython.core.display.Markdown object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "foo    two\n",
       "bar      B\n",
       "baz      6\n",
       "zoo      z\n",
       "dtype: object"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/markdown": [
       "**Moyenne**"
      ],
      "text/plain": [
       "<IPython.core.display.Markdown object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "baz    3.5\n",
       "dtype: float64"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/markdown": [
       "**Médiane**"
      ],
      "text/plain": [
       "<IPython.core.display.Markdown object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "baz    3.5\n",
       "dtype: float64"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/markdown": [
       "**Écart-type**"
      ],
      "text/plain": [
       "<IPython.core.display.Markdown object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "baz    1.870829\n",
       "dtype: float64"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/markdown": [
       "**Comptage : nombre de valeurs non manquants par colonne**"
      ],
      "text/plain": [
       "<IPython.core.display.Markdown object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "foo    6\n",
       "bar    6\n",
       "baz    6\n",
       "zoo    6\n",
       "dtype: int64"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Elles renvoient des Series dont les Index sont les colonnes du DataFrame\n",
    "\n",
    "display(Markdown(\"**Opérations sur les DataFrames**\"))\n",
    "\n",
    "display(Markdown(\"**Somme**\"))\n",
    "display(data.sum()) # idem que data.sum(axis=0) ou data.sum(axis='rows') \n",
    "display(Markdown(\"**Minimum**\"))\n",
    "display(data.min())\n",
    "display(Markdown(\"**Maximum**\"))\n",
    "display(data.max())\n",
    "display(Markdown(\"**Moyenne**\"))\n",
    "# display(data.mean())\n",
    "display(data.mean(numeric_only=True))\n",
    "display(Markdown(\"**Médiane**\"))\n",
    "# display(data.median())\n",
    "display(data.median(numeric_only=True))\n",
    "display(Markdown(\"**Écart-type**\"))\n",
    "# display(data.std())\n",
    "display(data.std(numeric_only=True))\n",
    "display(Markdown(\"**Comptage : nombre de valeurs non manquants par colonne**\"))\n",
    "display(data.count())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Pour obtenir un résumé statistique des variables numériques, on peut utiliser la méthode `.describe()` :"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 72,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:18.149062Z",
     "iopub.status.busy": "2026-10-30T13:14:18.148868Z",
     "iopub.status.idle": "2026-10-30T13:14:18.155921Z",
     "shell.execute_reply": "2026-10-30T13:14:18.155424Z"
    }
   },
   "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>baz</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>count</th>\n",
       "      <td>6.000000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>mean</th>\n",
       "      <td>3.500000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>std</th>\n",
       "      <td>1.870829</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>min</th>\n",
       "      <td>1.000000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>25%</th>\n",
       "      <td>2.250000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>50%</th>\n",
       "      <td>3.500000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>75%</th>\n",
       "      <td>4.750000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>max</th>\n",
       "      <td>6.000000</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "            baz\n",
       "count  6.000000\n",
       "mean   3.500000\n",
       "std    1.870829\n",
       "min    1.000000\n",
       "25%    2.250000\n",
       "50%    3.500000\n",
       "75%    4.750000\n",
       "max    6.000000"
      ]
     },
     "execution_count": 72,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data.describe()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Cette méthode renvoie un objet de type DataFrame, où les statistiques descriptives sont calculées pour chaque colonne numérique. Pour les variables catégorielles, on peut obtenir le nombre de valeurs uniques, la valeur la plus fréquente et sa fréquence :"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 73,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:18.157652Z",
     "iopub.status.busy": "2026-10-30T13:14:18.157500Z",
     "iopub.status.idle": "2026-10-30T13:14:18.165851Z",
     "shell.execute_reply": "2026-10-30T13:14:18.165290Z"
    }
   },
   "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>foo</th>\n",
       "      <th>bar</th>\n",
       "      <th>zoo</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>count</th>\n",
       "      <td>6</td>\n",
       "      <td>6</td>\n",
       "      <td>6</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>unique</th>\n",
       "      <td>2</td>\n",
       "      <td>2</td>\n",
       "      <td>6</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>top</th>\n",
       "      <td>one</td>\n",
       "      <td>A</td>\n",
       "      <td>x</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>freq</th>\n",
       "      <td>3</td>\n",
       "      <td>3</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "        foo bar zoo\n",
       "count     6   6   6\n",
       "unique    2   2   6\n",
       "top     one   A   x\n",
       "freq      3   3   1"
      ]
     },
     "execution_count": 73,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data[['foo', 'bar', 'zoo']].describe()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 74,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:18.167538Z",
     "iopub.status.busy": "2026-10-30T13:14:18.167384Z",
     "iopub.status.idle": "2026-10-30T13:14:18.178050Z",
     "shell.execute_reply": "2026-10-30T13:14:18.177612Z"
    }
   },
   "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>foo</th>\n",
       "      <th>bar</th>\n",
       "      <th>baz</th>\n",
       "      <th>zoo</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>count</th>\n",
       "      <td>6</td>\n",
       "      <td>6</td>\n",
       "      <td>6.000000</td>\n",
       "      <td>6</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>unique</th>\n",
       "      <td>2</td>\n",
       "      <td>2</td>\n",
       "      <td>NaN</td>\n",
       "      <td>6</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>top</th>\n",
       "      <td>one</td>\n",
       "      <td>A</td>\n",
       "      <td>NaN</td>\n",
       "      <td>x</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>freq</th>\n",
       "      <td>3</td>\n",
       "      <td>3</td>\n",
       "      <td>NaN</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>mean</th>\n",
       "      <td>NaN</td>\n",
       "      <td>NaN</td>\n",
       "      <td>3.500000</td>\n",
       "      <td>NaN</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>std</th>\n",
       "      <td>NaN</td>\n",
       "      <td>NaN</td>\n",
       "      <td>1.870829</td>\n",
       "      <td>NaN</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>min</th>\n",
       "      <td>NaN</td>\n",
       "      <td>NaN</td>\n",
       "      <td>1.000000</td>\n",
       "      <td>NaN</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>25%</th>\n",
       "      <td>NaN</td>\n",
       "      <td>NaN</td>\n",
       "      <td>2.250000</td>\n",
       "      <td>NaN</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>50%</th>\n",
       "      <td>NaN</td>\n",
       "      <td>NaN</td>\n",
       "      <td>3.500000</td>\n",
       "      <td>NaN</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>75%</th>\n",
       "      <td>NaN</td>\n",
       "      <td>NaN</td>\n",
       "      <td>4.750000</td>\n",
       "      <td>NaN</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>max</th>\n",
       "      <td>NaN</td>\n",
       "      <td>NaN</td>\n",
       "      <td>6.000000</td>\n",
       "      <td>NaN</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "        foo  bar       baz  zoo\n",
       "count     6    6  6.000000    6\n",
       "unique    2    2       NaN    6\n",
       "top     one    A       NaN    x\n",
       "freq      3    3       NaN    1\n",
       "mean    NaN  NaN  3.500000  NaN\n",
       "std     NaN  NaN  1.870829  NaN\n",
       "min     NaN  NaN  1.000000  NaN\n",
       "25%     NaN  NaN  2.250000  NaN\n",
       "50%     NaN  NaN  3.500000  NaN\n",
       "75%     NaN  NaN  4.750000  NaN\n",
       "max     NaN  NaN  6.000000  NaN"
      ]
     },
     "execution_count": 74,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data.describe(include=\"all\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "On peut modifier le type de données d'une colonne avec la méthode `.astype()` et demander à Pandas d'y associer des codes numériques.\n",
    "Si on ne veut pas ecraser le DataFrame initial, on peut utiliser la méthode `.copy()`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 75,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:18.179857Z",
     "iopub.status.busy": "2026-10-30T13:14:18.179704Z",
     "iopub.status.idle": "2026-10-30T13:14:18.190489Z",
     "shell.execute_reply": "2026-10-30T13:14:18.189895Z"
    }
   },
   "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>foo</th>\n",
       "      <th>bar</th>\n",
       "      <th>baz</th>\n",
       "      <th>zoo</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>one</td>\n",
       "      <td>A</td>\n",
       "      <td>1</td>\n",
       "      <td>x</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>one</td>\n",
       "      <td>A</td>\n",
       "      <td>2</td>\n",
       "      <td>y</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>one</td>\n",
       "      <td>B</td>\n",
       "      <td>3</td>\n",
       "      <td>z</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>two</td>\n",
       "      <td>A</td>\n",
       "      <td>4</td>\n",
       "      <td>q</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>two</td>\n",
       "      <td>B</td>\n",
       "      <td>5</td>\n",
       "      <td>w</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>two</td>\n",
       "      <td>B</td>\n",
       "      <td>6</td>\n",
       "      <td>t</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   foo bar  baz zoo\n",
       "0  one   A    1   x\n",
       "1  one   A    2   y\n",
       "2  one   B    3   z\n",
       "3  two   A    4   q\n",
       "4  two   B    5   w\n",
       "5  two   B    6   t"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "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>foo</th>\n",
       "      <th>bar</th>\n",
       "      <th>baz</th>\n",
       "      <th>zoo</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>3</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>2</td>\n",
       "      <td>4</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>3</td>\n",
       "      <td>5</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>1</td>\n",
       "      <td>0</td>\n",
       "      <td>4</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>5</td>\n",
       "      <td>2</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>6</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   foo  bar  baz  zoo\n",
       "0    0    0    1    3\n",
       "1    0    0    2    4\n",
       "2    0    1    3    5\n",
       "3    1    0    4    0\n",
       "4    1    1    5    2\n",
       "5    1    1    6    1"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "data_bis = data.copy()\n",
    "for col in data_bis.select_dtypes(include='object').columns:\n",
    "    data_bis[col] = data_bis[col].astype('category').cat.codes\n",
    "\n",
    "display(data)\n",
    "display(data_bis)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 76,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:18.192280Z",
     "iopub.status.busy": "2026-10-30T13:14:18.192126Z",
     "iopub.status.idle": "2026-10-30T13:14:18.197760Z",
     "shell.execute_reply": "2026-10-30T13:14:18.197431Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<class 'pandas.core.frame.DataFrame'>\n",
      "RangeIndex: 6 entries, 0 to 5\n",
      "Data columns (total 4 columns):\n",
      " #   Column  Non-Null Count  Dtype\n",
      "---  ------  --------------  -----\n",
      " 0   foo     6 non-null      int8 \n",
      " 1   bar     6 non-null      int8 \n",
      " 2   baz     6 non-null      int64\n",
      " 3   zoo     6 non-null      int8 \n",
      "dtypes: int64(1), int8(3)\n",
      "memory usage: 198.0 bytes\n"
     ]
    }
   ],
   "source": [
    "data_bis.info()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 77,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:18.199348Z",
     "iopub.status.busy": "2026-10-30T13:14:18.199194Z",
     "iopub.status.idle": "2026-10-30T13:14:18.210951Z",
     "shell.execute_reply": "2026-10-30T13:14:18.210502Z"
    }
   },
   "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>foo</th>\n",
       "      <th>bar</th>\n",
       "      <th>baz</th>\n",
       "      <th>zoo</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>count</th>\n",
       "      <td>6.000000</td>\n",
       "      <td>6.000000</td>\n",
       "      <td>6.000000</td>\n",
       "      <td>6.000000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>mean</th>\n",
       "      <td>0.500000</td>\n",
       "      <td>0.500000</td>\n",
       "      <td>3.500000</td>\n",
       "      <td>2.500000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>std</th>\n",
       "      <td>0.547723</td>\n",
       "      <td>0.547723</td>\n",
       "      <td>1.870829</td>\n",
       "      <td>1.870829</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>min</th>\n",
       "      <td>0.000000</td>\n",
       "      <td>0.000000</td>\n",
       "      <td>1.000000</td>\n",
       "      <td>0.000000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>25%</th>\n",
       "      <td>0.000000</td>\n",
       "      <td>0.000000</td>\n",
       "      <td>2.250000</td>\n",
       "      <td>1.250000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>50%</th>\n",
       "      <td>0.500000</td>\n",
       "      <td>0.500000</td>\n",
       "      <td>3.500000</td>\n",
       "      <td>2.500000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>75%</th>\n",
       "      <td>1.000000</td>\n",
       "      <td>1.000000</td>\n",
       "      <td>4.750000</td>\n",
       "      <td>3.750000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>max</th>\n",
       "      <td>1.000000</td>\n",
       "      <td>1.000000</td>\n",
       "      <td>6.000000</td>\n",
       "      <td>5.000000</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "            foo       bar       baz       zoo\n",
       "count  6.000000  6.000000  6.000000  6.000000\n",
       "mean   0.500000  0.500000  3.500000  2.500000\n",
       "std    0.547723  0.547723  1.870829  1.870829\n",
       "min    0.000000  0.000000  1.000000  0.000000\n",
       "25%    0.000000  0.000000  2.250000  1.250000\n",
       "50%    0.500000  0.500000  3.500000  2.500000\n",
       "75%    1.000000  1.000000  4.750000  3.750000\n",
       "max    1.000000  1.000000  6.000000  5.000000"
      ]
     },
     "execution_count": 77,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data_bis.describe()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Comptage : valeurs uniques, dénombrement de valeurs et appartenance"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Une autre catégorie de méthodes connexes permet d’extraire des informations sur les valeurs contenues dans une série unidimensionnelle. \n",
    "Il s'agit des méthodes `.count()`, `.unique()`, `.nunique()` et `.value_counts()`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 78,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:18.212639Z",
     "iopub.status.busy": "2026-10-30T13:14:18.212483Z",
     "iopub.status.idle": "2026-10-30T13:14:18.217578Z",
     "shell.execute_reply": "2026-10-30T13:14:18.217092Z"
    }
   },
   "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>foo</th>\n",
       "      <th>bar</th>\n",
       "      <th>baz</th>\n",
       "      <th>zoo</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>one</td>\n",
       "      <td>A</td>\n",
       "      <td>1</td>\n",
       "      <td>x</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>one</td>\n",
       "      <td>A</td>\n",
       "      <td>2</td>\n",
       "      <td>y</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>one</td>\n",
       "      <td>B</td>\n",
       "      <td>3</td>\n",
       "      <td>z</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>two</td>\n",
       "      <td>A</td>\n",
       "      <td>4</td>\n",
       "      <td>q</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>two</td>\n",
       "      <td>B</td>\n",
       "      <td>5</td>\n",
       "      <td>w</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>two</td>\n",
       "      <td>B</td>\n",
       "      <td>6</td>\n",
       "      <td>t</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   foo bar  baz zoo\n",
       "0  one   A    1   x\n",
       "1  one   A    2   y\n",
       "2  one   B    3   z\n",
       "3  two   A    4   q\n",
       "4  two   B    5   w\n",
       "5  two   B    6   t"
      ]
     },
     "execution_count": 78,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 79,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:18.219302Z",
     "iopub.status.busy": "2026-10-30T13:14:18.219151Z",
     "iopub.status.idle": "2026-10-30T13:14:18.222366Z",
     "shell.execute_reply": "2026-10-30T13:14:18.221931Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "6"
      ]
     },
     "execution_count": 79,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Combien de valuers : ici , on peut également utiliser .size(), puisqu'il n'y a pas de valeurs manquantes    \n",
    "data['foo'].count()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Pour déterminer le nombre de valeurs uniques d’une variable, plutôt que chercher à écrire soi-même une fonction, on utilise la méthode `nunique`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 80,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:18.224040Z",
     "iopub.status.busy": "2026-10-30T13:14:18.223882Z",
     "iopub.status.idle": "2026-10-30T13:14:18.227684Z",
     "shell.execute_reply": "2026-10-30T13:14:18.227373Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "foo    2\n",
       "bar    2\n",
       "baz    6\n",
       "zoo    6\n",
       "dtype: int64"
      ]
     },
     "execution_count": 80,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# On affiche combien de valeurs uniques par colonne :\n",
    "data.nunique()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 81,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:18.229328Z",
     "iopub.status.busy": "2026-10-30T13:14:18.229175Z",
     "iopub.status.idle": "2026-10-30T13:14:18.232365Z",
     "shell.execute_reply": "2026-10-30T13:14:18.232019Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "Valeurs uniques dans la colonne foo :\n",
      "['one' 'two']\n",
      "\n",
      "Valeurs uniques dans la colonne bar :\n",
      "['A' 'B']\n",
      "\n",
      "Valeurs uniques dans la colonne baz :\n",
      "[1 2 3 4 5 6]\n",
      "\n",
      "Valeurs uniques dans la colonne zoo :\n",
      "['x' 'y' 'z' 'q' 'w' 't']\n"
     ]
    }
   ],
   "source": [
    "# Pour chaque colonne selectionnée, on affiche les valeurs uniques :\n",
    "for col in data.columns:\n",
    "    print((f\"\\nValeurs uniques dans la colonne {col} :\"))\n",
    "    print(data[col].unique())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 82,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:18.234042Z",
     "iopub.status.busy": "2026-10-30T13:14:18.233874Z",
     "iopub.status.idle": "2026-10-30T13:14:18.238672Z",
     "shell.execute_reply": "2026-10-30T13:14:18.238226Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "Valeurs et occurrences dans la colonne foo :\n",
      "foo\n",
      "one    3\n",
      "two    3\n",
      "Name: count, dtype: int64\n",
      "\n",
      "Valeurs et occurrences dans la colonne bar :\n",
      "bar\n",
      "A    3\n",
      "B    3\n",
      "Name: count, dtype: int64\n",
      "\n",
      "Valeurs et occurrences dans la colonne baz :\n",
      "baz\n",
      "1    1\n",
      "2    1\n",
      "3    1\n",
      "4    1\n",
      "5    1\n",
      "6    1\n",
      "Name: count, dtype: int64\n",
      "\n",
      "Valeurs et occurrences dans la colonne zoo :\n",
      "zoo\n",
      "x    1\n",
      "y    1\n",
      "z    1\n",
      "q    1\n",
      "w    1\n",
      "t    1\n",
      "Name: count, dtype: int64\n"
     ]
    }
   ],
   "source": [
    "# On peut obtenir ces deux informations en une seule instruction :\n",
    "for col in data.columns:\n",
    "    print((f\"\\nValeurs et occurrences dans la colonne {col} :\"))\n",
    "    print(data[col].value_counts())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Filtres et opérations "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "L’opération de sélection de lignes s’appelle Filtrer. Elle s’utilise en fonction d’une condition logique, on sélectionne les données sur une condition logique.\n",
    "\n",
    "Il existe plusieurs méthodes en Pandas. La plus simple est d’utiliser les boolean mask, déjà vus en numpy.\n",
    "\n",
    "La plupart de ce qu’on a pu voir avec les arrays de NumPy est applicable aux objets Series et DataFrames de Pandas. On peut par exemple effectuer des opérations arithmétiques sur les objets, ou encore appliquer des fonctions mathématiques."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Opérations sur les DataFrames\n",
    "\n",
    "On peut appliquer des opérations arithmétiques sur les DataFrames, de la même manière que pour les Series. Les opérations sont effectuées élément par élément, en alignant les lignes et les colonnes par leurs étiquettes respectives."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 83,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:18.240359Z",
     "iopub.status.busy": "2026-10-30T13:14:18.240204Z",
     "iopub.status.idle": "2026-10-30T13:14:18.247429Z",
     "shell.execute_reply": "2026-10-30T13:14:18.246978Z"
    }
   },
   "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>Population</th>\n",
       "      <th>Area</th>\n",
       "      <th>Density</th>\n",
       "      <th>NewCol1</th>\n",
       "      <th>NewCol2</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>Suisse</th>\n",
       "      <td>8.516</td>\n",
       "      <td>41285.0</td>\n",
       "      <td>0.000206</td>\n",
       "      <td>17.032</td>\n",
       "      <td>0.000206</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>France</th>\n",
       "      <td>67.060</td>\n",
       "      <td>551695.0</td>\n",
       "      <td>0.000122</td>\n",
       "      <td>134.120</td>\n",
       "      <td>0.000122</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>USA</th>\n",
       "      <td>328.200</td>\n",
       "      <td>3796742.0</td>\n",
       "      <td>0.000086</td>\n",
       "      <td>656.400</td>\n",
       "      <td>0.000086</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Italie</th>\n",
       "      <td>60.360</td>\n",
       "      <td>301338.0</td>\n",
       "      <td>0.000200</td>\n",
       "      <td>120.720</td>\n",
       "      <td>0.000200</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "        Population       Area   Density  NewCol1   NewCol2\n",
       "Suisse       8.516    41285.0  0.000206   17.032  0.000206\n",
       "France      67.060   551695.0  0.000122  134.120  0.000122\n",
       "USA        328.200  3796742.0  0.000086  656.400  0.000086\n",
       "Italie      60.360   301338.0  0.000200  120.720  0.000200"
      ]
     },
     "execution_count": 83,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df1['NewCol1'] = df1['Population'] * 2\n",
    "df1['NewCol2'] = df1['Population']/df1['Area']\n",
    "df1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Filtrer un DataFrame : masque booléen *vs* `query()`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Pandas propose deux façons principales de filtrer un DataFrame : les masques booléens “à la NumPy”, très flexibles, et la méthode `query()`, plus lisible et proche du SQL.\n",
    "\n",
    "Voici un exemple de DataFrame que nous allons utiliser pour illustrer ces deux approches :"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 84,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:18.249145Z",
     "iopub.status.busy": "2026-10-30T13:14:18.248946Z",
     "iopub.status.idle": "2026-10-30T13:14:18.254143Z",
     "shell.execute_reply": "2026-10-30T13:14:18.253815Z"
    }
   },
   "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>foo</th>\n",
       "      <th>bar</th>\n",
       "      <th>baz</th>\n",
       "      <th>zoo</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>one</td>\n",
       "      <td>A</td>\n",
       "      <td>1</td>\n",
       "      <td>x</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>one</td>\n",
       "      <td>A</td>\n",
       "      <td>2</td>\n",
       "      <td>y</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>one</td>\n",
       "      <td>B</td>\n",
       "      <td>3</td>\n",
       "      <td>z</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>two</td>\n",
       "      <td>A</td>\n",
       "      <td>4</td>\n",
       "      <td>q</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>two</td>\n",
       "      <td>B</td>\n",
       "      <td>5</td>\n",
       "      <td>w</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>two</td>\n",
       "      <td>B</td>\n",
       "      <td>6</td>\n",
       "      <td>t</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   foo bar  baz zoo\n",
       "0  one   A    1   x\n",
       "1  one   A    2   y\n",
       "2  one   B    3   z\n",
       "3  two   A    4   q\n",
       "4  two   B    5   w\n",
       "5  two   B    6   t"
      ]
     },
     "execution_count": 84,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Filtre \"à la NumPy\" (masques booléens)\n",
    "\n",
    "Avec cette méthode, on crée un masque booléen en appliquant une condition logique sur une ou plusieurs colonnes du DataFrame. On utilise ensuite ce masque pour sélectionner les lignes correspondantes.\n",
    "\n",
    "\n",
    "- Avantages\n",
    "    - Très flexible (méthodes pandas/NumPy, fonctions Python).\n",
    "    - Compatible avec tous les noms de colonnes.\n",
    "- Inconvénients\n",
    "    - Syntaxe moins intuitive car `& | ~` au lieu de `and or not` et parenthèses obligatoires."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 85,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:18.255779Z",
     "iopub.status.busy": "2026-10-30T13:14:18.255594Z",
     "iopub.status.idle": "2026-10-30T13:14:18.259725Z",
     "shell.execute_reply": "2026-10-30T13:14:18.259173Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0     True\n",
       "1     True\n",
       "2    False\n",
       "3     True\n",
       "4    False\n",
       "5    False\n",
       "Name: bar, dtype: bool"
      ]
     },
     "execution_count": 85,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "mask = data[\"bar\"]==\"A\"\n",
    "mask"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 86,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:18.261465Z",
     "iopub.status.busy": "2026-10-30T13:14:18.261277Z",
     "iopub.status.idle": "2026-10-30T13:14:18.266961Z",
     "shell.execute_reply": "2026-10-30T13:14:18.266577Z"
    }
   },
   "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>foo</th>\n",
       "      <th>bar</th>\n",
       "      <th>baz</th>\n",
       "      <th>zoo</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>one</td>\n",
       "      <td>A</td>\n",
       "      <td>1</td>\n",
       "      <td>x</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>one</td>\n",
       "      <td>A</td>\n",
       "      <td>2</td>\n",
       "      <td>y</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>two</td>\n",
       "      <td>A</td>\n",
       "      <td>4</td>\n",
       "      <td>q</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   foo bar  baz zoo\n",
       "0  one   A    1   x\n",
       "1  one   A    2   y\n",
       "3  two   A    4   q"
      ]
     },
     "execution_count": 86,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data[mask]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 87,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:18.268552Z",
     "iopub.status.busy": "2026-10-30T13:14:18.268396Z",
     "iopub.status.idle": "2026-10-30T13:14:18.274341Z",
     "shell.execute_reply": "2026-10-30T13:14:18.273910Z"
    }
   },
   "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>foo</th>\n",
       "      <th>bar</th>\n",
       "      <th>baz</th>\n",
       "      <th>zoo</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>one</td>\n",
       "      <td>A</td>\n",
       "      <td>2</td>\n",
       "      <td>y</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>two</td>\n",
       "      <td>A</td>\n",
       "      <td>4</td>\n",
       "      <td>q</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   foo bar  baz zoo\n",
       "1  one   A    2   y\n",
       "3  two   A    4   q"
      ]
     },
     "execution_count": 87,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# `&` pour le `and` \n",
    "# `|` pour le `or` \n",
    "# `~` pour le `not` \n",
    "# parenthèses obligatoires\n",
    "mask = (data[\"bar\"]==\"A\") & (data[\"baz\"]>=2) \n",
    "data[mask]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Remarque : dans l'exemple ci-dessous, on utilise la méthode `str.startswith('t')` pour vérifier si les chaînes de caractères de la colonne 'foo' commencent par la lettre 't'. `.str.` est une méthode particulière qui permet de traiter chaque valeur d’une colonne comme un string natif en Python sur lequel appliquer une méthode ultérieure (en l’occurrence `startswith`)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 88,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:18.276142Z",
     "iopub.status.busy": "2026-10-30T13:14:18.275946Z",
     "iopub.status.idle": "2026-10-30T13:14:18.281816Z",
     "shell.execute_reply": "2026-10-30T13:14:18.281424Z"
    }
   },
   "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>foo</th>\n",
       "      <th>bar</th>\n",
       "      <th>baz</th>\n",
       "      <th>zoo</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>two</td>\n",
       "      <td>A</td>\n",
       "      <td>4</td>\n",
       "      <td>q</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>two</td>\n",
       "      <td>B</td>\n",
       "      <td>5</td>\n",
       "      <td>w</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>two</td>\n",
       "      <td>B</td>\n",
       "      <td>6</td>\n",
       "      <td>t</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   foo bar  baz zoo\n",
       "3  two   A    4   q\n",
       "4  two   B    5   w\n",
       "5  two   B    6   t"
      ]
     },
     "execution_count": 88,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "mask = data['foo'].str.startswith('t')\n",
    "data[mask]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Filtre avec `query()`\n",
    "\n",
    "- Avantages\n",
    "    - Syntaxe type SQL : plus lisible.\n",
    "    - Pas de parenthèses autour des conditions.\n",
    "- Inconvénients \n",
    "    - Limitations si noms de colonnes avec espaces ou fonctions Python complexes.\n",
    "    - Expression passée en chaîne de caractères."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 89,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:18.283451Z",
     "iopub.status.busy": "2026-10-30T13:14:18.283299Z",
     "iopub.status.idle": "2026-10-30T13:14:18.290442Z",
     "shell.execute_reply": "2026-10-30T13:14:18.290021Z"
    }
   },
   "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>foo</th>\n",
       "      <th>bar</th>\n",
       "      <th>baz</th>\n",
       "      <th>zoo</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>one</td>\n",
       "      <td>A</td>\n",
       "      <td>1</td>\n",
       "      <td>x</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>one</td>\n",
       "      <td>A</td>\n",
       "      <td>2</td>\n",
       "      <td>y</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>two</td>\n",
       "      <td>A</td>\n",
       "      <td>4</td>\n",
       "      <td>q</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   foo bar  baz zoo\n",
       "0  one   A    1   x\n",
       "1  one   A    2   y\n",
       "3  two   A    4   q"
      ]
     },
     "execution_count": 89,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data.query(\"bar=='A'\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 90,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:18.292161Z",
     "iopub.status.busy": "2026-10-30T13:14:18.291963Z",
     "iopub.status.idle": "2026-10-30T13:14:18.299615Z",
     "shell.execute_reply": "2026-10-30T13:14:18.299160Z"
    }
   },
   "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>foo</th>\n",
       "      <th>bar</th>\n",
       "      <th>baz</th>\n",
       "      <th>zoo</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>one</td>\n",
       "      <td>A</td>\n",
       "      <td>2</td>\n",
       "      <td>y</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>two</td>\n",
       "      <td>A</td>\n",
       "      <td>4</td>\n",
       "      <td>q</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   foo bar  baz zoo\n",
       "1  one   A    2   y\n",
       "3  two   A    4   q"
      ]
     },
     "execution_count": 90,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data.query(\"bar=='A' & baz>=2\") \n",
    "# idem que \n",
    "# data[(data[\"bar\"]==\"A\") & (data[\"baz\"]>=2)]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Aggregations"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## `groupby` : analyse par groupes"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "La méthode `groupby` permet de grouper les données suivant certains critères."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Commençons par un exemple simple :"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 91,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:18.301880Z",
     "iopub.status.busy": "2026-10-30T13:14:18.301689Z",
     "iopub.status.idle": "2026-10-30T13:14:18.312188Z",
     "shell.execute_reply": "2026-10-30T13:14:18.311761Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/markdown": [
       "**DataFrame initial**"
      ],
      "text/plain": [
       "<IPython.core.display.Markdown object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "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>key</th>\n",
       "      <th>data</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>A</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>B</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>C</td>\n",
       "      <td>2</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>A</td>\n",
       "      <td>3</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>B</td>\n",
       "      <td>4</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>C</td>\n",
       "      <td>5</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "  key  data\n",
       "0   A     0\n",
       "1   B     1\n",
       "2   C     2\n",
       "3   A     3\n",
       "4   B     4\n",
       "5   C     5"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/markdown": [
       "**GroupBy sum sur la colonne 'key'**"
      ],
      "text/plain": [
       "<IPython.core.display.Markdown object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "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>data</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>key</th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>A</th>\n",
       "      <td>3</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>B</th>\n",
       "      <td>5</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>C</th>\n",
       "      <td>7</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "     data\n",
       "key      \n",
       "A       3\n",
       "B       5\n",
       "C       7"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "df = pd.DataFrame({'key': ['A', 'B', 'C', 'A', 'B', 'C'],\n",
    "                   'data': range(6)}, columns=['key', 'data'])\n",
    "\n",
    "display(Markdown(\"**DataFrame initial**\"))\n",
    "display(df)\n",
    "display(Markdown(\"**GroupBy sum sur la colonne 'key'**\"))\n",
    "display(df.groupby('key').sum())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 92,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:18.314028Z",
     "iopub.status.busy": "2026-10-30T13:14:18.313841Z",
     "iopub.status.idle": "2026-10-30T13:14:18.324765Z",
     "shell.execute_reply": "2026-10-30T13:14:18.324366Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/markdown": [
       "**DataFrame initial**"
      ],
      "text/plain": [
       "<IPython.core.display.Markdown object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "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>key</th>\n",
       "      <th>data</th>\n",
       "      <th>toto</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>A</td>\n",
       "      <td>0</td>\n",
       "      <td>a</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>B</td>\n",
       "      <td>1</td>\n",
       "      <td>a</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>C</td>\n",
       "      <td>2</td>\n",
       "      <td>b</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>A</td>\n",
       "      <td>3</td>\n",
       "      <td>b</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>B</td>\n",
       "      <td>4</td>\n",
       "      <td>a</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>C</td>\n",
       "      <td>5</td>\n",
       "      <td>b</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "  key  data toto\n",
       "0   A     0    a\n",
       "1   B     1    a\n",
       "2   C     2    b\n",
       "3   A     3    b\n",
       "4   B     4    a\n",
       "5   C     5    b"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/markdown": [
       "**GroupBy sum sur la colonne 'key'**"
      ],
      "text/plain": [
       "<IPython.core.display.Markdown object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "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>data</th>\n",
       "      <th>toto</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>key</th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>A</th>\n",
       "      <td>3</td>\n",
       "      <td>ab</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>B</th>\n",
       "      <td>5</td>\n",
       "      <td>aa</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>C</th>\n",
       "      <td>7</td>\n",
       "      <td>bb</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "     data toto\n",
       "key           \n",
       "A       3   ab\n",
       "B       5   aa\n",
       "C       7   bb"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "df = pd.DataFrame({'key': ['A', 'B', 'C', 'A', 'B', 'C'],\n",
    "                   'data': range(6),\n",
    "                   'toto': ['a','a','b','b','a','b']}, \n",
    "                   columns=['key', 'data', 'toto'])\n",
    "\n",
    "display(Markdown(\"**DataFrame initial**\"))\n",
    "display(df)\n",
    "display(Markdown(\"**GroupBy sum sur la colonne 'key'**\"))\n",
    "display(df.groupby('key').sum())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "C'est ce que l'on appelle le paradigme « split-apply-combine » (diviser-appliquer-combiner) : d'abord on génère un groupe de DataFrame en fonction de la valeur de la clé spécifiée. À chaque groupe, on applique ensuite une fonction d'agrégation (ici la somme) pour obtenir une seule ligne.  Enfin on fusionne les lignes obtenues dans un nouveau DataFrame.\n",
    "\n",
    "\n",
    "![](03.08-split-apply-combine.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Passons à un exemple plus riche."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 93,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:18.326474Z",
     "iopub.status.busy": "2026-10-30T13:14:18.326323Z",
     "iopub.status.idle": "2026-10-30T13:14:18.332535Z",
     "shell.execute_reply": "2026-10-30T13:14:18.332101Z"
    }
   },
   "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>foo</th>\n",
       "      <th>bar</th>\n",
       "      <th>baz</th>\n",
       "      <th>zoo</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>one</td>\n",
       "      <td>A</td>\n",
       "      <td>1</td>\n",
       "      <td>x</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>one</td>\n",
       "      <td>A</td>\n",
       "      <td>2</td>\n",
       "      <td>y</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>one</td>\n",
       "      <td>B</td>\n",
       "      <td>3</td>\n",
       "      <td>z</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>two</td>\n",
       "      <td>A</td>\n",
       "      <td>4</td>\n",
       "      <td>q</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>two</td>\n",
       "      <td>B</td>\n",
       "      <td>5</td>\n",
       "      <td>w</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>two</td>\n",
       "      <td>B</td>\n",
       "      <td>6</td>\n",
       "      <td>t</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   foo bar  baz zoo\n",
       "0  one   A    1   x\n",
       "1  one   A    2   y\n",
       "2  one   B    3   z\n",
       "3  two   A    4   q\n",
       "4  two   B    5   w\n",
       "5  two   B    6   t"
      ]
     },
     "execution_count": 93,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data = pd.DataFrame( {  \"foo\" : [\"one\", \"one\", \"one\", \"two\", \"two\", \"two\"],\n",
    "                        \"bar\" : [\"A\", \"A\", \"B\", \"A\", \"B\", \"B\"],\n",
    "                        \"baz\" : [1, 2, 3, 4, 5, 6], \n",
    "                        \"zoo\" : [\"x\", \"y\", \"z\", \"q\", \"w\", 't'] } )\n",
    "\n",
    "data"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "On va regrouper les données en fonction de la colonne `foo`. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 94,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:18.334247Z",
     "iopub.status.busy": "2026-10-30T13:14:18.334094Z",
     "iopub.status.idle": "2026-10-30T13:14:18.337571Z",
     "shell.execute_reply": "2026-10-30T13:14:18.337182Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<pandas.core.groupby.generic.DataFrameGroupBy object at 0x7f29dba4faa0>"
      ]
     },
     "execution_count": 94,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data_gbfoo = data.groupby(\"foo\")\n",
    "data_gbfoo"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "On obtient un objet `DataFrameGroupBy`, c'est-à-dire **une sorte de dictionnaire de DatFrame dont les clés sont les valeurs uniques de la colonne `foo` et les valeurs sont les DataFrame correspondant à chaque groupe** :"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 95,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:18.339214Z",
     "iopub.status.busy": "2026-10-30T13:14:18.339052Z",
     "iopub.status.idle": "2026-10-30T13:14:18.349181Z",
     "shell.execute_reply": "2026-10-30T13:14:18.348745Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/markdown": [
       "**Nom du groupe : one**"
      ],
      "text/plain": [
       "<IPython.core.display.Markdown object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "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>foo</th>\n",
       "      <th>bar</th>\n",
       "      <th>baz</th>\n",
       "      <th>zoo</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>one</td>\n",
       "      <td>A</td>\n",
       "      <td>1</td>\n",
       "      <td>x</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>one</td>\n",
       "      <td>A</td>\n",
       "      <td>2</td>\n",
       "      <td>y</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>one</td>\n",
       "      <td>B</td>\n",
       "      <td>3</td>\n",
       "      <td>z</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   foo bar  baz zoo\n",
       "0  one   A    1   x\n",
       "1  one   A    2   y\n",
       "2  one   B    3   z"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/markdown": [
       "**Nom du groupe : two**"
      ],
      "text/plain": [
       "<IPython.core.display.Markdown object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "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>foo</th>\n",
       "      <th>bar</th>\n",
       "      <th>baz</th>\n",
       "      <th>zoo</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>two</td>\n",
       "      <td>A</td>\n",
       "      <td>4</td>\n",
       "      <td>q</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>two</td>\n",
       "      <td>B</td>\n",
       "      <td>5</td>\n",
       "      <td>w</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>two</td>\n",
       "      <td>B</td>\n",
       "      <td>6</td>\n",
       "      <td>t</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   foo bar  baz zoo\n",
       "3  two   A    4   q\n",
       "4  two   B    5   w\n",
       "5  two   B    6   t"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "for name, group in data_gbfoo:\n",
    "    display(Markdown(f\"**Nom du groupe : {name}**\"))\n",
    "    display(group)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "On peut regrouper les données en fonction de plusieurs colonnes. Par exemple, on peut regrouper les données en fonction des colonnes `foo` et `bar` :"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 96,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:18.350912Z",
     "iopub.status.busy": "2026-10-30T13:14:18.350726Z",
     "iopub.status.idle": "2026-10-30T13:14:18.368429Z",
     "shell.execute_reply": "2026-10-30T13:14:18.368016Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/markdown": [
       "**Nom du groupe : ('one', 'A')**"
      ],
      "text/plain": [
       "<IPython.core.display.Markdown object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "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>foo</th>\n",
       "      <th>bar</th>\n",
       "      <th>baz</th>\n",
       "      <th>zoo</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>one</td>\n",
       "      <td>A</td>\n",
       "      <td>1</td>\n",
       "      <td>x</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>one</td>\n",
       "      <td>A</td>\n",
       "      <td>2</td>\n",
       "      <td>y</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   foo bar  baz zoo\n",
       "0  one   A    1   x\n",
       "1  one   A    2   y"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/markdown": [
       "**Nom du groupe : ('one', 'B')**"
      ],
      "text/plain": [
       "<IPython.core.display.Markdown object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "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>foo</th>\n",
       "      <th>bar</th>\n",
       "      <th>baz</th>\n",
       "      <th>zoo</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>one</td>\n",
       "      <td>B</td>\n",
       "      <td>3</td>\n",
       "      <td>z</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   foo bar  baz zoo\n",
       "2  one   B    3   z"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/markdown": [
       "**Nom du groupe : ('two', 'A')**"
      ],
      "text/plain": [
       "<IPython.core.display.Markdown object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "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>foo</th>\n",
       "      <th>bar</th>\n",
       "      <th>baz</th>\n",
       "      <th>zoo</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>two</td>\n",
       "      <td>A</td>\n",
       "      <td>4</td>\n",
       "      <td>q</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   foo bar  baz zoo\n",
       "3  two   A    4   q"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/markdown": [
       "**Nom du groupe : ('two', 'B')**"
      ],
      "text/plain": [
       "<IPython.core.display.Markdown object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "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>foo</th>\n",
       "      <th>bar</th>\n",
       "      <th>baz</th>\n",
       "      <th>zoo</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>two</td>\n",
       "      <td>B</td>\n",
       "      <td>5</td>\n",
       "      <td>w</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>two</td>\n",
       "      <td>B</td>\n",
       "      <td>6</td>\n",
       "      <td>t</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   foo bar  baz zoo\n",
       "4  two   B    5   w\n",
       "5  two   B    6   t"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "data_gbfoobar =  data.groupby(['foo', 'bar'])\n",
    "for name, group in data_gbfoobar:\n",
    "    display(Markdown(f\"**Nom du groupe : {name}**\"))\n",
    "    display(group)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "On peut appliquer une même opération sur chaque groupe en même temps. Par exemple, on peut extraire une seule colonne de cet objet, et on obtient un objet `SeriesGroupBy` :"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 97,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:18.370215Z",
     "iopub.status.busy": "2026-10-30T13:14:18.370047Z",
     "iopub.status.idle": "2026-10-30T13:14:18.373557Z",
     "shell.execute_reply": "2026-10-30T13:14:18.373091Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<pandas.core.groupby.generic.SeriesGroupBy object at 0x7f29dba4ee10>"
      ]
     },
     "execution_count": 97,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data_gbfoo_baz = data_gbfoo[\"baz\"]\n",
    "data_gbfoo_baz"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "On obtient ainsi un objet `SeriesGroupBy`, qui est une sorte de dictionnaire de Series. Chaque Series correspond à un groupe."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 98,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:18.375349Z",
     "iopub.status.busy": "2026-10-30T13:14:18.375201Z",
     "iopub.status.idle": "2026-10-30T13:14:18.381709Z",
     "shell.execute_reply": "2026-10-30T13:14:18.381234Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/markdown": [
       "**Nom du groupe : one**"
      ],
      "text/plain": [
       "<IPython.core.display.Markdown object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "0    1\n",
       "1    2\n",
       "2    3\n",
       "Name: baz, dtype: int64"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/markdown": [
       "**Nom du groupe : two**"
      ],
      "text/plain": [
       "<IPython.core.display.Markdown object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "3    4\n",
       "4    5\n",
       "5    6\n",
       "Name: baz, dtype: int64"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "for name, group in data_gbfoo_baz:\n",
    "    display(Markdown(f\"**Nom du groupe : {name}**\"))\n",
    "    display(group)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Pour ne pas itérer sur les groupes mais juste connaître des informations sur les groupes, on peut utiliser :\n",
    "\n",
    "- `ngroups` permet de connaître le nombre de groupes obtenus (ici 2 car on a deux valeurs uniques dans la colonne `foo`) \n",
    "- `size` combien d'éléments sont dans chaque groupe\n",
    "- `groups`  les indices des lignes de chaque groupe avec la méthode."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 99,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:18.383531Z",
     "iopub.status.busy": "2026-10-30T13:14:18.383345Z",
     "iopub.status.idle": "2026-10-30T13:14:18.389654Z",
     "shell.execute_reply": "2026-10-30T13:14:18.389185Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "foo\n",
       "one    3\n",
       "two    3\n",
       "dtype: int64"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "{'one': [0, 1, 2], 'two': [3, 4, 5]}"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Combien de groupes ?\n",
    "nb_groupe = data_gbfoo.ngroups\n",
    "display(nb_groupe)\n",
    "\n",
    "# Combien d'éléments par groupe ?\n",
    "nb_element_par_groupe = data_gbfoo.size()\n",
    "display(nb_element_par_groupe)\n",
    "\n",
    "# Quelles sont les indices des lignes de chaque groupe ?\n",
    "group_indices = data_gbfoo.groups\n",
    "display(group_indices)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "L'object `DataFrameGroupBy` peut être immaginé comme un dictionnaire de DataFrames où chaque clé est un nom de groupe et chaque valeur est le DataFrame correspondant à ce groupe. Cependant, ce c'est pas un vrai dictionnaire, en particulier on ne peut pas accéder aux groupes avec la syntaxe des dictionnaires `data_gbfoo['one']`.  \n",
    "\n",
    "Pour récuperer les dataframes on doit utiliser la méthode `get_group()` :"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 100,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:18.391341Z",
     "iopub.status.busy": "2026-10-30T13:14:18.391173Z",
     "iopub.status.idle": "2026-10-30T13:14:18.400313Z",
     "shell.execute_reply": "2026-10-30T13:14:18.399789Z"
    }
   },
   "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>foo</th>\n",
       "      <th>bar</th>\n",
       "      <th>baz</th>\n",
       "      <th>zoo</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>one</td>\n",
       "      <td>A</td>\n",
       "      <td>1</td>\n",
       "      <td>x</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>one</td>\n",
       "      <td>A</td>\n",
       "      <td>2</td>\n",
       "      <td>y</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>one</td>\n",
       "      <td>B</td>\n",
       "      <td>3</td>\n",
       "      <td>z</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   foo bar  baz zoo\n",
       "0  one   A    1   x\n",
       "1  one   A    2   y\n",
       "2  one   B    3   z"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "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>foo</th>\n",
       "      <th>bar</th>\n",
       "      <th>baz</th>\n",
       "      <th>zoo</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>two</td>\n",
       "      <td>A</td>\n",
       "      <td>4</td>\n",
       "      <td>q</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>two</td>\n",
       "      <td>B</td>\n",
       "      <td>5</td>\n",
       "      <td>w</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>two</td>\n",
       "      <td>B</td>\n",
       "      <td>6</td>\n",
       "      <td>t</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   foo bar  baz zoo\n",
       "3  two   A    4   q\n",
       "4  two   B    5   w\n",
       "5  two   B    6   t"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "data_one = data_gbfoo.get_group(\"one\") # les lignes du groupe \"one\" constitue un dataframe\n",
    "data_two = data_gbfoo.get_group(\"two\") # les lignes du groupe \"two\" constitue un dataframe\n",
    "display(data_one, data_two)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "L'utilisation principale de `groupby` nest pas la simple création de sous-DataFrames. Généralement **on applique des fonctions d'agrégation** sur les groupes obtenus. **On obtient un unique DataFrame** avec une ligne par groupe.\n",
    "\n",
    "Par exemple, on peut calculer la somme des éléments de chaque groupe (on se rappelle que l'operation \"somme\" entre chaînes de caractères correspond à la concaténation de ces chaînes) :"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 101,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:18.402071Z",
     "iopub.status.busy": "2026-10-30T13:14:18.401865Z",
     "iopub.status.idle": "2026-10-30T13:14:18.407586Z",
     "shell.execute_reply": "2026-10-30T13:14:18.407164Z"
    }
   },
   "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>bar</th>\n",
       "      <th>baz</th>\n",
       "      <th>zoo</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>foo</th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>one</th>\n",
       "      <td>AAB</td>\n",
       "      <td>6</td>\n",
       "      <td>xyz</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>two</th>\n",
       "      <td>ABB</td>\n",
       "      <td>15</td>\n",
       "      <td>qwt</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "     bar  baz  zoo\n",
       "foo               \n",
       "one  AAB    6  xyz\n",
       "two  ABB   15  qwt"
      ]
     },
     "execution_count": 101,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data_gbfoo.sum()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 102,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:18.409465Z",
     "iopub.status.busy": "2026-10-30T13:14:18.409275Z",
     "iopub.status.idle": "2026-10-30T13:14:18.413378Z",
     "shell.execute_reply": "2026-10-30T13:14:18.412885Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "foo\n",
       "one     6\n",
       "two    15\n",
       "Name: baz, dtype: int64"
      ]
     },
     "execution_count": 102,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data_gbfoo['baz'].sum()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "On peut aussi **regrouper par plusieurs colonnes**, puis appliquer une fonction d'agrégation.\n",
    "\n",
    "Exemple : regroupons les données de `data` en utilisant les colonnes `foo` et `bar`, puis calculons la somme de la colonne `baz` pour chaque groupe unique de `foo` et `bar`.\n",
    "\n",
    "Voici les étapes décomposées :\n",
    "\n",
    "1. **`data_grfoobar = data.groupby(['foo', 'bar'])`** : regroupe le DataFrame par les colonnes `foo` et `bar`, en obtenant autant de groupes que de couples différents ; \n",
    "2. **`data_grfoobar['baz'].sum()`** : calcule la somme de `baz` pour chaque groupe.\n",
    "3. Facultatif : **`.unstack()`** qui transforme le résultat en une table pivotée, en déplaçant les valeurs uniques de `bar` en colonnes. Ainsi, pour chaque valeur de `foo`, on obtient les sommes de `baz` réparties par les différentes valeurs de `bar`.\n",
    "\n",
    "Le résultat est un DataFrame avec les valeurs de `foo` en index et celles de `bar` en colonnes, où chaque cellule contient la somme correspondante de `baz`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 103,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:18.415097Z",
     "iopub.status.busy": "2026-10-30T13:14:18.414902Z",
     "iopub.status.idle": "2026-10-30T13:14:18.429645Z",
     "shell.execute_reply": "2026-10-30T13:14:18.429170Z"
    }
   },
   "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>foo</th>\n",
       "      <th>bar</th>\n",
       "      <th>baz</th>\n",
       "      <th>zoo</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>one</td>\n",
       "      <td>A</td>\n",
       "      <td>1</td>\n",
       "      <td>x</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>one</td>\n",
       "      <td>A</td>\n",
       "      <td>2</td>\n",
       "      <td>y</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   foo bar  baz zoo\n",
       "0  one   A    1   x\n",
       "1  one   A    2   y"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "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>foo</th>\n",
       "      <th>bar</th>\n",
       "      <th>baz</th>\n",
       "      <th>zoo</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>one</td>\n",
       "      <td>B</td>\n",
       "      <td>3</td>\n",
       "      <td>z</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   foo bar  baz zoo\n",
       "2  one   B    3   z"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "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>foo</th>\n",
       "      <th>bar</th>\n",
       "      <th>baz</th>\n",
       "      <th>zoo</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>two</td>\n",
       "      <td>A</td>\n",
       "      <td>4</td>\n",
       "      <td>q</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   foo bar  baz zoo\n",
       "3  two   A    4   q"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "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>foo</th>\n",
       "      <th>bar</th>\n",
       "      <th>baz</th>\n",
       "      <th>zoo</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>two</td>\n",
       "      <td>B</td>\n",
       "      <td>5</td>\n",
       "      <td>w</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>two</td>\n",
       "      <td>B</td>\n",
       "      <td>6</td>\n",
       "      <td>t</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   foo bar  baz zoo\n",
       "4  two   B    5   w\n",
       "5  two   B    6   t"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# on regroupe les couples (foo, bar) \n",
    "data_grfoobar = data.groupby(['foo', 'bar'])\n",
    "\n",
    "display(data_grfoobar.get_group(('one', 'A'))) # df pour qui foo == 'one' et bar == 'A'\n",
    "display(data_grfoobar.get_group(('one', 'B'))) # df pour qui foo == 'one' et bar == 'B'\n",
    "display(data_grfoobar.get_group(('two', 'A'))) # df pour qui foo == 'two' et bar == 'A'\n",
    "display(data_grfoobar.get_group(('two', 'B'))) # df pour qui foo == 'two' et bar == 'B'\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 104,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:18.431405Z",
     "iopub.status.busy": "2026-10-30T13:14:18.431217Z",
     "iopub.status.idle": "2026-10-30T13:14:18.435957Z",
     "shell.execute_reply": "2026-10-30T13:14:18.435633Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "foo  bar\n",
       "one  A       3\n",
       "     B       3\n",
       "two  A       4\n",
       "     B      11\n",
       "Name: baz, dtype: int64"
      ]
     },
     "execution_count": 104,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Pour chaque groupe, on somme les valeurs de la colonne \"baz\"\n",
    "data_gbfoobarbaz = data_grfoobar['baz'].sum() \n",
    "data_gbfoobarbaz # c'est une Series avec un MultiIndex"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 105,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:18.437751Z",
     "iopub.status.busy": "2026-10-30T13:14:18.437609Z",
     "iopub.status.idle": "2026-10-30T13:14:18.441076Z",
     "shell.execute_reply": "2026-10-30T13:14:18.440740Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "MultiIndex([('one', 'A'),\n",
       "            ('one', 'B'),\n",
       "            ('two', 'A'),\n",
       "            ('two', 'B')],\n",
       "           names=['foo', 'bar'])"
      ]
     },
     "execution_count": 105,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data_gbfoobarbaz.index"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Avec `.unstack()`, on peut transformer la Series MultiIndex en DataFrame pour une meilleure lisibilité :"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 106,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:18.442611Z",
     "iopub.status.busy": "2026-10-30T13:14:18.442465Z",
     "iopub.status.idle": "2026-10-30T13:14:18.448293Z",
     "shell.execute_reply": "2026-10-30T13:14:18.447783Z"
    }
   },
   "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>bar</th>\n",
       "      <th>A</th>\n",
       "      <th>B</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>foo</th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>one</th>\n",
       "      <td>3</td>\n",
       "      <td>3</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>two</th>\n",
       "      <td>4</td>\n",
       "      <td>11</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "bar  A   B\n",
       "foo       \n",
       "one  3   3\n",
       "two  4  11"
      ]
     },
     "execution_count": 106,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data_gbfoobarbaz.unstack()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## `pivot_table` : tableaux croisés"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "Le résultat obtenu avec l’instruction :  \n",
    "\n",
    "`data.groupby(['col1', 'col2'])['col3'].sum().unstack()`\n",
    "\n",
    "est un **tableau croisé dynamique** : à chaque couple `(col1, col2)`, on associe la **somme des valeurs de `col3`**.\n",
    "\n",
    "Si le DataFrame `data` contient les colonnes `col1`, `col2` et `col3`, on peut imaginer que chaque ligne du DataFrame correspond à un triplet `(x_i, y_i, z_i)`. On peut alors representer cela comme un point en 3D : \n",
    "\n",
    "- `col1` est la coordonnée `x_i`\n",
    "- `col2` est la coordonnée `y_i`\n",
    "- `col3` est la coordonnée `z_i`\n",
    "\n",
    "On peut imaginer que l’on cherche à représenter une fonction de deux variables $z = f(x,y)$, où $x$ et $y$ sont les coordonnées dans le plan horizontal, et $z$ est la hauteur associée à chaque point $(x,y)$. **Problème** : si le couple `(x_i, y_i)` est unique dans le DataFrame, on a un seul point pour cette position. Mais si plusieurs lignes $j$ ont le même couple `(x_i, y_i)` mais des valeurs différentes `(z_i)_j`, cela correspond à plusieurs point empilés verticalement, ce qui ne représente plus une fonction de $\\mathbb R^2$ vers $\\mathbb R$.  Dans ce cas, pour obtenir une valeur unique $\\tilde z_i$, il faut **agréger** ces différentes valeurs. C’est là qu’intervient l’agrégation (ici la somme) :\n",
    "\n",
    "$$\n",
    "(x_i, y_i) \\mapsto \\tilde z_i = \\sum_{j} (z_i)_j\n",
    "$$\n",
    "\n",
    "On pourrait  également utiliser d’autres fonctions d’agrégation comme `mean`, `max`, `min`, etc.\n",
    "\n",
    "\n",
    "C’est une opération très courante en analyse de données. Pandas propose donc une méthode dédiée : `pivot_table`.\n",
    "\n",
    "```python\n",
    "pivot = data.pivot_table(\n",
    "    index='col1',\n",
    "    columns='col2',\n",
    "    values='col3',\n",
    "    aggfunc='sum'\n",
    ")\n",
    "```\n",
    "\n",
    "On applique la méthode `pivot_table` sur le DataFrame `data` en précisant :\n",
    "\n",
    "* `index` : le nom de la colonne dans de DataFrame qui sera la ligne du nouveau tableau\n",
    "* `columns` : le nom de la colonne dans le DataFrame qui sera la colonne du nouveau tableau\n",
    "* `values` : le nom de la colonne sur laquelle appliquer l’agrégation\n",
    "* `aggfunc` : la fonction d’agrégation (`sum`, `mean`, `count`, `min`, `max`, etc.). Si ce n'est pas spécifié, par défaut on calcule la moyenne (mean) des valeurs.\n",
    "\n",
    "Avec `pivot_table`, on obtient un résultat équivalent à \n",
    "\n",
    "`groupby(...).agg(...).unstack()`\n",
    "\n",
    "mais plus lisible et directement structuré sous forme de tableau croisé.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![](C0Q63.png)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 107,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:18.450090Z",
     "iopub.status.busy": "2026-10-30T13:14:18.449900Z",
     "iopub.status.idle": "2026-10-30T13:14:18.455219Z",
     "shell.execute_reply": "2026-10-30T13:14:18.454864Z"
    }
   },
   "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>foo</th>\n",
       "      <th>bar</th>\n",
       "      <th>baz</th>\n",
       "      <th>zoo</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>one</td>\n",
       "      <td>A</td>\n",
       "      <td>1</td>\n",
       "      <td>x</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>one</td>\n",
       "      <td>A</td>\n",
       "      <td>2</td>\n",
       "      <td>y</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>one</td>\n",
       "      <td>B</td>\n",
       "      <td>3</td>\n",
       "      <td>z</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>two</td>\n",
       "      <td>A</td>\n",
       "      <td>4</td>\n",
       "      <td>q</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>two</td>\n",
       "      <td>B</td>\n",
       "      <td>5</td>\n",
       "      <td>w</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>two</td>\n",
       "      <td>B</td>\n",
       "      <td>6</td>\n",
       "      <td>t</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   foo bar  baz zoo\n",
       "0  one   A    1   x\n",
       "1  one   A    2   y\n",
       "2  one   B    3   z\n",
       "3  two   A    4   q\n",
       "4  two   B    5   w\n",
       "5  two   B    6   t"
      ]
     },
     "execution_count": 107,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 108,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:18.456899Z",
     "iopub.status.busy": "2026-10-30T13:14:18.456722Z",
     "iopub.status.idle": "2026-10-30T13:14:18.465861Z",
     "shell.execute_reply": "2026-10-30T13:14:18.465446Z"
    }
   },
   "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>bar</th>\n",
       "      <th>A</th>\n",
       "      <th>B</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>foo</th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>one</th>\n",
       "      <td>3</td>\n",
       "      <td>3</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>two</th>\n",
       "      <td>4</td>\n",
       "      <td>11</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "bar  A   B\n",
       "foo       \n",
       "one  3   3\n",
       "two  4  11"
      ]
     },
     "execution_count": 108,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data.pivot_table(index='foo', columns='bar', values='baz', aggfunc='sum')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Lorsqu'on a besoin de calculer des sous-totaux, on peut utiliser l'option `margins=True` :"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 109,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:18.467454Z",
     "iopub.status.busy": "2026-10-30T13:14:18.467300Z",
     "iopub.status.idle": "2026-10-30T13:14:18.486588Z",
     "shell.execute_reply": "2026-10-30T13:14:18.486202Z"
    }
   },
   "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>bar</th>\n",
       "      <th>A</th>\n",
       "      <th>B</th>\n",
       "      <th>All</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>foo</th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>one</th>\n",
       "      <td>3</td>\n",
       "      <td>3</td>\n",
       "      <td>6</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>two</th>\n",
       "      <td>4</td>\n",
       "      <td>11</td>\n",
       "      <td>15</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>All</th>\n",
       "      <td>7</td>\n",
       "      <td>14</td>\n",
       "      <td>21</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "bar  A   B  All\n",
       "foo            \n",
       "one  3   3    6\n",
       "two  4  11   15\n",
       "All  7  14   21"
      ]
     },
     "execution_count": 109,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data.pivot_table(index='foo', \n",
    "                 columns='bar', \n",
    "                 values='baz', \n",
    "                 aggfunc='sum', \n",
    "                 margins=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## `crosstab` : tableaux croisés"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`crosstab` est une fonction principalement utilisée pour **compter les occurrences ou les fréquences** des combinaisons de valeurs de deux colonnes.\n",
    "\n",
    "```python\n",
    "pd.crosstab(data['col1'], data['col2'])\n",
    "```\n",
    "\n",
    "Cette fonction crée un tableau croisé indipendamment d'un DataFrame, en utilisant deux séries ou colonnes comme entrées :\n",
    "\n",
    "* `index` : les données stockées dans une séries ou une colonne d'un DataFrame qui sera la ligne du nouveau tableau\n",
    "* `columns` : les données stockées dans une séries ou une colonne d'un DataFrame qui sera la colonne du nouveau tableau\n",
    "\n",
    "Pour chaque combinaison unique de valeurs dans `col1` et `col2`, `crosstab` compte le nombre d'occurrences et remplit le tableau avec ces comptes.\n",
    "\n",
    "`crosstab` peut également agréger les valeurs d'une colonne supplémentaire, mais la syntaxe est différente de `pivot_table` :\n",
    "\n",
    "```python\n",
    "pd.crosstab(\n",
    "    index=data['col1'],\n",
    "    columns=data['col2'],\n",
    "    values=data['col3'],\n",
    "    aggfunc=sum\n",
    ")\n",
    "```\n",
    "\n",
    "* `index`  et `columns` : comme précédemment\n",
    "* `values` : les données stockées dans une séries ou une colonne d'un DataFrame qui sera la colonne à agréger\n",
    "* `aggfunc` : fonction d’agrégation (par défaut `count` pour compter les occurrences, mais on peut utiliser `sum`, `mean`, etc.)\n",
    "\n",
    "Enfin, `crosstab` propose un argument très pratique, `normalize`, qui permet d'afficher les résultats en pourcentages (par rapport à la somme totale, aux lignes ou aux colonnes) en une seule instruction, sans calcul supplémentaire."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 110,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:18.488326Z",
     "iopub.status.busy": "2026-10-30T13:14:18.488172Z",
     "iopub.status.idle": "2026-10-30T13:14:18.493511Z",
     "shell.execute_reply": "2026-10-30T13:14:18.493106Z"
    }
   },
   "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>foo</th>\n",
       "      <th>bar</th>\n",
       "      <th>baz</th>\n",
       "      <th>zoo</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>one</td>\n",
       "      <td>A</td>\n",
       "      <td>1</td>\n",
       "      <td>x</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>one</td>\n",
       "      <td>A</td>\n",
       "      <td>2</td>\n",
       "      <td>y</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>one</td>\n",
       "      <td>B</td>\n",
       "      <td>3</td>\n",
       "      <td>z</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>two</td>\n",
       "      <td>A</td>\n",
       "      <td>4</td>\n",
       "      <td>q</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>two</td>\n",
       "      <td>B</td>\n",
       "      <td>5</td>\n",
       "      <td>w</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>two</td>\n",
       "      <td>B</td>\n",
       "      <td>6</td>\n",
       "      <td>t</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   foo bar  baz zoo\n",
       "0  one   A    1   x\n",
       "1  one   A    2   y\n",
       "2  one   B    3   z\n",
       "3  two   A    4   q\n",
       "4  two   B    5   w\n",
       "5  two   B    6   t"
      ]
     },
     "execution_count": 110,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "La commande `pd.crosstab(data['foo'], data['bar'])` crée un tableau croisé (ou \"contingence\") en comptant les occurrences de chaque combinaison unique de valeurs dans les colonnes `foo` et `bar` du DataFrame `data`.\n",
    "\n",
    "1. **`pd.crosstab(data['foo'], data['bar'])`** : compte le nombre de fois que chaque paire de valeurs (`foo`,`bar`) apparaît dans le DataFrame.\n",
    "2. Le résultat est un nouveau DataFrame avec :\n",
    "   - Les valeurs uniques de `foo` comme index (lignes).\n",
    "   - Les valeurs uniques de `bar` comme colonnes.\n",
    "   - Les cellules contiennent le nombre d'occurrences de chaque combinaison `(foo, bar)`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 111,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:18.495767Z",
     "iopub.status.busy": "2026-10-30T13:14:18.495537Z",
     "iopub.status.idle": "2026-10-30T13:14:18.505148Z",
     "shell.execute_reply": "2026-10-30T13:14:18.504770Z"
    }
   },
   "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>bar</th>\n",
       "      <th>A</th>\n",
       "      <th>B</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>foo</th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>one</th>\n",
       "      <td>2</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>two</th>\n",
       "      <td>1</td>\n",
       "      <td>2</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "bar  A  B\n",
       "foo      \n",
       "one  2  1\n",
       "two  1  2"
      ]
     },
     "execution_count": 111,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pd.crosstab(data['foo'], data['bar'])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 112,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:18.506800Z",
     "iopub.status.busy": "2026-10-30T13:14:18.506648Z",
     "iopub.status.idle": "2026-10-30T13:14:18.514977Z",
     "shell.execute_reply": "2026-10-30T13:14:18.514503Z"
    }
   },
   "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>bar</th>\n",
       "      <th>A</th>\n",
       "      <th>B</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>foo</th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>one</th>\n",
       "      <td>2</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>two</th>\n",
       "      <td>1</td>\n",
       "      <td>2</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "bar  A  B\n",
       "foo      \n",
       "one  2  1\n",
       "two  1  2"
      ]
     },
     "execution_count": 112,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# idem que\n",
    "data.pivot_table(values='baz', index='foo', columns='bar', aggfunc='count')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "C'est utile pour obtenir une vue d'ensemble des fréquences d'association entre les deux variables. Pour obtenir les fréquences au lieu des comptes bruts dans un tableau croisé de pandas, on ajoute l'argument `normalize=True`, chaque valeur est alors divisée par le total de toutes les combinaisons. Cela affichera chaque valeur comme une proportion du total, ce qui correspond aux fréquences relatives."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 113,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:18.516809Z",
     "iopub.status.busy": "2026-10-30T13:14:18.516654Z",
     "iopub.status.idle": "2026-10-30T13:14:18.526221Z",
     "shell.execute_reply": "2026-10-30T13:14:18.525872Z"
    }
   },
   "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>bar</th>\n",
       "      <th>A</th>\n",
       "      <th>B</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>foo</th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>one</th>\n",
       "      <td>0.333333</td>\n",
       "      <td>0.166667</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>two</th>\n",
       "      <td>0.166667</td>\n",
       "      <td>0.333333</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "bar         A         B\n",
       "foo                    \n",
       "one  0.333333  0.166667\n",
       "two  0.166667  0.333333"
      ]
     },
     "execution_count": 113,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pd.crosstab(data['foo'], data['bar'], normalize=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "On peut aussi normaliser par ligne ou par colonne :\n",
    "\n",
    "- Par ligne (chaque ligne totalisera 1): `normalize='index'`\n",
    "- Par colonne (chaque colonne totalisera 1): `normalize='columns'`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 114,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:18.527831Z",
     "iopub.status.busy": "2026-10-30T13:14:18.527679Z",
     "iopub.status.idle": "2026-10-30T13:14:18.537253Z",
     "shell.execute_reply": "2026-10-30T13:14:18.536912Z"
    }
   },
   "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>bar</th>\n",
       "      <th>A</th>\n",
       "      <th>B</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>foo</th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>one</th>\n",
       "      <td>0.666667</td>\n",
       "      <td>0.333333</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>two</th>\n",
       "      <td>0.333333</td>\n",
       "      <td>0.666667</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "bar         A         B\n",
       "foo                    \n",
       "one  0.666667  0.333333\n",
       "two  0.333333  0.666667"
      ]
     },
     "execution_count": 114,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pd.crosstab(data['foo'], data['bar'], normalize='index')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Bonus : `melt`, du format large au format long"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div class=\"alert alert-block alert-warning\">\n",
    "\n",
    "**Bonus : hors séance.** Cette section n'est pas nécessaire pour la suite du cours, mais elle vous servira dès que vous voudrez tracer des données groupées avec Seaborn ou Plotly.\n",
    "\n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`pivot_table` transforme un tableau **long** (une observation par ligne) en tableau **large** (une modalité par colonne). `melt` réalise l'opération **inverse**.\n",
    "\n",
    "Pourquoi en aurait-on besoin ? Parce que Seaborn et Plotly attendent presque toujours le format **long** : une colonne qui dit *de quelle variable il s'agit*, une colonne qui dit *quelle en est la valeur*."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 115,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:18.538870Z",
     "iopub.status.busy": "2026-10-30T13:14:18.538719Z",
     "iopub.status.idle": "2026-10-30T13:14:18.544055Z",
     "shell.execute_reply": "2026-10-30T13:14:18.543690Z"
    }
   },
   "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>etudiant</th>\n",
       "      <th>algebre</th>\n",
       "      <th>analyse</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>Alice</td>\n",
       "      <td>12</td>\n",
       "      <td>14</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>Bob</td>\n",
       "      <td>15</td>\n",
       "      <td>8</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>Chloé</td>\n",
       "      <td>9</td>\n",
       "      <td>16</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "  etudiant  algebre  analyse\n",
       "0    Alice       12       14\n",
       "1      Bob       15        8\n",
       "2    Chloé        9       16"
      ]
     },
     "execution_count": 115,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "notes_large = pd.DataFrame({ \"etudiant\" : [\"Alice\", \"Bob\", \"Chloé\"],\n",
    "                             \"algebre\"  : [12, 15, 9],\n",
    "                             \"analyse\"  : [14, 8, 16] })\n",
    "notes_large"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 116,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:18.545630Z",
     "iopub.status.busy": "2026-10-30T13:14:18.545479Z",
     "iopub.status.idle": "2026-10-30T13:14:18.553355Z",
     "shell.execute_reply": "2026-10-30T13:14:18.552803Z"
    }
   },
   "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>etudiant</th>\n",
       "      <th>matiere</th>\n",
       "      <th>note</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>Alice</td>\n",
       "      <td>algebre</td>\n",
       "      <td>12</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>Bob</td>\n",
       "      <td>algebre</td>\n",
       "      <td>15</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>Chloé</td>\n",
       "      <td>algebre</td>\n",
       "      <td>9</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>Alice</td>\n",
       "      <td>analyse</td>\n",
       "      <td>14</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>Bob</td>\n",
       "      <td>analyse</td>\n",
       "      <td>8</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>Chloé</td>\n",
       "      <td>analyse</td>\n",
       "      <td>16</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "  etudiant  matiere  note\n",
       "0    Alice  algebre    12\n",
       "1      Bob  algebre    15\n",
       "2    Chloé  algebre     9\n",
       "3    Alice  analyse    14\n",
       "4      Bob  analyse     8\n",
       "5    Chloé  analyse    16"
      ]
     },
     "execution_count": 116,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "notes_long = notes_large.melt( id_vars    = \"etudiant\",\n",
    "                               var_name   = \"matiere\",\n",
    "                               value_name = \"note\" )\n",
    "notes_long"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- `id_vars` : les colonnes à **conserver telles quelles** (ici l'identifiant de l'étudiant) ;\n",
    "- `var_name` : le nom de la nouvelle colonne qui recevra les **anciens noms de colonnes** ;\n",
    "- `value_name` : le nom de la nouvelle colonne qui recevra les **valeurs**.\n",
    "\n",
    "Le format long se prête directement à une visualisation groupée :"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 117,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:18.555493Z",
     "iopub.status.busy": "2026-10-30T13:14:18.555313Z",
     "iopub.status.idle": "2026-10-30T13:14:19.371492Z",
     "shell.execute_reply": "2026-10-30T13:14:19.370882Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAjMAAAGwCAYAAABcnuQpAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjYuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/P9b71AAAACXBIWXMAAA9hAAAPYQGoP6dpAAA0EUlEQVR4nO3deVyU5f7/8feAMLggiIaAouC+5ZKamZ7CXVxyydQy961cSkktvuWCHiXt6LHSNFtcWq1cjmVZppiZWW64JMeFQDguPyoVBBUU7t8fHuc0Cag4MHPr6/l43I9H93Vf13V/Zpjg7X1fM2MxDMMQAACASbk5uwAAAIDbQZgBAACmRpgBAACmRpgBAACmRpgBAACmRpgBAACmRpgBAACmVszZBRS2nJwcnTx5Ut7e3rJYLM4uBwAA3ATDMHT+/HkFBQXJzS3/ay93fJg5efKkgoODnV0GAAAogOTkZFWsWDHfPnd8mPH29pZ09ckoXbq0k6sBAAA3Iy0tTcHBwba/4/m548PMtVtLpUuXJswAAGAyN7NEhAXAAADA1AgzAADA1AgzAADA1O74NTMAAEhSdna2Ll++7Owy8F8eHh5yd3d3yFyEGQDAHc0wDJ0+fVrnzp1zdin4C19fXwUEBNz258ARZgAAd7RrQcbf318lSpTgA1RdgGEYunDhglJSUiRJgYGBtzUfYQYAcMfKzs62BZmyZcs6uxz8SfHixSVJKSkp8vf3v61bTiwABgDcsa6tkSlRooSTK0Furv1cbnctE2EGAHDH49aSa3LUz4UwAwAATI0wAwAATM2pYWbr1q3q2rWrgoKCZLFYtHbt2uv6xMXF6ZFHHpGPj4+8vb31wAMPKCkpqeiLBQDABSQmJspisSg2NtbZpbgMp4aZjIwMNWjQQAsWLMj1eHx8vFq2bKlatWppy5Yt2rdvnyZPniwvL68irhQAgKI3aNAgde/e3a4tODhYp06dUr169ZxTlAty6luzw8PDFR4enufxF198UZ06ddKcOXNsbVWqVCmK0gAAcEnu7u4KCAi4rTmysrLk6enpoIqcz2XXzOTk5Gj9+vWqUaOGOnToIH9/fzVr1izXW1F/lpmZqbS0NLsNAIDCFhYWprFjx2rcuHEqU6aMypcvryVLligjI0ODBw+Wt7e3qlatqq+++krS1c/AGTp0qEJDQ1W8eHHVrFlTr776qm2+adOmafny5frXv/4li8Uii8WiLVu25Hqb6dChQ+rUqZNKlSql8uXLq3///vr999/tahszZowiIiJUrlw5tWvX7qbGmYXLhpmUlBSlp6fr5ZdfVseOHfXNN9+oR48e6tmzp7777rs8x0VHR8vHx8e2BQcHF2HVAIC72fLly+XrmaNtn3+gpwf20dNPP61HHwlX0zoh2vHVSrVt2VT9+z2hs/G7dPE/BxTg46n3F0Zrb8xaRY4dov+LjNQHi+cq8+QvGtuvs3p17aD2rVoqce8WJe7dosYhvsr8f0ckSVkp8co8+YsS927Rw39rqXpVK2j7lx9p3XsLdSopXr26dVbmyV+UefIX5WRlaPmypbJcOqeY1cv0+owJNzXudrai5LKfAJyTkyNJ6tatm8aPHy9JatiwobZv367Fixfr4YcfznVcZGSkIiIibPtpaWkEGgBAkWjQoIEix42UJE0aO0z/WPi2ypbx1dB+vSRJ/zf+aS1ZsVIHDh1Rs8YNNGXCGNvY0EoVtWNXrFZ9/rV6PdJRpUqWkJeXlzKzLivAv1ye51yyYqUa3ltbMyLH2drenDtD1Zq21dH4RFWvGiJJqhpSSbNees7WJ+qVBTc1zgxcNsyUK1dOxYoVU506dezaa9eurW3btuU5zmq1ymq1FnZ5AABcp379+rb/dnd3l18ZX9WrXd3WVv6eq1+p8NsfZyRJb61YqaUfrVLSf07p4qVLyrp8WQ3q1rqlc+7d/4u+2/6zylZvet2xX48n20LJfQ3qFmicGbhsmPH09FTTpk11+PBhu/YjR46ocuXKTqoKAIC8eXh42O1bLBZ5FPOw25eu3n34bN0GTYyao9mTJ6pZkwbyLllS8xYt1c69+2/pnDmGoc7twjTz/yKuOxZQ/n9XdEr+97uQbnWcGTg1zKSnp+vYsWO2/YSEBMXGxsrPz0+VKlXSxIkT1adPHz300ENq1aqVNmzYoM8//1xbtmxxXtEAADjADz/v0QONG2rkoL62tl+PJ9v18fT0UHZ2dr7zNKxXW2u//FaVg4NUrNjN/1kv6DhX5NQFwLt27VKjRo3UqFEjSVJERIQaNWqkKVOmSJJ69OihxYsXa86cObr33nv19ttva9WqVWrZsqUzywYA4LZVDQnWnv2/aOOWH3Q0PlHT5ryu3fsO2vWpXDFIB+KO6MixBP1+5myuX8j41KDHdfZcqgaMmqSdew/o1+PJ2vjdDxoR8VK+Qaig41yRU6NYWFiYDMPIt8+QIUM0ZMiQIqoIAICiMbx/H+375bCefHqCLBaLencL14iBffTN5v+tCx3Sr5e2/rhTD3bqo/SMC/r603dVObiC3TxBAf6KWfueXpw1T137jVRmZpYqVQxU+7CWcnPL+5pFQce5IotxozRhcmlpafLx8VFqaqpKly7t7HIAAEXo0qVLSkhIUGhoaJF9enxRvy3ZVVmD6t6wT34/n1v5+22u6AUAAPAXhBkAAGBqhBkAAGBqhBkAAGBqhBkAAGBqhBkAAGBqhBkAAGBqhBkAAGBqhBkAAEwoMfmEvCrU076D/3bovDWatdfrb73n0DkLm7m/WQoAgAJqPHFFkZ5v+/jGRXq+uwlXZgAAwG3J7QswixJhBgAAF/VNzDa16t5f5Ws3V1DdFuoxYJTiE5Py7P/FNzGq26KTfKs2Vvteg/XeJ/+SV4V6OpeaZuvz4869atNzoHyrNlbVJm0UMXmWMi5csJvnfHqGBoyepLLVmyr0vlZ6490P7I57Vaint1asVK/BY+VXramiX31TkrT+my1q3rG3fKrcpypVqigqKkpXrlxx4DOSO8IMAAAuKuPCRT07YoB+WP+xvlr5jtzc3NRn2LPKycm5rm9i8gk9PmK8HunYWj9//ZmG9X9M02a/ZtfnYNwRde03Ut3D22rXxtV6f9E/tP3nvRr34iy7fv9cvFT31q6hHRs+1cQxwzRx2hx9u3W7XZ8ZcxeqS4dW2rVptQb27amNW37Q4Gde0Kgh/bQ35l968803tWzZMs2cOdPxT8xfsGYGAAAX1aNzO7v9xXOnK7j+Q4o7Eq+SJUvYHXv7vU9Uo2qooidPkCTVqBaqX/59TLNfW2LrM2/RUvXp3lljh/eXJFWrUllzZ0Sq3aOD9Hr0ZHl5WSVJzZs20sQxwyRJ1auG6Mede/X6W++p7UMP2ubq072zBvXtadsf8kykJoweqv69u0mSagfV1YwZMzRp0iRNnTrVUU9JrggzgEkV9eJFV7X7lQHOLgEoNPGJSYp6ZYF+3rNff5w5a7sik3zilGrVqGrX90h8gho3qGvX1rTRvXb7ew8cUnxikj5e84WtzTCknJwcJSb/R7WqX52zWeMGduOaNW6g199+367tvr+ca+/+Q9q97+D/wpPFTdnZ2bp06ZIuXLigEiXsw5cjEWYAAHBRjw4ao4pBAXpjzjQFBdyjnBxD97XurqxcFtwahmSxWP7SZtjt5+TkaNiTj2n0kCevGx9cITDfWv46d8kSxe3nNnI0+bnR6h7eVpLkWb667ZiXl1e+c98uwgwAAC7ojzPn9O+jv2rB7Klq2ezq27p/+HlPnv1rVgvVhs1b7dp27/vFbr/hvXV06HC8qoZWyvfcP+/Zb7f/0579qlktNN8xDevV1pH4BNvc1qBq+fZ3JBYAAwDggsr4llbZMr565/1PFZ+QpJhtP+n5qDl59h/65GM6fCxBL86cp6Pxifps3Qa998laSf+7qjJh1BD9tHufnv2/v2vfwX/r2K/H9cU3MRr/kv0C4B937tXcN97V0fhELV72kVZ/8Y3GDL3+as6fvTj+aX3w2eeaMXehDh0+pri4OK1cuVIvvfTS7T0RN4EwAwCAC3Jzc9OKN17R3gOHdF+b7po0bbZmvfRcnv1DK1XUR0v+qbVffqsm7XpqyYqVeuGZEZIkq6enJOneOjW1cdVSHUs4rjY9B6hZh16KmvO6AvzL2c317MiB2rv/FzXr8Jii5y/W7CkT1C6sRb71tgtroTXLF2rT1h/VolNfPfDAA5o3b54qV658m8/EjVmMv95Qu8OkpaXJx8dHqampKl26tLPLARyGBcBXsQAY+bl06ZISEhIUGhpa6Os2rsk8+cuNOxWRl199U2+994nid20q8nNbg+resE9+P59b+fvNmhkAAO4Qby77WI0b1pVfGV/9uHOv/rl4mZ4a9Lizyyp0hBkAAO4QxxKO6+XX3tSZc6kKDgrUsyMGatLYYc4uq9ARZgAAuEO8EvW8Xol63tllFDkWAAMAAFMjzAAAAFMjzAAAAFMjzAAAAFMjzAAAAFMjzAAAAFMjzAAAcBdr12uQJkx52dll3BY+ZwYAcFdKmn5vkZ6v/LCPi/R8dxOuzAAAAFNzapjZunWrunbtqqCgIFksFq1duzbPviNHjpTFYtH8+fOLrD4AAJzpm5htatW9v8rXbq6gui3UY8AoxScmSZISk0/Iq0I9rf1yo9r3GqwyVZuoadue2rEr1jb+jzPn1H/URFVt3EZlqjZR4zY9tHLtl3meb+Y/F6lxmx7XtTfv2FtRryyQJH23/We17NxXftWaqnzt5grr9qSO/+ekre/6b7aoecfe8vLyUpUqVRQVFaUrV6446BnJnVPDTEZGhho0aKAFCxbk22/t2rX66aefFBQUVESVAQDgfBkXLurZEQP0w/qP9dXKd+Tm5qY+w55VTk6Orc/U2a9p/FOD9PM3n6l6lRANGD3JFh4uZWbqvvp1tHr5Qu3evEZD+/XSkGci9fOe/bmeb2CfHoo7Eq9dsQdsbQcOHVbswTj1791NV65cUe+hz+pvDzTRzm9X6bt172tov8dksVgkSRu3/KDBz7ygUUP66dChQ3rzzTe1bNkyzZw5sxCfJSevmQkPD1d4eHi+fU6cOKExY8bo66+/VufOnYuoMgAAnK9H53Z2+4vnTldw/YcUdyReJUuWkCSNe2qQwts+LEmaPGG0GrXqpvjEJNWsVkUVAstr/FODbeNHDemnb2K2afUXX+v+++pfd76KQQFqF9ZCK1auVZOGV9cUrVi5Vn97oImqVA7WmbOpSk07r/C2D6tqSCVJUq3qVW3jX35tiSaMHqr+vbvJGlRFVapU0YwZMzRp0iRNnTrVsU/On7j0AuCcnBz1799fEydOVN26dW9qTGZmpjIzM237aWlphVUeAACFKj4xSVGvLNDPe/brjzNnbVdkkk+cUq0aV0PEvbVr2PoH+JeTJKX8fkY1q1VRdna2Xlnwtj77fINOnkpRZlaWMrMuq2SJEnmec8gTj2rkc1M0Z+okubu76eM16/XylAmSJL8yPurfu7u69hupNn9rrtZ/e0CPdu2owPL3SJL27j+k3fsOavZrSyTL1Zs/2dnZunTpki5cuKAS+Zz3drh0mJk9e7aKFSumZ5555qbHREdHKyoqqhCrursV9ep/V1VpyoEbdwKA2/TooDGqGBSgN+ZMU1DAPcrJMXRf6+7KunzZ1sejmIftv6/d7rkWeua/uVyvv/WeXol6XvVqVVfJEiU0YerLduP/qnO7MFk9PfSvDZtk9fRQZlaW3RWit/75d40eevUKz2frNmjanNe1/qO31KxxA+UYOZr83Gh1D28rz/LV7eb18vJyyHOSG5cNM7t379arr76qPXv22H44NyMyMlIRERG2/bS0NAUHBxdGiQAAFJo/zpzTv4/+qgWzp6pls8aSpB9+3nNLc/zw02516dBKTzzaVdLVkHMsIUm1qlfJc0yxYsX05GPdtGLlGlk9PfXYI+EqUby4XZ+G9WqrYb3amjR2uB7u2k8r165Xs8YN1LBebR2JT1DV0EqyBlW7xUdccC4bZr7//nulpKSoUqVKtrbs7Gw999xzmj9/vhITE3MdZ7VaZbVai6hKAAAKRxnf0ipbxlfvvP+pAv3vUdKJU5oc/c9bmqNqSCWt+XKjfty5V2V8S+vVJSv0/377Pd8wI0mDH39UDcIekSTFrH3P1p6Q9B+988Gn6tKulQID/HUkPkFHf01Uv15Xw9KL459Wj4GjVTEoQI8PGS03Nzft379fBw4c0N///vdbfAZunsuGmf79+6tt27Z2bR06dFD//v01ePDgPEYBAHBncHNz04o3XtFzU6J1X5vuqlElRHNnRKp9r5v/Gxg57iklJp9Q134jVaK4l4b0e0xdO7RW2vn0fMdVq1JZDzRpqDNnz9ktFC5R3EtHjiXo8U/X6Y+z5xTgf4+eGvy4hvXvLUlqF9ZCa5Yv1Mx/LtK8Rcvk4eGhWrVqadiwYQV7Em6SU8NMenq6jh07ZttPSEhQbGys/Pz8VKlSJZUtW9auv4eHhwICAlSzZs2iLhUAcIcprLVvmSd/cdhcbR5qrtgt6+zaLp04mOt/S5KvT2m7Nr8yPvr03dfyPcfGz5Zd12YYhlJ++0PDnnzMrr38PeX0yTv5z9curIXahbWQNejm3rjjCE4NM7t27VKrVq1s+9fWugwcOFDLli1zUlUAANy9Un7/Qx9+9rlOnv5/GtCnu7PLuSlODTNhYWEyDOOm++e1TgYAADhGpQYPq5xfGS2cM01lfH2cXc5Ncdk1MwAAoOj99daVGfBFkwAAwNQIMwCAO96tLGlA0XHUz4UwAwC4Y3l4XP103AsXLji5EuTm2s/l2s+poFgzAwC4Y7m7u8vX11cpKSmSpBIlStzSp8oXRNaVnBt3ugsYly7lfcwwdOHCBaWkpMjX11fu7u63dS7CDADgjhYQECBJtkBT2K6cK5rzuLpiGTeOGL6+vrafz22d67ZnAADAhVksFgUGBsrf31+X8/mCRUc5ufDZQj+HGQSNXpfvcQ8Pj9u+InMNYQYAcFdwd3d32B/P/BTLOFXo5zCDwvyW7L9iATAAADA1wgwAADA1wgwAADA1wgwAADA1wgwAADA1wgwAADA1wgwAADA1wgwAADA1wgwAADA1wgwAADA1wgwAADA1wgwAADA1wgwAADA1wgwAADA1wgwAADA1wgwAADA1wgwAADA1wgwAADA1wgwAADA1wgwAADA1wgwAADA1wgwAADA1wgwAADA1wgwAADA1wgwAADA1p4aZrVu3qmvXrgoKCpLFYtHatWttxy5fvqznn39e9957r0qWLKmgoCANGDBAJ0+edF7BAADA5Tg1zGRkZKhBgwZasGDBdccuXLigPXv2aPLkydqzZ49Wr16tI0eO6JFHHnFCpQAAwFUVc+bJw8PDFR4enusxHx8fbdy40a7t9ddf1/3336+kpCRVqlSpKEoEAAAuzqlh5lalpqbKYrHI19c3zz6ZmZnKzMy07aelpRVBZQAAwFlME2YuXbqkF154QU888YRKly6dZ7/o6GhFRUU5/PyNJ65w+JxmtMbb2RUAAGDPFO9munz5svr27aucnBy98cYb+faNjIxUamqqbUtOTi6iKgEAgDO4/JWZy5cvq3fv3kpISNDmzZvzvSojSVarVVartYiqAwAAzubSYeZakDl69KhiYmJUtmxZZ5cEAABcjFPDTHp6uo4dO2bbT0hIUGxsrPz8/BQUFKRevXppz549+uKLL5Sdna3Tp09Lkvz8/OTp6emssgEAgAtxapjZtWuXWrVqZduPiIiQJA0cOFDTpk3TunXrJEkNGza0GxcTE6OwsLCiKhMAALgwp4aZsLAwGYaR5/H8jgEAAEgmeTcTAABAXggzAADA1AgzAADA1AgzAADA1AgzAADA1AgzAADA1AgzAADA1AgzAADA1AgzAADA1AgzAADA1AgzAADA1AgzAADA1AgzAADA1AgzAADA1AgzAADA1AgzAADA1AgzAADA1AgzAADA1AgzAADA1AgzAADA1AgzAADA1AgzAADA1AgzAADA1AgzAADA1AgzAADA1AgzAADA1AgzAADA1AgzAADA1AgzAADA1AgzAADA1AgzAADA1AgzAADA1AgzAADA1JwaZrZu3aquXbsqKChIFotFa9eutTtuGIamTZumoKAgFS9eXGFhYfrll1+cUywAAHBJTg0zGRkZatCggRYsWJDr8Tlz5mjevHlasGCBdu7cqYCAALVr107nz58v4koBAICrKubMk4eHhys8PDzXY4ZhaP78+XrxxRfVs2dPSdLy5ctVvnx5ffjhhxo5cmRRlgoAAFyUy66ZSUhI0OnTp9W+fXtbm9Vq1cMPP6zt27fnOS4zM1NpaWl2GwAAuHO5bJg5ffq0JKl8+fJ27eXLl7cdy010dLR8fHxsW3BwcKHWCQAAnMtlw8w1FovFbt8wjOva/iwyMlKpqam2LTk5ubBLBAAATuTUNTP5CQgIkHT1Ck1gYKCtPSUl5bqrNX9mtVpltVoLvT4AAOAaXPbKTGhoqAICArRx40ZbW1ZWlr777js9+OCDTqwMAAC4EqdemUlPT9exY8ds+wkJCYqNjZWfn58qVaqkcePGadasWapevbqqV6+uWbNmqUSJEnriiSecWDUAAHAlTg0zu3btUqtWrWz7ERERkqSBAwdq2bJlmjRpki5evKhRo0bp7Nmzatasmb755ht5e3s7q2QAAOBinBpmwsLCZBhGnsctFoumTZumadOmFV1RAADAVFx2zQwAAMDNIMwAAABTI8wAAABTI8wAAABTI8wAAABTI8wAAABTI8wAAABTI8wAAABTI8wAAABTI8wAAABTI8wAAABTI8wAAABTI8wAAABTI8wAAABTI8wAAABTK+bsAgAAd4bGE1c4uwSXsMbb2RXcfQp8ZebcuXN6++23FRkZqTNnzkiS9uzZoxMnTjisOAAAgBsp0JWZ/fv3q23btvLx8VFiYqKGDx8uPz8/rVmzRsePH9eKFaRzAABQNAp0ZSYiIkKDBg3S0aNH5eXlZWsPDw/X1q1bHVYcAADAjRQozOzcuVMjR468rr1ChQo6ffr0bRcFAABwswoUZry8vJSWlnZd++HDh3XPPffcdlEAAAA3q0Bhplu3bpo+fbouX74sSbJYLEpKStILL7ygRx991KEFAgAA5KdAYeYf//iHfvvtN/n7++vixYt6+OGHVa1aNXl7e2vmzJmOrhEAACBPBXo3U+nSpbVt2zZt3rxZe/bsUU5Oju677z61bdvW0fUBAADkq0BhZsWKFerTp49at26t1q1b29qzsrL08ccfa8CAAQ4rEAAAID8Fus00ePBgpaamXtd+/vx5DR48+LaLAgAAuFkFCjOGYchisVzX/p///Ec+Pj63XRQAAMDNuqXbTI0aNZLFYpHFYlGbNm1UrNj/hmdnZyshIUEdO3Z0eJEAAAB5uaUw0717d0lSbGysOnTooFKlStmOeXp6KiQkhLdmAwCAInVLYWbq1KmSpJCQEPXp08fuqwwAAACcoUDvZho4cKAkaffu3YqLi5PFYlGdOnXUqFEjhxYHAABwIwUKMykpKerbt6+2bNkiX19fGYah1NRUtWrVSh9//DFfaQAAAIpMgd7NNHbsWKWlpemXX37RmTNndPbsWR08eFBpaWl65plnHF0jAABAngoUZjZs2KBFixapdu3atrY6depo4cKF+uqrrxxW3JUrV/TSSy8pNDRUxYsXV5UqVTR9+nTl5OQ47BwAAMDcCnSbKScnRx4eHte1e3h4ODRozJ49W4sXL9by5ctVt25d7dq1S4MHD5aPj4+effZZh50HAACYV4GuzLRu3VrPPvusTp48aWs7ceKExo8frzZt2jisuB9//FHdunVT586dFRISol69eql9+/batWuXw84BAADMrUBhZsGCBTp//rxCQkJUtWpVVatWTSEhITp//rxee+01hxXXsmVLbdq0SUeOHJEk7du3T9u2bVOnTp3yHJOZmam0tDS7DQAA3LkKdJspODhYe/bs0bfffqu4uDgZhqE6deo4/Fuzn3/+eaWmpqpWrVpyd3dXdna2Zs6cqccffzzPMdHR0YqKinJoHQBcV9L0e51dgkuoNOWAs0sAnKZAYUaSNm3apM2bNyslJUU5OTmKjY3Vhx9+KEl69913HVLcypUr9f777+vDDz9U3bp1FRsbq3HjxikoKMj2WTd/FRkZqYiICNt+WlqagoODHVIPAABwPQUKM1FRUZo+fbqaNGmiwMDAXL900hEmTpyoF154QX379pUk3XvvvTp+/Liio6PzDDNWq1VWq7VQ6gEAAK6nQGFm8eLFWrZsmfr37+/oeuxcuHBBbm72y3rc3d15azYAALApUJjJysrSgw8+6OhartO1a1fNnDlTlSpVUt26dbV3717NmzdPQ4YMKfRzAwAAcyjQu5mGDRtmWx9TmF5//XX16tVLo0aNUu3atTVhwgSNHDlSM2bMKPRzAwAAcyjQlZlLly5pyZIl+vbbb1W/fv3rPkBv3rx5DinO29tb8+fP1/z58x0yHwAAuPMUKMzs379fDRs2lCQdPHjQ7lhhLQYGAADITYHCTExMjKPrAAAAKJACrZkBAABwFYQZAABgaoQZAABgaoQZAABgaoQZAABgaoQZAABgaoQZAABgaoQZAABgaoQZAABgaoQZAABgaoQZAABgaoQZAABgaoQZAABgaoQZAABgaoQZAABgaoQZAABgaoQZAABgaoQZAABgaoQZAABgaoQZAABgaoQZAABgaoQZAABgaoQZAABgaoQZAABgaoQZAABgaoQZAABgaoQZAABgaoQZAABgaoQZAABgaoQZAABgaoQZAABgai4fZk6cOKEnn3xSZcuWVYkSJdSwYUPt3r3b2WUBAAAXUczZBeTn7NmzatGihVq1aqWvvvpK/v7+io+Pl6+vr7NLAwAALsKlw8zs2bMVHByspUuX2tpCQkKcVxAAAHA5Ln2bad26dWrSpIkee+wx+fv7q1GjRnrrrbfyHZOZmam0tDS7DQAA3LlcOsz8+uuvWrRokapXr66vv/5aTz31lJ555hmtWLEizzHR0dHy8fGxbcHBwUVYMQAAKGouHWZycnJ03333adasWWrUqJFGjhyp4cOHa9GiRXmOiYyMVGpqqm1LTk4uwooBAEBRc+kwExgYqDp16ti11a5dW0lJSXmOsVqtKl26tN0GAADuXC4dZlq0aKHDhw/btR05ckSVK1d2UkUAAMDVuHSYGT9+vHbs2KFZs2bp2LFj+vDDD7VkyRKNHj3a2aUBAAAX4dJhpmnTplqzZo0++ugj1atXTzNmzND8+fPVr18/Z5cGAABchEt/zowkdenSRV26dHF2GQAAwEW59JUZAACAGyHMAAAAUyPMAAAAUyPMAAAAUyPMAAAAUyPMAAAAUyPMAAAAUyPMAAAAUyPMAAAAUyPMAAAAUyPMAAAAUyPMAAAAUyPMAAAAUyPMAAAAUyPMAAAAUyPMAAAAUyPMAAAAUyPMAAAAUyPMAAAAUyPMAAAAUyPMAAAAUyPMAAAAUyPMAAAAUyPMAAAAUyPMAAAAUyPMAAAAUyPMAAAAUyPMAAAAUyPMAAAAUyPMAAAAUyPMAAAAUyPMAAAAUyPMAAAAUzNVmImOjpbFYtG4ceOcXQoAAHARpgkzO3fu1JIlS1S/fn1nlwIAAFyIKcJMenq6+vXrp7feektlypRxdjkAAMCFmCLMjB49Wp07d1bbtm1v2DczM1NpaWl2GwAAuHMVc3YBN/Lxxx9rz5492rlz5031j46OVlRUVCFXBQAAXIVLX5lJTk7Ws88+q/fff19eXl43NSYyMlKpqam2LTk5uZCrBAAAzuTSV2Z2796tlJQUNW7c2NaWnZ2trVu3asGCBcrMzJS7u7vdGKvVKqvVWtSlAgAAJ3HpMNOmTRsdOHDArm3w4MGqVauWnn/++euCDAAAuPu4dJjx9vZWvXr17NpKliypsmXLXtcOAADuTi69ZgYAAOBGXPrKTG62bNni7BIAAIAL4coMAAAwNcIMAAAwNcIMAAAwNcIMAAAwNcIMAAAwNcIMAAAwNcIMAAAwNcIMAAAwNcIMAAAwNcIMAAAwNcIMAAAwNcIMAAAwNcIMAAAwNcIMAAAwNcIMAAAwNcIMAAAwNcIMAAAwNcIMAAAwNcIMAAAwNcIMAAAwNcIMAAAwNcIMAAAwNcIMAAAwNcIMAAAwNcIMAAAwNcIMAAAwNcIMAAAwNcIMAAAwNcIMAAAwNcIMAAAwNcIMAAAwNcIMAAAwNcIMAAAwNZcOM9HR0WratKm8vb3l7++v7t276/Dhw84uCwAAuBCXDjPfffedRo8erR07dmjjxo26cuWK2rdvr4yMDGeXBgAAXEQxZxeQnw0bNtjtL126VP7+/tq9e7ceeughJ1UFAABciUuHmb9KTU2VJPn5+eXZJzMzU5mZmbb9tLS0Qq8LAAA4j0vfZvozwzAUERGhli1bql69enn2i46Olo+Pj20LDg4uwioBAEBRM02YGTNmjPbv36+PPvoo336RkZFKTU21bcnJyUVUIQAAcAZT3GYaO3as1q1bp61bt6pixYr59rVarbJarUVUGQAAcDaXDjOGYWjs2LFas2aNtmzZotDQUGeXBAAAXIxLh5nRo0frww8/1L/+9S95e3vr9OnTkiQfHx8VL17cydUBAABX4NJrZhYtWqTU1FSFhYUpMDDQtq1cudLZpQEAABfh0ldmDMNwdgkAAMDFufSVGQAAgBshzAAAAFMjzAAAAFMjzAAAAFMjzAAAAFMjzAAAAFMjzAAAAFMjzAAAAFMjzAAAAFMjzAAAAFMjzAAAAFMjzAAAAFMjzAAAAFMjzAAAAFMjzAAAAFMjzAAAAFMjzAAAAFMjzAAAAFMjzAAAAFMjzAAAAFMjzAAAAFMjzAAAAFMjzAAAAFMjzAAAAFMjzAAAAFMjzAAAAFMjzAAAAFMjzAAAAFMjzAAAAFMjzAAAAFMjzAAAAFMjzAAAAFMjzAAAAFMzRZh54403FBoaKi8vLzVu3Fjff/+9s0sCAAAuwuXDzMqVKzVu3Di9+OKL2rt3r/72t78pPDxcSUlJzi4NAAC4AJcPM/PmzdPQoUM1bNgw1a5dW/Pnz1dwcLAWLVrk7NIAAIALKObsAvKTlZWl3bt364UXXrBrb9++vbZv357rmMzMTGVmZtr2U1NTJUlpaWm3VUt25sXbGn+nOO+R7ewSXMLtvp4cgdfkVbwmr+I16Tp4TV51u6/Ja+MNw7hhX5cOM7///ruys7NVvnx5u/by5cvr9OnTuY6Jjo5WVFTUde3BwcGFUuPdpp6zC3AV0T7OrgD/xWvyv3hNugxek//loNfk+fPn5eOT/1wuHWausVgsdvuGYVzXdk1kZKQiIiJs+zk5OTpz5ozKli2b5xjcnLS0NAUHBys5OVmlS5d2djkAr0m4HF6TjmMYhs6fP6+goKAb9nXpMFOuXDm5u7tfdxUmJSXluqs111itVlmtVrs2X1/fwirxrlS6dGn+J4VL4TUJV8Nr0jFudEXmGpdeAOzp6anGjRtr48aNdu0bN27Ugw8+6KSqAACAK3HpKzOSFBERof79+6tJkyZq3ry5lixZoqSkJD311FPOLg0AALgAlw8zffr00R9//KHp06fr1KlTqlevnr788ktVrlzZ2aXddaxWq6ZOnXrdbTzAWXhNwtXwmnQOi3Ez73kCAABwUS69ZgYAAOBGCDMAAMDUCDMAALiIrKwszZo1S3Fxcc4uxVQIM7CzZcsWWSwWnTt3TpK0bNkyPqcHpjNo0CB1797d2WXgDmSxWLR27do8j//1d+itmjBhgg4cOKBatWoVrMC7FGHmLrV9+3a5u7urY8eO+fbr06ePjhw5UkRVAVeDiMVisW1ly5ZVx44dtX//fmeXhrvA6dOnNXbsWFWpUkVWq1XBwcHq2rWrNm3aVOjnXrVqlQ4ePKjly5fzifW3iDBzl3r33Xc1duxYbdu2TUlJSXn2K168uPz9/YuwMkDq2LGjTp06pVOnTmnTpk0qVqyYunTp4uyycIdLTExU48aNtXnzZs2ZM0cHDhzQhg0b1KpVK40ePbrQz//oo49q8+bN8vT0LPRz3WkIM3ehjIwMffLJJ3r66afVpUsXLVu2LM++ud1mWrdunZo0aSIvLy+VK1dOPXv2tB3LysrSpEmTVKFCBZUsWVLNmjXTli1bCueB4I5ltVoVEBCggIAANWzYUM8//7ySk5P122+/SZIOHDig1q1bq3jx4ipbtqxGjBih9PT06+aJioqSv7+/SpcurZEjRyorK6uoHwpMZNSoUbJYLPr555/Vq1cv1ahRQ3Xr1lVERIR27Nhh6/f777+rR48eKlGihKpXr65169blO++qVatUt25dWa1WhYSEaO7cuXbH+b15+wgzd6GVK1eqZs2aqlmzpp588kktXbr0pr5iXZLWr1+vnj17qnPnztq7d682bdqkJk2a2I4PHjxYP/zwgz7++GPt379fjz32mDp27KijR48W1sPBHS49PV0ffPCBqlWrprJly+rChQvq2LGjypQpo507d+rTTz/Vt99+qzFjxtiN27Rpk+Li4hQTE6OPPvpIa9asUVRUlJMeBVzdmTNntGHDBo0ePVolS5a87vif/1EXFRWl3r17a//+/erUqZP69eunM2fO5Drv7t271bt3b/Xt21cHDhzQtGnTNHnyZLt/RPJ70wEM3HUefPBBY/78+YZhGMbly5eNcuXKGRs3bjQMwzBiYmIMScbZs2cNwzCMpUuXGj4+PraxzZs3N/r165frvMeOHTMsFotx4sQJu/Y2bdoYkZGRjn8guCMNHDjQcHd3N0qWLGmULFnSkGQEBgYau3fvNgzDMJYsWWKUKVPGSE9Pt41Zv3694ebmZpw+fdo2h5+fn5GRkWHrs2jRIqNUqVJGdnZ20T4gmMJPP/1kSDJWr16dbz9JxksvvWTbT09PNywWi/HVV18ZhnH979AnnnjCaNeund0cEydONOrUqWMYBr83HYUrM3eZw4cP6+eff1bfvn0lScWKFVOfPn307rvv3tT42NhYtWnTJtdje/bskWEYqlGjhkqVKmXbvvvuO8XHxzvsMeDO16pVK8XGxio2NlY//fST2rdvr/DwcB0/flxxcXFq0KCB3b+eW7RooZycHB0+fNjW1qBBA5UoUcK237x5c6Wnpys5OblIHwvMwfjv1embWXhbv35923+XLFlS3t7eSklJybVvXFycWrRoYdfWokULHT16VNnZ2fzedBCX/24mONY777yjK1euqEKFCrY2wzDk4eGhs2fP3nB88eLF8zyWk5Mjd3d37d69W+7u7nbHSpUqVfCicdcpWbKkqlWrZttv3LixfHx89NZbb8kwjDz/4NzMHyLeJYLcVK9eXRaLRXFxcTd8W7+Hh4fdvsViUU5OTq59c3u9Gn+6rc/vTcfgysxd5MqVK1qxYoXmzp1r+1dvbGys9u3bp8qVK+uDDz644Rz169fP8y2KjRo1UnZ2tlJSUlStWjW7LSAgwNEPB3cRi8UiNzc3Xbx4UXXq1FFsbKwyMjJsx3/44Qe5ubmpRo0atrZ9+/bp4sWLtv0dO3aoVKlSqlixYpHWDnPw8/NThw4dtHDhQrvX1jUF/dyYOnXqaNu2bXZt27dvV40aNeTu7s7vTQchzNxFvvjiC509e1ZDhw5VvXr17LZevXrpnXfeueEcU6dO1UcffaSpU6cqLi5OBw4c0Jw5cyRJNWrUUL9+/TRgwACtXr1aCQkJ2rlzp2bPnq0vv/yysB8e7iCZmZk6ffq0Tp8+rbi4OI0dO1bp6enq2rWr+vXrJy8vLw0cOFAHDx5UTEyMxo4dq/79+6t8+fK2ObKysjR06FAdOnRIX331laZOnaoxY8bIzY1fe8jdG2+8oezsbN1///1atWqVjh49qri4OL322mtq3rx5geZ87rnntGnTJs2YMUNHjhzR8uXLtWDBAk2YMEESvzcdxonrdVDEunTpYnTq1CnXY7t37zYkGXPnzs13AbBhGMaqVauMhg0bGp6enka5cuWMnj172o5lZWUZU6ZMMUJCQgwPDw8jICDA6NGjh7F///7Celi4wwwcONCQZNu8vb2Npk2bGp999pmtz/79+41WrVoZXl5ehp+fnzF8+HDj/PnzdnN069bNmDJlilG2bFmjVKlSxrBhw4xLly454yHBRE6ePGmMHj3aqFy5suHp6WlUqFDBeOSRR4yYmBjDMK4uAF6zZo3dGB8fH2Pp0qWGYVy/ANgwDOOzzz4z6tSpY3h4eBiVKlUyXnnlFbvx/N68fRbDuMn35AIAALggrrcCAABTI8wAAABTI8wAAABTI8wAAABTI8wAAABTI8wAAABTI8wAAABTI8wAAABTI8wAMDWLxaK1a9dKkhITE2WxWBQbG+vUmgAULcIMgCK1ZcsWWSyWAn9xX36Cg4N16tQp1atXz6HzhoSEaP78+Q6dE4DjFHN2AQDgKO7u7nzTMHAX4soMAIczDENz5sxRlSpVVLx4cTVo0ECfffaZEhMT1apVK0lSmTJlZLFYNGjQIEm5X/1o2LChpk2bZts/evSoHnroIXl5ealOnTrauHGjXf+/3mbKzs7W0KFDFRoaquLFi6tmzZp69dVX7cYMGjRI3bt31z/+8Q8FBgaqbNmyGj16tC5fvixJCgsL0/HjxzV+/HhZLBZZLBbHPVEAHIIrMwAc7qWXXtLq1au1aNEiVa9eXVu3btWTTz6pr7/+WqtWrdKjjz6qw4cPq3Tp0ipevPhNzZmTk6OePXuqXLly2rFjh9LS0jRu3LgbjqlYsaI++eQTlStXTtu3b9eIESMUGBio3r172/rFxMQoMDBQMTExOnbsmPr06aOGDRtq+PDhWr16tRo0aKARI0Zo+PDht/O0ACgkhBkADpWRkaF58+Zp8+bNat68uSSpSpUq2rZtm958802NGDFCkuTv7y9fX9+bnvfbb79VXFycEhMTVbFiRUnSrFmzFB4enucYDw8PRUVF2fZDQ0O1fft2ffLJJ3ZhpkyZMlqwYIHc3d1Vq1Ytde7cWZs2bdLw4cPl5+cnd3d3eXt7cwsLcFGEGQAOdejQIV26dEnt2rWza8/KylKjRo0KPG9cXJwqVapkCzKSbGEpP4sXL9bbb7+t48eP6+LFi8rKylLDhg3t+tStW1fu7u62/cDAQB04cKDAtQIoWoQZAA6Vk5MjSVq/fr0qVKhgd8xqtSo+Pj7XcW5ubjIMw67t2roVSdcdk3TD9SuffPKJxo8fr7lz56p58+by9vbWK6+8op9++smun4eHx3XzXnscAFwfYQaAQ9WpU0dWq1VJSUl6+OGHrzuenJws6eri3D+75557dOrUKdt+WlqaEhIS7OZNSkrSyZMnFRQUJEn68ccf863l+++/14MPPqhRo0bZ2vIKU/nx9PS8rl4AroN3MwFwKG9vb02YMEHjx4/X8uXLFR8fr71792rhwoVavny5KleuLIvFoi+++EK//fab0tPTJUmtW7fWe++9p++//14HDx7UwIED7W79tG3bVjVr1tSAAQO0b98+ff/993rxxRfzraVatWratWuXvv76ax05ckSTJ0/Wzp07b/kxhYSEaOvWrTpx4oR+//33Wx4PoHARZgA43IwZMzRlyhRFR0erdu3a6tChgz7//HOFhoaqQoUKioqK0gsvvKDy5ctrzJgxkqTIyEg99NBD6tKlizp16qTu3buratWqtjnd3Ny0Zs0aZWZm6v7779ewYcM0c+bMfOt46qmn1LNnT/Xp00fNmjXTH3/8YXeV5mZNnz5diYmJqlq1qu65555bHg+gcFmM3G5EAwAAmARXZgAAgKkRZgAAgKkRZgAAgKkRZgAAgKkRZgAAgKkRZgAAgKkRZgAAgKkRZgAAgKkRZgAAgKkRZgAAgKkRZgAAgKn9fzg7WKdHCht0AAAAAElFTkSuQmCC",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import seaborn as sns\n",
    "\n",
    "sns.barplot(data=notes_long, x=\"etudiant\", y=\"note\", hue=\"matiere\");"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Et `pivot_table` permet de revenir au format large :"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 118,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:19.373350Z",
     "iopub.status.busy": "2026-10-30T13:14:19.373016Z",
     "iopub.status.idle": "2026-10-30T13:14:19.382922Z",
     "shell.execute_reply": "2026-10-30T13:14:19.382406Z"
    }
   },
   "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>matiere</th>\n",
       "      <th>algebre</th>\n",
       "      <th>analyse</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>etudiant</th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>Alice</th>\n",
       "      <td>12.0</td>\n",
       "      <td>14.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Bob</th>\n",
       "      <td>15.0</td>\n",
       "      <td>8.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Chloé</th>\n",
       "      <td>9.0</td>\n",
       "      <td>16.0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "matiere   algebre  analyse\n",
       "etudiant                  \n",
       "Alice        12.0     14.0\n",
       "Bob          15.0      8.0\n",
       "Chloé         9.0     16.0"
      ]
     },
     "execution_count": 118,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "notes_long.pivot_table(index=\"etudiant\", columns=\"matiere\", values=\"note\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Arithmétique et alignement des données : différences avec `Numpy`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Il existe une différence très importante : lorsque vous faites une opération entre deux objets Series, **l’opération se fait terme à terme, mais les termes sont identifiés par leur index.** Si les index ne correspondent pas, le résultat sera un objet Series avec un index qui est l’union des deux index initiaux. Les valeurs correspondant à des index qui n’étaient pas présents dans les deux objets initiaux seront des valeurs manquantes, notées `NaN` (pour Not a Number)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 119,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:19.384832Z",
     "iopub.status.busy": "2026-10-30T13:14:19.384642Z",
     "iopub.status.idle": "2026-10-30T13:14:19.392532Z",
     "shell.execute_reply": "2026-10-30T13:14:19.392036Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "A   -0.405428\n",
       "B   -0.741871\n",
       "C   -0.080453\n",
       "D    0.626063\n",
       "E   -1.865330\n",
       "dtype: float64"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "A    0.286204\n",
       "B    0.812590\n",
       "C    0.220832\n",
       "F   -0.737247\n",
       "dtype: float64"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "A   -0.119224\n",
       "B    0.070718\n",
       "C    0.140379\n",
       "D         NaN\n",
       "E         NaN\n",
       "F         NaN\n",
       "dtype: float64"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "ma_serie4 = pd.Series( np.random.randn(5), index=[\"A\",\"B\",\"C\",\"D\",\"E\"] )\n",
    "display(ma_serie4)\n",
    "ma_serie5 = pd.Series( np.random.randn(4), index=[\"A\",\"B\",\"C\",\"F\"] )\n",
    "display(ma_serie5)\n",
    "ma_serie_somme = ma_serie4 + ma_serie5\n",
    "display(ma_serie_somme)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "On voit ici que les deux Series ont en commun A, B et C. La somme donne bien une valeur qui est la somme des deux objets Series. Pour D, E et F, on obtient une valeur manquante car l’un des deux objets Series ne comprend pas d’index D, E ou F. On a donc la somme d’une valeur manquante et d’une valeur présente qui logiquement donne une valeur manquante. Si vous voulez faire une somme en supposant que les données manquantes sont équivalentes à 0, il faut utiliser : `s1.add(s2, fill_value=0)`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 120,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:19.394311Z",
     "iopub.status.busy": "2026-10-30T13:14:19.394122Z",
     "iopub.status.idle": "2026-10-30T13:14:19.398529Z",
     "shell.execute_reply": "2026-10-30T13:14:19.398183Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "A   -0.119224\n",
       "B    0.070718\n",
       "C    0.140379\n",
       "D    0.626063\n",
       "E   -1.865330\n",
       "F   -0.737247\n",
       "dtype: float64"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "ma_serie_somme_BIS = ma_serie4.add(ma_serie5, fill_value=0)\n",
    "display(ma_serie_somme_BIS)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## L’indexation des DataFrames\n",
    "\n",
    "Vous pouvez avoir besoin de modifier les index de vos données. Pour cela, différentes options s’offrent à vous :"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- `.reindex()` permet de sélectionner des colonnes et de réordonner les colonnes et les lignes. Si une colonne ou une ligne n’est pas présente dans le DataFrame initial, elle sera ajoutée avec des valeurs manquantes."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 121,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:19.400286Z",
     "iopub.status.busy": "2026-10-30T13:14:19.400098Z",
     "iopub.status.idle": "2026-10-30T13:14:19.405804Z",
     "shell.execute_reply": "2026-10-30T13:14:19.405339Z"
    }
   },
   "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>Col 1</th>\n",
       "      <th>Col 2</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>a</th>\n",
       "      <td>1.108168</td>\n",
       "      <td>0.632504</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>b</th>\n",
       "      <td>1.556843</td>\n",
       "      <td>1.267146</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>c</th>\n",
       "      <td>1.032881</td>\n",
       "      <td>-1.404079</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>d</th>\n",
       "      <td>-0.025248</td>\n",
       "      <td>0.105628</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>e</th>\n",
       "      <td>-1.924212</td>\n",
       "      <td>1.302465</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "      Col 1     Col 2\n",
       "a  1.108168  0.632504\n",
       "b  1.556843  1.267146\n",
       "c  1.032881 -1.404079\n",
       "d -0.025248  0.105628\n",
       "e -1.924212  1.302465"
      ]
     },
     "execution_count": 121,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df5 = pd.DataFrame( np.random.randn(5,2), index=[\"a\",\"b\",\"c\",\"d\",\"e\"], columns=[\"Col 1\",\"Col 2\"])\n",
    "df5\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 122,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:19.407472Z",
     "iopub.status.busy": "2026-10-30T13:14:19.407286Z",
     "iopub.status.idle": "2026-10-30T13:14:19.412998Z",
     "shell.execute_reply": "2026-10-30T13:14:19.412657Z"
    }
   },
   "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>Col 2</th>\n",
       "      <th>Col 1</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>e</th>\n",
       "      <td>1.302465</td>\n",
       "      <td>-1.924212</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>c</th>\n",
       "      <td>-1.404079</td>\n",
       "      <td>1.032881</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>d</th>\n",
       "      <td>0.105628</td>\n",
       "      <td>-0.025248</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "      Col 2     Col 1\n",
       "e  1.302465 -1.924212\n",
       "c -1.404079  1.032881\n",
       "d  0.105628 -0.025248"
      ]
     },
     "execution_count": 122,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# on ne garde que les lignes \"c\",\"d\",\"e\" mais on les réordonne\n",
    "# on garde toutes les colonnes mais on les réordonne aussi\n",
    "df6 = df5.reindex(index=[\"e\",\"c\",\"d\"], columns=[\"Col 2\",\"Col 1\"])\n",
    "df6"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- `.rename()` permet de renommer les colonnes ou les lignes."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 123,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:19.414801Z",
     "iopub.status.busy": "2026-10-30T13:14:19.414615Z",
     "iopub.status.idle": "2026-10-30T13:14:19.420570Z",
     "shell.execute_reply": "2026-10-30T13:14:19.420106Z"
    }
   },
   "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>Seconde</th>\n",
       "      <th>Premiere</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>E</th>\n",
       "      <td>1.302465</td>\n",
       "      <td>-1.924212</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>C</th>\n",
       "      <td>-1.404079</td>\n",
       "      <td>1.032881</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>d</th>\n",
       "      <td>0.105628</td>\n",
       "      <td>-0.025248</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "    Seconde  Premiere\n",
       "E  1.302465 -1.924212\n",
       "C -1.404079  1.032881\n",
       "d  0.105628 -0.025248"
      ]
     },
     "execution_count": 123,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df7 = df6.rename(index={\"e\":\"E\", \"c\":\"C\"}, columns={\"Col 1\":\"Premiere\", \"Col 2\":\"Seconde\"})\n",
    "df7"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 124,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:19.422377Z",
     "iopub.status.busy": "2026-10-30T13:14:19.422192Z",
     "iopub.status.idle": "2026-10-30T13:14:19.427792Z",
     "shell.execute_reply": "2026-10-30T13:14:19.427367Z"
    }
   },
   "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>Seconde</th>\n",
       "      <th>Premiere</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>E</th>\n",
       "      <td>1.302465</td>\n",
       "      <td>-1.924212</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>C</th>\n",
       "      <td>-1.404079</td>\n",
       "      <td>1.032881</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>D</th>\n",
       "      <td>0.105628</td>\n",
       "      <td>-0.025248</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "    Seconde  Premiere\n",
       "E  1.302465 -1.924212\n",
       "C -1.404079  1.032881\n",
       "D  0.105628 -0.025248"
      ]
     },
     "execution_count": 124,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df8 = df7.rename(index=str.upper, columns=str.title)\n",
    "df8"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 125,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:19.429403Z",
     "iopub.status.busy": "2026-10-30T13:14:19.429219Z",
     "iopub.status.idle": "2026-10-30T13:14:19.434763Z",
     "shell.execute_reply": "2026-10-30T13:14:19.434297Z"
    }
   },
   "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>SECONDE</th>\n",
       "      <th>PREMIERE</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>E</th>\n",
       "      <td>1.302465</td>\n",
       "      <td>-1.924212</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>C</th>\n",
       "      <td>-1.404079</td>\n",
       "      <td>1.032881</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>D</th>\n",
       "      <td>0.105628</td>\n",
       "      <td>-0.025248</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "    SECONDE  PREMIERE\n",
       "E  1.302465 -1.924212\n",
       "C -1.404079  1.032881\n",
       "D  0.105628 -0.025248"
      ]
     },
     "execution_count": 125,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df9 = df8.rename(mapper=lambda x: x.upper(), axis=1)\n",
    "df9"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Bonus : `set_index` et `reset_index`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div class=\"alert alert-block alert-warning\">\n",
    "\n",
    "**Bonus : hors séance,** mais très utile en pratique : `groupby` produit constamment des index qu'il faut ensuite remettre en colonnes.\n",
    "\n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- `.set_index(\"col\")` promeut une **colonne** au rang d'index ;\n",
    "- `.reset_index()` fait l'inverse : il remet l'index **dans une colonne** et renumérote les lignes."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 126,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:19.436425Z",
     "iopub.status.busy": "2026-10-30T13:14:19.436237Z",
     "iopub.status.idle": "2026-10-30T13:14:19.444724Z",
     "shell.execute_reply": "2026-10-30T13:14:19.444227Z"
    }
   },
   "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>ville</th>\n",
       "      <th>dep</th>\n",
       "      <th>population</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>Toulon</td>\n",
       "      <td>83</td>\n",
       "      <td>176198</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>Nice</td>\n",
       "      <td>06</td>\n",
       "      <td>342669</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>Marseille</td>\n",
       "      <td>13</td>\n",
       "      <td>873076</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "       ville dep  population\n",
       "0     Toulon  83      176198\n",
       "1       Nice  06      342669\n",
       "2  Marseille  13      873076"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "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>dep</th>\n",
       "      <th>population</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>ville</th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>Toulon</th>\n",
       "      <td>83</td>\n",
       "      <td>176198</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Nice</th>\n",
       "      <td>06</td>\n",
       "      <td>342669</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Marseille</th>\n",
       "      <td>13</td>\n",
       "      <td>873076</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "          dep  population\n",
       "ville                    \n",
       "Toulon     83      176198\n",
       "Nice       06      342669\n",
       "Marseille  13      873076"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "villes = pd.DataFrame({ \"ville\"      : [\"Toulon\", \"Nice\", \"Marseille\"],\n",
    "                        \"dep\"        : [\"83\", \"06\", \"13\"],\n",
    "                        \"population\" : [176198, 342669, 873076] })\n",
    "\n",
    "display(villes)\n",
    "display(villes.set_index(\"ville\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Une fois la colonne devenue index, elle est utilisable avec `loc` :"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 127,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:19.446425Z",
     "iopub.status.busy": "2026-10-30T13:14:19.446213Z",
     "iopub.status.idle": "2026-10-30T13:14:19.450891Z",
     "shell.execute_reply": "2026-10-30T13:14:19.450411Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "dep               06\n",
       "population    342669\n",
       "Name: Nice, dtype: object"
      ]
     },
     "execution_count": 127,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "villes_idx = villes.set_index(\"ville\")\n",
    "villes_idx.loc[\"Nice\"]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`reset_index()` est l'opération réciproque. C'est le réflexe à avoir après un `groupby`, dont le résultat a pour index la clé de regroupement :"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 128,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:19.452666Z",
     "iopub.status.busy": "2026-10-30T13:14:19.452476Z",
     "iopub.status.idle": "2026-10-30T13:14:19.459858Z",
     "shell.execute_reply": "2026-10-30T13:14:19.459351Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "dep\n",
       "06    342669\n",
       "13    873076\n",
       "83    176198\n",
       "Name: population, dtype: int64"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "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>dep</th>\n",
       "      <th>population</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>06</td>\n",
       "      <td>342669</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>13</td>\n",
       "      <td>873076</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>83</td>\n",
       "      <td>176198</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "  dep  population\n",
       "0  06      342669\n",
       "1  13      873076\n",
       "2  83      176198"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "resume = villes.groupby(\"dep\")[\"population\"].sum()\n",
    "\n",
    "display(resume)                # une Series indexée par le département\n",
    "display(resume.reset_index())  # un DataFrame à deux colonnes, plus commode à réutiliser"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Enfin, `reset_index(drop=True)` **jette** l'ancien index au lieu d'en faire une colonne : pratique après un filtrage qui a laissé des trous dans la numérotation."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 129,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:19.461857Z",
     "iopub.status.busy": "2026-10-30T13:14:19.461669Z",
     "iopub.status.idle": "2026-10-30T13:14:19.469897Z",
     "shell.execute_reply": "2026-10-30T13:14:19.469403Z"
    }
   },
   "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>ville</th>\n",
       "      <th>dep</th>\n",
       "      <th>population</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>Nice</td>\n",
       "      <td>06</td>\n",
       "      <td>342669</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>Marseille</td>\n",
       "      <td>13</td>\n",
       "      <td>873076</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "       ville dep  population\n",
       "1       Nice  06      342669\n",
       "2  Marseille  13      873076"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "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>ville</th>\n",
       "      <th>dep</th>\n",
       "      <th>population</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>Nice</td>\n",
       "      <td>06</td>\n",
       "      <td>342669</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>Marseille</td>\n",
       "      <td>13</td>\n",
       "      <td>873076</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "       ville dep  population\n",
       "0       Nice  06      342669\n",
       "1  Marseille  13      873076"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "filtre = villes[ villes[\"population\"] > 200_000 ]\n",
    "\n",
    "display(filtre)                         # les index 1 et 2 d'origine ont été conservés\n",
    "display(filtre.reset_index(drop=True))  # renumérotation de 0 à n-1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Copie *vs* vue"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Lorsqu'on crée un objet à partir d'un autre : `df2 = df1`, `df2 = df1[[\"col\"]]`, ou un DataFrame construit sur un tableau NumPy : pandas ne duplique pas systématiquement les données. Tantôt vous obtenez une **copie** indépendante, tantôt une **vue** qui partage la même zone mémoire que l'original : modifier l'une modifie alors l'autre, parfois à votre insu."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div class=\"alert alert-block alert-danger\">\n",
    "\n",
    "**La règle à retenir.** Si vous voulez un objet réellement indépendant de l'original, dites-le explicitement :\n",
    "\n",
    "```python\n",
    "df2 = df1.copy()\n",
    "```\n",
    "\n",
    "</div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 130,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:19.471862Z",
     "iopub.status.busy": "2026-10-30T13:14:19.471673Z",
     "iopub.status.idle": "2026-10-30T13:14:19.479317Z",
     "shell.execute_reply": "2026-10-30T13:14:19.478730Z"
    }
   },
   "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>a</th>\n",
       "      <th>b</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>1</td>\n",
       "      <td>4</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>2</td>\n",
       "      <td>5</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>3</td>\n",
       "      <td>6</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   a  b\n",
       "0  1  4\n",
       "1  2  5\n",
       "2  3  6"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "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>a</th>\n",
       "      <th>b</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>999</td>\n",
       "      <td>4</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>2</td>\n",
       "      <td>5</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>3</td>\n",
       "      <td>6</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "     a  b\n",
       "0  999  4\n",
       "1    2  5\n",
       "2    3  6"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "df_origine = pd.DataFrame({ \"a\" : [1, 2, 3],\n",
    "                            \"b\" : [4, 5, 6] })\n",
    "\n",
    "df_copie = df_origine.copy()\n",
    "df_copie.loc[0, \"a\"] = 999\n",
    "\n",
    "display(df_origine)   # intact\n",
    "display(df_copie)     # seul le second a changé"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Pourquoi ? Ce qui se passe réellement"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Un DataFrame construit à partir d'un tableau NumPy **partage la mémoire** de ce tableau. Modifier le DataFrame modifie donc aussi le tableau d'origine :"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 131,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:19.481228Z",
     "iopub.status.busy": "2026-10-30T13:14:19.480994Z",
     "iopub.status.idle": "2026-10-30T13:14:19.490789Z",
     "shell.execute_reply": "2026-10-30T13:14:19.490326Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Avant modification\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "array([[0, 1],\n",
       "       [2, 3],\n",
       "       [4, 5]])"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "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>0</th>\n",
       "      <th>1</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>2</td>\n",
       "      <td>3</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>4</td>\n",
       "      <td>5</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   0  1\n",
       "0  0  1\n",
       "1  2  3\n",
       "2  4  5"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Après modification d'une valeur du DataFrame\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>0</th>\n",
       "      <th>1</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>2</td>\n",
       "      <td>22</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>4</td>\n",
       "      <td>5</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   0   1\n",
       "0  0   1\n",
       "1  2  22\n",
       "2  4   5"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "array([[ 0,  1],\n",
       "       [ 2, 22],\n",
       "       [ 4,  5]])"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "arr1 = np.arange(6).reshape(3, 2)\n",
    "df1  = pd.DataFrame(arr1)\n",
    "\n",
    "print(\"Avant modification\")\n",
    "display(arr1)\n",
    "display(df1)\n",
    "\n",
    "df1.iloc[1, 1] = 22\n",
    "\n",
    "print(\"Après modification d'une valeur du DataFrame\")\n",
    "display(df1)\n",
    "display(arr1)   # ici, l'array a été modifié lui aussi"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Le même phénomène explique l'avertissement `SettingWithCopyWarning`, que vous rencontrerez tôt ou tard : pandas vous prévient qu'il ne sait pas si l'objet que vous modifiez est une vue ou une copie, et donc si votre modification atteindra l'original ou se perdra."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Ce qui change avec le *Copy-on-Write*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Depuis pandas 2.0, un mode appelé **Copy-on-Write** (CoW) est disponible ; il devient le comportement **par défaut à partir de pandas 3.0**. Sa règle est simple et uniforme : *toute opération se comporte comme si elle renvoyait une copie*. Le partage de mémoire continue d'exister en interne, pour la performance, mais il n'est plus jamais observable : et l'avertissement `SettingWithCopyWarning` disparaît.\n",
    "\n",
    "Activons-le le temps d'une démonstration, avec `pd.option_context` qui restaure automatiquement le réglage à la sortie du bloc :"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 132,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:19.492573Z",
     "iopub.status.busy": "2026-10-30T13:14:19.492383Z",
     "iopub.status.idle": "2026-10-30T13:14:19.499592Z",
     "shell.execute_reply": "2026-10-30T13:14:19.499133Z"
    }
   },
   "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>0</th>\n",
       "      <th>1</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>2</td>\n",
       "      <td>22</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>4</td>\n",
       "      <td>5</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   0   1\n",
       "0  0   1\n",
       "1  2  22\n",
       "2  4   5"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "array([[0, 1],\n",
       "       [2, 3],\n",
       "       [4, 5]])"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/markdown": [
       "Copy-on-Write actif en dehors du bloc ? **False**"
      ],
      "text/plain": [
       "<IPython.core.display.Markdown object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "with pd.option_context(\"mode.copy_on_write\", True):\n",
    "\n",
    "    arr2 = np.arange(6).reshape(3, 2)\n",
    "    df2  = pd.DataFrame(arr2)\n",
    "    df2.iloc[1, 1] = 22\n",
    "\n",
    "    display(df2)    # le DataFrame est bien modifié\n",
    "    display(arr2)   # ici, en revanche, l'array n'est PLUS modifié\n",
    "\n",
    "display(Markdown(f\"Copy-on-Write actif en dehors du bloc ? **{pd.options.mode.copy_on_write}**\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "| | pandas 2.x (comportement actuel) | Copy-on-Write (défaut en pandas 3.0) |\n",
    "|---|---|---|\n",
    "| Modification « à distance » | possible, et silencieuse | impossible |\n",
    "| `SettingWithCopyWarning` | fréquent | n'existe plus |\n",
    "| Rôle de `.copy()` | **indispensable** pour s'isoler | explicite votre intention |\n",
    "\n",
    "Dans les deux cas, la règle du début de section reste la bonne : **si vous voulez une copie, écrivez `.copy()`.**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Combiner des tables"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Jusqu'ici nous avons travaillé sur un seul DataFrame. En pratique, l'information est presque toujours répartie dans **plusieurs tables** qu'il faut rapprocher. Deux situations, très différentes, se présentent.\n",
    "\n",
    "- **Empiler** des tables qui décrivent *les mêmes variables* pour *des individus différents* : les ventes de janvier et celles de février, les étudiants du groupe A et ceux du groupe B. C'est le rôle de `pd.concat`.\n",
    "- **Joindre** des tables qui décrivent *des variables différentes* pour *les mêmes individus*, en s'appuyant sur une colonne commune appelée **clé** : une table des communes et une table des départements, reliées par le code du département. C'est le rôle de `pd.merge`.\n",
    "\n",
    "Retenez la distinction : **empiler ajoute des lignes, joindre ajoute des colonnes.**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Empiler des tables : `pd.concat`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`pd.concat` prend une **liste** de DataFrame et les empile. Par défaut l'empilement est vertical (`axis=0`) : les lignes du second viennent sous celles du premier, et les colonnes sont appariées **par leur nom**."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 133,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:19.501433Z",
     "iopub.status.busy": "2026-10-30T13:14:19.501245Z",
     "iopub.status.idle": "2026-10-30T13:14:19.508964Z",
     "shell.execute_reply": "2026-10-30T13:14:19.508580Z"
    }
   },
   "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>produit</th>\n",
       "      <th>quantite</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>stylo</td>\n",
       "      <td>120</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>cahier</td>\n",
       "      <td>80</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>gomme</td>\n",
       "      <td>45</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "  produit  quantite\n",
       "0   stylo       120\n",
       "1  cahier        80\n",
       "2   gomme        45"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "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>produit</th>\n",
       "      <th>quantite</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>stylo</td>\n",
       "      <td>95</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>cahier</td>\n",
       "      <td>110</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>règle</td>\n",
       "      <td>30</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "  produit  quantite\n",
       "0   stylo        95\n",
       "1  cahier       110\n",
       "2   règle        30"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "ventes_janvier = pd.DataFrame({ \"produit\"  : [\"stylo\", \"cahier\", \"gomme\"],\n",
    "                                \"quantite\" : [120, 80, 45] })\n",
    "\n",
    "ventes_fevrier = pd.DataFrame({ \"produit\"  : [\"stylo\", \"cahier\", \"règle\"],\n",
    "                                \"quantite\" : [95, 110, 30] })\n",
    "\n",
    "display(ventes_janvier, ventes_fevrier)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 134,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:19.510707Z",
     "iopub.status.busy": "2026-10-30T13:14:19.510558Z",
     "iopub.status.idle": "2026-10-30T13:14:19.515696Z",
     "shell.execute_reply": "2026-10-30T13:14:19.515308Z"
    }
   },
   "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>produit</th>\n",
       "      <th>quantite</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>stylo</td>\n",
       "      <td>120</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>cahier</td>\n",
       "      <td>80</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>gomme</td>\n",
       "      <td>45</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>stylo</td>\n",
       "      <td>95</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>cahier</td>\n",
       "      <td>110</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>règle</td>\n",
       "      <td>30</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "  produit  quantite\n",
       "0   stylo       120\n",
       "1  cahier        80\n",
       "2   gomme        45\n",
       "0   stylo        95\n",
       "1  cahier       110\n",
       "2   règle        30"
      ]
     },
     "execution_count": 134,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ventes = pd.concat([ventes_janvier, ventes_fevrier])\n",
    "ventes"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Remarquez les **index dupliqués** : chaque DataFrame a conservé le sien (0, 1, 2 puis 0, 1, 2). C'est rarement souhaitable. Deux solutions :\n",
    "\n",
    "- `ignore_index=True` renumérote les lignes de $0$ à $n-1$ ;\n",
    "- `keys=[...]` conserve la **provenance** de chaque ligne, dans un index à deux niveaux."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 135,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:19.517361Z",
     "iopub.status.busy": "2026-10-30T13:14:19.517211Z",
     "iopub.status.idle": "2026-10-30T13:14:19.522185Z",
     "shell.execute_reply": "2026-10-30T13:14:19.521763Z"
    }
   },
   "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>produit</th>\n",
       "      <th>quantite</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>stylo</td>\n",
       "      <td>120</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>cahier</td>\n",
       "      <td>80</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>gomme</td>\n",
       "      <td>45</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>stylo</td>\n",
       "      <td>95</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>cahier</td>\n",
       "      <td>110</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>règle</td>\n",
       "      <td>30</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "  produit  quantite\n",
       "0   stylo       120\n",
       "1  cahier        80\n",
       "2   gomme        45\n",
       "3   stylo        95\n",
       "4  cahier       110\n",
       "5   règle        30"
      ]
     },
     "execution_count": 135,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pd.concat([ventes_janvier, ventes_fevrier], ignore_index=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 136,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:19.523965Z",
     "iopub.status.busy": "2026-10-30T13:14:19.523814Z",
     "iopub.status.idle": "2026-10-30T13:14:19.529649Z",
     "shell.execute_reply": "2026-10-30T13:14:19.529267Z"
    }
   },
   "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></th>\n",
       "      <th>produit</th>\n",
       "      <th>quantite</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th rowspan=\"3\" valign=\"top\">Janvier</th>\n",
       "      <th>0</th>\n",
       "      <td>stylo</td>\n",
       "      <td>120</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>cahier</td>\n",
       "      <td>80</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>gomme</td>\n",
       "      <td>45</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th rowspan=\"3\" valign=\"top\">Février</th>\n",
       "      <th>0</th>\n",
       "      <td>stylo</td>\n",
       "      <td>95</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>cahier</td>\n",
       "      <td>110</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>règle</td>\n",
       "      <td>30</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "          produit  quantite\n",
       "Janvier 0   stylo       120\n",
       "        1  cahier        80\n",
       "        2   gomme        45\n",
       "Février 0   stylo        95\n",
       "        1  cahier       110\n",
       "        2   règle        30"
      ]
     },
     "execution_count": 136,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ventes_par_mois = pd.concat([ventes_janvier, ventes_fevrier], keys=[\"Janvier\", \"Février\"])\n",
    "ventes_par_mois"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Si les deux tables n'ont pas exactement les mêmes colonnes, `concat` fait l'**union** des colonnes et comble les trous par des `NaN` :"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 137,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:19.531332Z",
     "iopub.status.busy": "2026-10-30T13:14:19.531182Z",
     "iopub.status.idle": "2026-10-30T13:14:19.537784Z",
     "shell.execute_reply": "2026-10-30T13:14:19.537400Z"
    }
   },
   "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>produit</th>\n",
       "      <th>quantite</th>\n",
       "      <th>remise</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>stylo</td>\n",
       "      <td>120</td>\n",
       "      <td>NaN</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>cahier</td>\n",
       "      <td>80</td>\n",
       "      <td>NaN</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>gomme</td>\n",
       "      <td>45</td>\n",
       "      <td>NaN</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>stylo</td>\n",
       "      <td>60</td>\n",
       "      <td>0.1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>trousse</td>\n",
       "      <td>12</td>\n",
       "      <td>0.2</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   produit  quantite  remise\n",
       "0    stylo       120     NaN\n",
       "1   cahier        80     NaN\n",
       "2    gomme        45     NaN\n",
       "3    stylo        60     0.1\n",
       "4  trousse        12     0.2"
      ]
     },
     "execution_count": 137,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ventes_mars = pd.DataFrame({ \"produit\"  : [\"stylo\", \"trousse\"],\n",
    "                             \"quantite\" : [60, 12],\n",
    "                             \"remise\"   : [0.1, 0.2] })   # colonne supplémentaire\n",
    "\n",
    "pd.concat([ventes_janvier, ventes_mars], ignore_index=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Avec `axis=1`, l'empilement devient **horizontal** : les colonnes sont juxtaposées et les lignes appariées **par leur index**, exactement selon la règle d'alignement vue plus haut."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 138,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:19.539484Z",
     "iopub.status.busy": "2026-10-30T13:14:19.539336Z",
     "iopub.status.idle": "2026-10-30T13:14:19.545527Z",
     "shell.execute_reply": "2026-10-30T13:14:19.545152Z"
    }
   },
   "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>prix</th>\n",
       "      <th>stock</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>stylo</th>\n",
       "      <td>1.2</td>\n",
       "      <td>300</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>cahier</th>\n",
       "      <td>2.5</td>\n",
       "      <td>150</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>gomme</th>\n",
       "      <td>0.6</td>\n",
       "      <td>80</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "        prix  stock\n",
       "stylo    1.2    300\n",
       "cahier   2.5    150\n",
       "gomme    0.6     80"
      ]
     },
     "execution_count": 138,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "prix  = pd.DataFrame({ \"prix\"  : [1.2, 2.5, 0.6] }, index=[\"stylo\", \"cahier\", \"gomme\"])\n",
    "stock = pd.DataFrame({ \"stock\" : [300, 150, 80]  }, index=[\"stylo\", \"cahier\", \"gomme\"])\n",
    "\n",
    "pd.concat([prix, stock], axis=1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div class=\"alert alert-block alert-danger\">\n",
    "\n",
    "**Attention :** `pd.concat(..., axis=1)` apparie les lignes **par leur index**, et non par leur ordre d'apparition. Si les index ne coïncident pas, vous obtiendrez des `NaN` sans le moindre message d'erreur.\n",
    "\n",
    "</div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 139,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:19.547317Z",
     "iopub.status.busy": "2026-10-30T13:14:19.547167Z",
     "iopub.status.idle": "2026-10-30T13:14:19.556861Z",
     "shell.execute_reply": "2026-10-30T13:14:19.556474Z"
    }
   },
   "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>prix</th>\n",
       "      <th>stock</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>stylo</th>\n",
       "      <td>1.2</td>\n",
       "      <td>300</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>cahier</th>\n",
       "      <td>2.5</td>\n",
       "      <td>150</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>gomme</th>\n",
       "      <td>0.6</td>\n",
       "      <td>80</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "        prix  stock\n",
       "stylo    1.2    300\n",
       "cahier   2.5    150\n",
       "gomme    0.6     80"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "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>prix</th>\n",
       "      <th>stock</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>stylo</th>\n",
       "      <td>1.2</td>\n",
       "      <td>300.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>cahier</th>\n",
       "      <td>2.5</td>\n",
       "      <td>NaN</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>gomme</th>\n",
       "      <td>0.6</td>\n",
       "      <td>NaN</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>règle</th>\n",
       "      <td>NaN</td>\n",
       "      <td>150.0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "        prix  stock\n",
       "stylo    1.2  300.0\n",
       "cahier   2.5    NaN\n",
       "gomme    0.6    NaN\n",
       "règle    NaN  150.0"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "stock_desordre = pd.DataFrame({ \"stock\" : [80, 300, 150] }, index=[\"gomme\", \"stylo\", \"cahier\"])\n",
    "\n",
    "# L'ordre des lignes diffère, mais l'appariement se fait sur l'index : le résultat est correct\n",
    "display(pd.concat([prix, stock_desordre], axis=1))\n",
    "\n",
    "stock_partiel = pd.DataFrame({ \"stock\" : [300, 150] }, index=[\"stylo\", \"règle\"])\n",
    "\n",
    "# Ici \"cahier\" et \"gomme\" n'ont pas de stock, \"règle\" n'a pas de prix : des NaN apparaissent\n",
    "display(pd.concat([prix, stock_partiel], axis=1))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Joindre deux tables : `pd.merge`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Changeons de situation. Nous disposons maintenant de deux tables qui décrivent **des choses différentes** :\n",
    "\n",
    "- une table des **communes**, avec leur population et le code de leur département ;\n",
    "- une table des **départements**, qui donne le nom du département et sa région.\n",
    "\n",
    "La colonne `dep` est présente dans les deux : c'est la **clé** de la jointure."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 140,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:19.558671Z",
     "iopub.status.busy": "2026-10-30T13:14:19.558521Z",
     "iopub.status.idle": "2026-10-30T13:14:19.567373Z",
     "shell.execute_reply": "2026-10-30T13:14:19.566956Z"
    }
   },
   "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>commune</th>\n",
       "      <th>dep</th>\n",
       "      <th>population</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>Toulon</td>\n",
       "      <td>83</td>\n",
       "      <td>176198</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>Hyères</td>\n",
       "      <td>83</td>\n",
       "      <td>57635</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>Nice</td>\n",
       "      <td>06</td>\n",
       "      <td>342669</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>Marseille</td>\n",
       "      <td>13</td>\n",
       "      <td>873076</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>Lyon</td>\n",
       "      <td>69</td>\n",
       "      <td>522250</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "     commune dep  population\n",
       "0     Toulon  83      176198\n",
       "1     Hyères  83       57635\n",
       "2       Nice  06      342669\n",
       "3  Marseille  13      873076\n",
       "4       Lyon  69      522250"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "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>dep</th>\n",
       "      <th>nom_departement</th>\n",
       "      <th>region</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>06</td>\n",
       "      <td>Alpes-Maritimes</td>\n",
       "      <td>PACA</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>13</td>\n",
       "      <td>Bouches-du-Rhône</td>\n",
       "      <td>PACA</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>69</td>\n",
       "      <td>Rhône</td>\n",
       "      <td>Auvergne-Rhône-Alpes</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>83</td>\n",
       "      <td>Var</td>\n",
       "      <td>PACA</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "  dep   nom_departement                region\n",
       "0  06   Alpes-Maritimes                  PACA\n",
       "1  13  Bouches-du-Rhône                  PACA\n",
       "2  69             Rhône  Auvergne-Rhône-Alpes\n",
       "3  83               Var                  PACA"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "communes = pd.DataFrame({\n",
    "    \"commune\"    : [\"Toulon\", \"Hyères\", \"Nice\", \"Marseille\", \"Lyon\"],\n",
    "    \"dep\"        : [\"83\", \"83\", \"06\", \"13\", \"69\"],\n",
    "    \"population\" : [176198, 57635, 342669, 873076, 522250] })\n",
    "\n",
    "departements = pd.DataFrame({\n",
    "    \"dep\"             : [\"06\", \"13\", \"69\", \"83\"],\n",
    "    \"nom_departement\" : [\"Alpes-Maritimes\", \"Bouches-du-Rhône\", \"Rhône\", \"Var\"],\n",
    "    \"region\"          : [\"PACA\", \"PACA\", \"Auvergne-Rhône-Alpes\", \"PACA\"] })\n",
    "\n",
    "display(communes, departements)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 141,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:19.569327Z",
     "iopub.status.busy": "2026-10-30T13:14:19.569124Z",
     "iopub.status.idle": "2026-10-30T13:14:19.576797Z",
     "shell.execute_reply": "2026-10-30T13:14:19.576418Z"
    }
   },
   "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>commune</th>\n",
       "      <th>dep</th>\n",
       "      <th>population</th>\n",
       "      <th>nom_departement</th>\n",
       "      <th>region</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>Toulon</td>\n",
       "      <td>83</td>\n",
       "      <td>176198</td>\n",
       "      <td>Var</td>\n",
       "      <td>PACA</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>Hyères</td>\n",
       "      <td>83</td>\n",
       "      <td>57635</td>\n",
       "      <td>Var</td>\n",
       "      <td>PACA</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>Nice</td>\n",
       "      <td>06</td>\n",
       "      <td>342669</td>\n",
       "      <td>Alpes-Maritimes</td>\n",
       "      <td>PACA</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>Marseille</td>\n",
       "      <td>13</td>\n",
       "      <td>873076</td>\n",
       "      <td>Bouches-du-Rhône</td>\n",
       "      <td>PACA</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>Lyon</td>\n",
       "      <td>69</td>\n",
       "      <td>522250</td>\n",
       "      <td>Rhône</td>\n",
       "      <td>Auvergne-Rhône-Alpes</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "     commune dep  population   nom_departement                region\n",
       "0     Toulon  83      176198               Var                  PACA\n",
       "1     Hyères  83       57635               Var                  PACA\n",
       "2       Nice  06      342669   Alpes-Maritimes                  PACA\n",
       "3  Marseille  13      873076  Bouches-du-Rhône                  PACA\n",
       "4       Lyon  69      522250             Rhône  Auvergne-Rhône-Alpes"
      ]
     },
     "execution_count": 141,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pd.merge(communes, departements, on=\"dep\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Que s'est-il passé ? Pour **chaque ligne** de la table de gauche, pandas a cherché dans la table de droite les lignes ayant la **même valeur de clé**, puis a recollé les colonnes. La table des départements comptait 4 lignes, celle des communes 5 ; le résultat en compte 5, car plusieurs communes partagent le même département.\n",
    "\n",
    "On peut écrire indifféremment `pd.merge(communes, departements, on=\"dep\")` ou `communes.merge(departements, on=\"dep\")`."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Les quatre types de jointure : l'argument `how`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Que faire des lignes qui **n'ont aucune correspondance** dans l'autre table ? C'est tout l'objet de l'argument `how` :\n",
    "\n",
    "| `how` | Lignes conservées | Équivalent SQL |\n",
    "|---|---|---|\n",
    "| `\"inner\"` *(défaut)* | seulement les clés présentes **dans les deux** tables | `INNER JOIN` |\n",
    "| `\"left\"`  | **toutes** celles de la table de gauche | `LEFT JOIN` |\n",
    "| `\"right\"` | **toutes** celles de la table de droite | `RIGHT JOIN` |\n",
    "| `\"outer\"` | **toutes**, des deux côtés | `FULL OUTER JOIN` |\n",
    "\n",
    "Les colonnes sans correspondance sont remplies par des `NaN`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 142,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:19.578668Z",
     "iopub.status.busy": "2026-10-30T13:14:19.578472Z",
     "iopub.status.idle": "2026-10-30T13:14:19.587604Z",
     "shell.execute_reply": "2026-10-30T13:14:19.587227Z"
    }
   },
   "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>commune</th>\n",
       "      <th>dep</th>\n",
       "      <th>population</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>Toulon</td>\n",
       "      <td>83</td>\n",
       "      <td>176198</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>Hyères</td>\n",
       "      <td>83</td>\n",
       "      <td>57635</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>Nice</td>\n",
       "      <td>06</td>\n",
       "      <td>342669</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>Marseille</td>\n",
       "      <td>13</td>\n",
       "      <td>873076</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>Lyon</td>\n",
       "      <td>69</td>\n",
       "      <td>522250</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>Ajaccio</td>\n",
       "      <td>2A</td>\n",
       "      <td>71361</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "     commune dep  population\n",
       "0     Toulon  83      176198\n",
       "1     Hyères  83       57635\n",
       "2       Nice  06      342669\n",
       "3  Marseille  13      873076\n",
       "4       Lyon  69      522250\n",
       "5    Ajaccio  2A       71361"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "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>dep</th>\n",
       "      <th>nom_departement</th>\n",
       "      <th>region</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>06</td>\n",
       "      <td>Alpes-Maritimes</td>\n",
       "      <td>PACA</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>13</td>\n",
       "      <td>Bouches-du-Rhône</td>\n",
       "      <td>PACA</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>69</td>\n",
       "      <td>Rhône</td>\n",
       "      <td>Auvergne-Rhône-Alpes</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>83</td>\n",
       "      <td>Var</td>\n",
       "      <td>PACA</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>75</td>\n",
       "      <td>Paris</td>\n",
       "      <td>Île-de-France</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "  dep   nom_departement                region\n",
       "0  06   Alpes-Maritimes                  PACA\n",
       "1  13  Bouches-du-Rhône                  PACA\n",
       "2  69             Rhône  Auvergne-Rhône-Alpes\n",
       "3  83               Var                  PACA\n",
       "4  75             Paris         Île-de-France"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# On ajoute une commune dont le département est absent de la table des départements (Ajaccio, 2A),\n",
    "# et un département dans lequel aucune commune n'est listée (Paris, 75).\n",
    "\n",
    "communes2 = pd.concat(\n",
    "    [communes, pd.DataFrame({\"commune\":[\"Ajaccio\"], \"dep\":[\"2A\"], \"population\":[71361]})],\n",
    "    ignore_index=True)\n",
    "\n",
    "departements2 = pd.concat(\n",
    "    [departements, pd.DataFrame({\"dep\":[\"75\"], \"nom_departement\":[\"Paris\"], \"region\":[\"Île-de-France\"]})],\n",
    "    ignore_index=True)\n",
    "\n",
    "display(communes2, departements2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 143,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:19.589303Z",
     "iopub.status.busy": "2026-10-30T13:14:19.589152Z",
     "iopub.status.idle": "2026-10-30T13:14:19.614665Z",
     "shell.execute_reply": "2026-10-30T13:14:19.614275Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/markdown": [
       "**`how = \"inner\"`**"
      ],
      "text/plain": [
       "<IPython.core.display.Markdown object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "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>commune</th>\n",
       "      <th>dep</th>\n",
       "      <th>population</th>\n",
       "      <th>nom_departement</th>\n",
       "      <th>region</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>Toulon</td>\n",
       "      <td>83</td>\n",
       "      <td>176198</td>\n",
       "      <td>Var</td>\n",
       "      <td>PACA</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>Hyères</td>\n",
       "      <td>83</td>\n",
       "      <td>57635</td>\n",
       "      <td>Var</td>\n",
       "      <td>PACA</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>Nice</td>\n",
       "      <td>06</td>\n",
       "      <td>342669</td>\n",
       "      <td>Alpes-Maritimes</td>\n",
       "      <td>PACA</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>Marseille</td>\n",
       "      <td>13</td>\n",
       "      <td>873076</td>\n",
       "      <td>Bouches-du-Rhône</td>\n",
       "      <td>PACA</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>Lyon</td>\n",
       "      <td>69</td>\n",
       "      <td>522250</td>\n",
       "      <td>Rhône</td>\n",
       "      <td>Auvergne-Rhône-Alpes</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "     commune dep  population   nom_departement                region\n",
       "0     Toulon  83      176198               Var                  PACA\n",
       "1     Hyères  83       57635               Var                  PACA\n",
       "2       Nice  06      342669   Alpes-Maritimes                  PACA\n",
       "3  Marseille  13      873076  Bouches-du-Rhône                  PACA\n",
       "4       Lyon  69      522250             Rhône  Auvergne-Rhône-Alpes"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/markdown": [
       "**`how = \"left\"`**"
      ],
      "text/plain": [
       "<IPython.core.display.Markdown object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "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>commune</th>\n",
       "      <th>dep</th>\n",
       "      <th>population</th>\n",
       "      <th>nom_departement</th>\n",
       "      <th>region</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>Toulon</td>\n",
       "      <td>83</td>\n",
       "      <td>176198</td>\n",
       "      <td>Var</td>\n",
       "      <td>PACA</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>Hyères</td>\n",
       "      <td>83</td>\n",
       "      <td>57635</td>\n",
       "      <td>Var</td>\n",
       "      <td>PACA</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>Nice</td>\n",
       "      <td>06</td>\n",
       "      <td>342669</td>\n",
       "      <td>Alpes-Maritimes</td>\n",
       "      <td>PACA</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>Marseille</td>\n",
       "      <td>13</td>\n",
       "      <td>873076</td>\n",
       "      <td>Bouches-du-Rhône</td>\n",
       "      <td>PACA</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>Lyon</td>\n",
       "      <td>69</td>\n",
       "      <td>522250</td>\n",
       "      <td>Rhône</td>\n",
       "      <td>Auvergne-Rhône-Alpes</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>Ajaccio</td>\n",
       "      <td>2A</td>\n",
       "      <td>71361</td>\n",
       "      <td>NaN</td>\n",
       "      <td>NaN</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "     commune dep  population   nom_departement                region\n",
       "0     Toulon  83      176198               Var                  PACA\n",
       "1     Hyères  83       57635               Var                  PACA\n",
       "2       Nice  06      342669   Alpes-Maritimes                  PACA\n",
       "3  Marseille  13      873076  Bouches-du-Rhône                  PACA\n",
       "4       Lyon  69      522250             Rhône  Auvergne-Rhône-Alpes\n",
       "5    Ajaccio  2A       71361               NaN                   NaN"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/markdown": [
       "**`how = \"right\"`**"
      ],
      "text/plain": [
       "<IPython.core.display.Markdown object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "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>commune</th>\n",
       "      <th>dep</th>\n",
       "      <th>population</th>\n",
       "      <th>nom_departement</th>\n",
       "      <th>region</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>Nice</td>\n",
       "      <td>06</td>\n",
       "      <td>342669.0</td>\n",
       "      <td>Alpes-Maritimes</td>\n",
       "      <td>PACA</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>Marseille</td>\n",
       "      <td>13</td>\n",
       "      <td>873076.0</td>\n",
       "      <td>Bouches-du-Rhône</td>\n",
       "      <td>PACA</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>Lyon</td>\n",
       "      <td>69</td>\n",
       "      <td>522250.0</td>\n",
       "      <td>Rhône</td>\n",
       "      <td>Auvergne-Rhône-Alpes</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>Toulon</td>\n",
       "      <td>83</td>\n",
       "      <td>176198.0</td>\n",
       "      <td>Var</td>\n",
       "      <td>PACA</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>Hyères</td>\n",
       "      <td>83</td>\n",
       "      <td>57635.0</td>\n",
       "      <td>Var</td>\n",
       "      <td>PACA</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>NaN</td>\n",
       "      <td>75</td>\n",
       "      <td>NaN</td>\n",
       "      <td>Paris</td>\n",
       "      <td>Île-de-France</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "     commune dep  population   nom_departement                region\n",
       "0       Nice  06    342669.0   Alpes-Maritimes                  PACA\n",
       "1  Marseille  13    873076.0  Bouches-du-Rhône                  PACA\n",
       "2       Lyon  69    522250.0             Rhône  Auvergne-Rhône-Alpes\n",
       "3     Toulon  83    176198.0               Var                  PACA\n",
       "4     Hyères  83     57635.0               Var                  PACA\n",
       "5        NaN  75         NaN             Paris         Île-de-France"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/markdown": [
       "**`how = \"outer\"`**"
      ],
      "text/plain": [
       "<IPython.core.display.Markdown object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "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>commune</th>\n",
       "      <th>dep</th>\n",
       "      <th>population</th>\n",
       "      <th>nom_departement</th>\n",
       "      <th>region</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>Toulon</td>\n",
       "      <td>83</td>\n",
       "      <td>176198.0</td>\n",
       "      <td>Var</td>\n",
       "      <td>PACA</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>Hyères</td>\n",
       "      <td>83</td>\n",
       "      <td>57635.0</td>\n",
       "      <td>Var</td>\n",
       "      <td>PACA</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>Nice</td>\n",
       "      <td>06</td>\n",
       "      <td>342669.0</td>\n",
       "      <td>Alpes-Maritimes</td>\n",
       "      <td>PACA</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>Marseille</td>\n",
       "      <td>13</td>\n",
       "      <td>873076.0</td>\n",
       "      <td>Bouches-du-Rhône</td>\n",
       "      <td>PACA</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>Lyon</td>\n",
       "      <td>69</td>\n",
       "      <td>522250.0</td>\n",
       "      <td>Rhône</td>\n",
       "      <td>Auvergne-Rhône-Alpes</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>Ajaccio</td>\n",
       "      <td>2A</td>\n",
       "      <td>71361.0</td>\n",
       "      <td>NaN</td>\n",
       "      <td>NaN</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>6</th>\n",
       "      <td>NaN</td>\n",
       "      <td>75</td>\n",
       "      <td>NaN</td>\n",
       "      <td>Paris</td>\n",
       "      <td>Île-de-France</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "     commune dep  population   nom_departement                region\n",
       "0     Toulon  83    176198.0               Var                  PACA\n",
       "1     Hyères  83     57635.0               Var                  PACA\n",
       "2       Nice  06    342669.0   Alpes-Maritimes                  PACA\n",
       "3  Marseille  13    873076.0  Bouches-du-Rhône                  PACA\n",
       "4       Lyon  69    522250.0             Rhône  Auvergne-Rhône-Alpes\n",
       "5    Ajaccio  2A     71361.0               NaN                   NaN\n",
       "6        NaN  75         NaN             Paris         Île-de-France"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "for methode in [\"inner\", \"left\", \"right\", \"outer\"]:\n",
    "    display(Markdown(f\"**`how = \\\"{methode}\\\"`**\"))\n",
    "    display(pd.merge(communes2, departements2, on=\"dep\", how=methode))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Comparez attentivement ces quatre tableaux :\n",
    "\n",
    "- `inner` **supprime silencieusement** Ajaccio (département inconnu) *et* Paris (aucune commune) ;\n",
    "- `left` conserve Ajaccio, avec `NaN` pour le nom du département et la région ;\n",
    "- `right` conserve Paris, avec `NaN` pour la commune et la population ;\n",
    "- `outer` conserve les deux.\n",
    "\n",
    "Le choix n'est pas anodin : `inner`, la valeur **par défaut**, est précisément celle qui perd des données sans prévenir."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Quand les colonnes clés n'ont pas le même nom"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Les deux tables ne nomment pas toujours la clé de la même façon. On précise alors `left_on` et `right_on` :"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 144,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:19.616419Z",
     "iopub.status.busy": "2026-10-30T13:14:19.616264Z",
     "iopub.status.idle": "2026-10-30T13:14:19.623723Z",
     "shell.execute_reply": "2026-10-30T13:14:19.623301Z"
    }
   },
   "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>commune</th>\n",
       "      <th>dep</th>\n",
       "      <th>population</th>\n",
       "      <th>code_departement</th>\n",
       "      <th>nom_departement</th>\n",
       "      <th>region</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>Toulon</td>\n",
       "      <td>83</td>\n",
       "      <td>176198</td>\n",
       "      <td>83</td>\n",
       "      <td>Var</td>\n",
       "      <td>PACA</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>Hyères</td>\n",
       "      <td>83</td>\n",
       "      <td>57635</td>\n",
       "      <td>83</td>\n",
       "      <td>Var</td>\n",
       "      <td>PACA</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>Nice</td>\n",
       "      <td>06</td>\n",
       "      <td>342669</td>\n",
       "      <td>06</td>\n",
       "      <td>Alpes-Maritimes</td>\n",
       "      <td>PACA</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>Marseille</td>\n",
       "      <td>13</td>\n",
       "      <td>873076</td>\n",
       "      <td>13</td>\n",
       "      <td>Bouches-du-Rhône</td>\n",
       "      <td>PACA</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>Lyon</td>\n",
       "      <td>69</td>\n",
       "      <td>522250</td>\n",
       "      <td>69</td>\n",
       "      <td>Rhône</td>\n",
       "      <td>Auvergne-Rhône-Alpes</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "     commune dep  population code_departement   nom_departement  \\\n",
       "0     Toulon  83      176198               83               Var   \n",
       "1     Hyères  83       57635               83               Var   \n",
       "2       Nice  06      342669               06   Alpes-Maritimes   \n",
       "3  Marseille  13      873076               13  Bouches-du-Rhône   \n",
       "4       Lyon  69      522250               69             Rhône   \n",
       "\n",
       "                 region  \n",
       "0                  PACA  \n",
       "1                  PACA  \n",
       "2                  PACA  \n",
       "3                  PACA  \n",
       "4  Auvergne-Rhône-Alpes  "
      ]
     },
     "execution_count": 144,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "departements_bis = departements.rename(columns={\"dep\": \"code_departement\"})\n",
    "\n",
    "pd.merge(communes, departements_bis,\n",
    "         left_on=\"dep\", right_on=\"code_departement\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "La clé apparaît alors en double (`dep` et `code_departement`) ; on supprime la colonne redondante :"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 145,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:19.625383Z",
     "iopub.status.busy": "2026-10-30T13:14:19.625230Z",
     "iopub.status.idle": "2026-10-30T13:14:19.632482Z",
     "shell.execute_reply": "2026-10-30T13:14:19.632087Z"
    }
   },
   "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>commune</th>\n",
       "      <th>dep</th>\n",
       "      <th>population</th>\n",
       "      <th>nom_departement</th>\n",
       "      <th>region</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>Toulon</td>\n",
       "      <td>83</td>\n",
       "      <td>176198</td>\n",
       "      <td>Var</td>\n",
       "      <td>PACA</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>Hyères</td>\n",
       "      <td>83</td>\n",
       "      <td>57635</td>\n",
       "      <td>Var</td>\n",
       "      <td>PACA</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>Nice</td>\n",
       "      <td>06</td>\n",
       "      <td>342669</td>\n",
       "      <td>Alpes-Maritimes</td>\n",
       "      <td>PACA</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>Marseille</td>\n",
       "      <td>13</td>\n",
       "      <td>873076</td>\n",
       "      <td>Bouches-du-Rhône</td>\n",
       "      <td>PACA</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>Lyon</td>\n",
       "      <td>69</td>\n",
       "      <td>522250</td>\n",
       "      <td>Rhône</td>\n",
       "      <td>Auvergne-Rhône-Alpes</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "     commune dep  population   nom_departement                region\n",
       "0     Toulon  83      176198               Var                  PACA\n",
       "1     Hyères  83       57635               Var                  PACA\n",
       "2       Nice  06      342669   Alpes-Maritimes                  PACA\n",
       "3  Marseille  13      873076  Bouches-du-Rhône                  PACA\n",
       "4       Lyon  69      522250             Rhône  Auvergne-Rhône-Alpes"
      ]
     },
     "execution_count": 145,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pd.merge(communes, departements_bis,\n",
    "         left_on=\"dep\", right_on=\"code_departement\").drop(columns=\"code_departement\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Vérifier une jointure"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Une jointure **réussit toujours** : y compris quand elle ne fait pas ce que vous croyez. Deux réflexes :\n",
    "\n",
    "1. comparer le **nombre de lignes** avant et après ;\n",
    "2. utiliser `indicator=True`, qui ajoute une colonne `_merge` indiquant l'origine de chaque ligne."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 146,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:19.634213Z",
     "iopub.status.busy": "2026-10-30T13:14:19.634018Z",
     "iopub.status.idle": "2026-10-30T13:14:19.648781Z",
     "shell.execute_reply": "2026-10-30T13:14:19.648246Z"
    }
   },
   "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>commune</th>\n",
       "      <th>dep</th>\n",
       "      <th>population</th>\n",
       "      <th>nom_departement</th>\n",
       "      <th>region</th>\n",
       "      <th>_merge</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>Toulon</td>\n",
       "      <td>83</td>\n",
       "      <td>176198.0</td>\n",
       "      <td>Var</td>\n",
       "      <td>PACA</td>\n",
       "      <td>both</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>Hyères</td>\n",
       "      <td>83</td>\n",
       "      <td>57635.0</td>\n",
       "      <td>Var</td>\n",
       "      <td>PACA</td>\n",
       "      <td>both</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>Nice</td>\n",
       "      <td>06</td>\n",
       "      <td>342669.0</td>\n",
       "      <td>Alpes-Maritimes</td>\n",
       "      <td>PACA</td>\n",
       "      <td>both</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>Marseille</td>\n",
       "      <td>13</td>\n",
       "      <td>873076.0</td>\n",
       "      <td>Bouches-du-Rhône</td>\n",
       "      <td>PACA</td>\n",
       "      <td>both</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>Lyon</td>\n",
       "      <td>69</td>\n",
       "      <td>522250.0</td>\n",
       "      <td>Rhône</td>\n",
       "      <td>Auvergne-Rhône-Alpes</td>\n",
       "      <td>both</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>Ajaccio</td>\n",
       "      <td>2A</td>\n",
       "      <td>71361.0</td>\n",
       "      <td>NaN</td>\n",
       "      <td>NaN</td>\n",
       "      <td>left_only</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>6</th>\n",
       "      <td>NaN</td>\n",
       "      <td>75</td>\n",
       "      <td>NaN</td>\n",
       "      <td>Paris</td>\n",
       "      <td>Île-de-France</td>\n",
       "      <td>right_only</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "     commune dep  population   nom_departement                region  \\\n",
       "0     Toulon  83    176198.0               Var                  PACA   \n",
       "1     Hyères  83     57635.0               Var                  PACA   \n",
       "2       Nice  06    342669.0   Alpes-Maritimes                  PACA   \n",
       "3  Marseille  13    873076.0  Bouches-du-Rhône                  PACA   \n",
       "4       Lyon  69    522250.0             Rhône  Auvergne-Rhône-Alpes   \n",
       "5    Ajaccio  2A     71361.0               NaN                   NaN   \n",
       "6        NaN  75         NaN             Paris         Île-de-France   \n",
       "\n",
       "       _merge  \n",
       "0        both  \n",
       "1        both  \n",
       "2        both  \n",
       "3        both  \n",
       "4        both  \n",
       "5   left_only  \n",
       "6  right_only  "
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "_merge\n",
       "both          5\n",
       "left_only     1\n",
       "right_only    1\n",
       "Name: count, dtype: int64"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "resultat = pd.merge(communes2, departements2, on=\"dep\", how=\"outer\", indicator=True)\n",
    "\n",
    "display(resultat)\n",
    "display(resultat[\"_merge\"].value_counts())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div class=\"alert alert-block alert-danger\">\n",
    "\n",
    "**Les deux pièges classiques de `merge`**\n",
    "\n",
    "1. **Des lignes disparaissent.** Avec `how=\"inner\"` (le défaut), toute clé absente d'un côté fait disparaître la ligne, sans message. Vérifiez systématiquement le nombre de lignes avant et après.\n",
    "2. **Des lignes se dupliquent.** Si la clé n'est **pas unique** dans la table de droite, chaque ligne de gauche est répétée autant de fois qu'il y a de correspondances : et tous les totaux calculés ensuite sont faux.\n",
    "\n",
    "</div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 147,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:19.650590Z",
     "iopub.status.busy": "2026-10-30T13:14:19.650434Z",
     "iopub.status.idle": "2026-10-30T13:14:19.660584Z",
     "shell.execute_reply": "2026-10-30T13:14:19.660205Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/markdown": [
       "communes          : 5 lignes"
      ],
      "text/plain": [
       "<IPython.core.display.Markdown object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/markdown": [
       "après la jointure : **7** lignes, car Toulon et Hyères ont été dupliquées"
      ],
      "text/plain": [
       "<IPython.core.display.Markdown object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "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>commune</th>\n",
       "      <th>dep</th>\n",
       "      <th>population</th>\n",
       "      <th>nom_departement</th>\n",
       "      <th>region</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>Toulon</td>\n",
       "      <td>83</td>\n",
       "      <td>176198</td>\n",
       "      <td>Var</td>\n",
       "      <td>PACA</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>Toulon</td>\n",
       "      <td>83</td>\n",
       "      <td>176198</td>\n",
       "      <td>Var (doublon)</td>\n",
       "      <td>PACA</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>Hyères</td>\n",
       "      <td>83</td>\n",
       "      <td>57635</td>\n",
       "      <td>Var</td>\n",
       "      <td>PACA</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>Hyères</td>\n",
       "      <td>83</td>\n",
       "      <td>57635</td>\n",
       "      <td>Var (doublon)</td>\n",
       "      <td>PACA</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>Nice</td>\n",
       "      <td>06</td>\n",
       "      <td>342669</td>\n",
       "      <td>Alpes-Maritimes</td>\n",
       "      <td>PACA</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>Marseille</td>\n",
       "      <td>13</td>\n",
       "      <td>873076</td>\n",
       "      <td>Bouches-du-Rhône</td>\n",
       "      <td>PACA</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>6</th>\n",
       "      <td>Lyon</td>\n",
       "      <td>69</td>\n",
       "      <td>522250</td>\n",
       "      <td>Rhône</td>\n",
       "      <td>Auvergne-Rhône-Alpes</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "     commune dep  population   nom_departement                region\n",
       "0     Toulon  83      176198               Var                  PACA\n",
       "1     Toulon  83      176198     Var (doublon)                  PACA\n",
       "2     Hyères  83       57635               Var                  PACA\n",
       "3     Hyères  83       57635     Var (doublon)                  PACA\n",
       "4       Nice  06      342669   Alpes-Maritimes                  PACA\n",
       "5  Marseille  13      873076  Bouches-du-Rhône                  PACA\n",
       "6       Lyon  69      522250             Rhône  Auvergne-Rhône-Alpes"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# La clé \"83\" apparaît deux fois à droite : la table des départements est mal construite\n",
    "departements_doublon = pd.concat(\n",
    "    [departements, pd.DataFrame({\"dep\":[\"83\"], \"nom_departement\":[\"Var (doublon)\"], \"region\":[\"PACA\"]})],\n",
    "    ignore_index=True)\n",
    "\n",
    "jointure = pd.merge(communes, departements_doublon, on=\"dep\")\n",
    "\n",
    "display(Markdown(f\"communes          : {len(communes)} lignes\"))\n",
    "display(Markdown(f\"après la jointure : **{len(jointure)}** lignes, \"\n",
    "                 \"car Toulon et Hyères ont été dupliquées\"))\n",
    "display(jointure)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "L'argument `validate` permet de **déclarer la relation attendue**. Pandas lève alors une erreur si l'hypothèse est fausse, au lieu de produire silencieusement un résultat faux :"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 148,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:19.662325Z",
     "iopub.status.busy": "2026-10-30T13:14:19.662174Z",
     "iopub.status.idle": "2026-10-30T13:14:19.667237Z",
     "shell.execute_reply": "2026-10-30T13:14:19.666798Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/markdown": [
       "MergeError : Merge keys are not unique in right dataset; not a many-to-one merge"
      ],
      "text/plain": [
       "<IPython.core.display.Markdown object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# On déclare : plusieurs communes pour un même département, donc \"many_to_one\"\n",
    "try:\n",
    "    pd.merge(communes, departements_doublon, on=\"dep\", validate=\"many_to_one\")\n",
    "except Exception as e:\n",
    "    display(Markdown(f\"{type(e).__name__} : {e}\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### `join` : la variante qui travaille sur l'index"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "La méthode `.join()` fait la même chose que `merge`, mais en utilisant par défaut l'**index** des deux tables plutôt qu'une colonne. C'est commode lorsque la clé est déjà l'index :"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 149,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-30T13:14:19.668981Z",
     "iopub.status.busy": "2026-10-30T13:14:19.668829Z",
     "iopub.status.idle": "2026-10-30T13:14:19.676662Z",
     "shell.execute_reply": "2026-10-30T13:14:19.676284Z"
    }
   },
   "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>commune</th>\n",
       "      <th>population</th>\n",
       "      <th>nom_departement</th>\n",
       "      <th>region</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>dep</th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>06</th>\n",
       "      <td>Nice</td>\n",
       "      <td>342669</td>\n",
       "      <td>Alpes-Maritimes</td>\n",
       "      <td>PACA</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>13</th>\n",
       "      <td>Marseille</td>\n",
       "      <td>873076</td>\n",
       "      <td>Bouches-du-Rhône</td>\n",
       "      <td>PACA</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>69</th>\n",
       "      <td>Lyon</td>\n",
       "      <td>522250</td>\n",
       "      <td>Rhône</td>\n",
       "      <td>Auvergne-Rhône-Alpes</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>83</th>\n",
       "      <td>Toulon</td>\n",
       "      <td>176198</td>\n",
       "      <td>Var</td>\n",
       "      <td>PACA</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>83</th>\n",
       "      <td>Hyères</td>\n",
       "      <td>57635</td>\n",
       "      <td>Var</td>\n",
       "      <td>PACA</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "       commune  population   nom_departement                region\n",
       "dep                                                               \n",
       "06        Nice      342669   Alpes-Maritimes                  PACA\n",
       "13   Marseille      873076  Bouches-du-Rhône                  PACA\n",
       "69        Lyon      522250             Rhône  Auvergne-Rhône-Alpes\n",
       "83      Toulon      176198               Var                  PACA\n",
       "83      Hyères       57635               Var                  PACA"
      ]
     },
     "execution_count": 149,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "communes_idx     = communes.set_index(\"dep\")\n",
    "departements_idx = departements.set_index(\"dep\")\n",
    "\n",
    "communes_idx.join(departements_idx)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### En résumé\n",
    "\n",
    "| Objectif | Outil |\n",
    "|---|---|\n",
    "| Ajouter des **lignes** (mêmes colonnes) | `pd.concat([df1, df2])` |\n",
    "| Ajouter des **colonnes**, appariées par l'index | `pd.concat([df1, df2], axis=1)` |\n",
    "| Ajouter des **colonnes**, appariées par une clé commune | `pd.merge(df1, df2, on=\"cle\", how=...)` |\n",
    "| Ajouter des **colonnes**, appariées par l'index | `df1.join(df2)` |\n",
    "\n",
    "Et le réflexe à garder : **après chaque jointure, vérifiez le nombre de lignes.**"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "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.12.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
