Files
training.python.datascience/documentation/old-01-c-pandas-actions.md
Steve Kossouho d06fd5414d Rename documentation files
Removed new- prefix.
Added old- prefix to old files.
2025-07-12 17:03:38 +02:00

818 lines
28 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
title: Manipuler des données avec Pandas
author: Steve Kossouho
---
# Analyse de données fichier avec Pandas
---
## Les `Index` dans une `Series`
Un objet de type `Series` est un jeu de données à une dimension. Ce qui n'empêche pas d'associer
un index à chacune des lignes du document. Un des intérêts est de pouvoir associer une étiquette
à chacune des données, par exemple un moment dans le temps. Cela permet d'obtenir une série temporelle
en bonne et dûe forme.
```{.python .numberLines}
import pandas as pd
s0 = pd.Series(data=[2, 5, 2, 6], index=["ligne1", "ligne2", "ligne3", "ligne4"])
s1 = pd.Series(data=[2, 5, 2, 6], index=pd.date_range("2023-01-01", "2023-01-31", 4))
s2 = pd.Series(data=[2, 5, 2, 6], index=["2022-01-01", "2022-01-02", "2022-01-03", "2022-01-04"])
s3 = pd.Series(data=[2, 5, 2, 6]) # Créer une série sans index
s3 = s3.set_axis(["ligne1", "ligne2", "ligne3", "ligne4"]) # Utilise ces valeurs comme index. Renvoie une nouvelle série.
print(s1, s2)
print(s0, s3)
```
---
### Petit aparté : utilisation de `date_range()`
La fonction de Pandas nommée `date_range()` permet de générer une suite de dates, qu'on pourrait
utiliser pour générer des colonnes d'un `DataFrame` ou des index à associer aux lignes d'un `DataFrame` ou une `Series`.
La fonction permet de générer des dates à intervalles réguliers, mais peut faire beaucoup plus intéressant :
```{.python .numberLines}
import pandas as pd
# Génère une liste de 8 dates, contenant la date de début et de fin, ainsi que 6 autres dates
# uniformément dispersés (avec les heures, minutes, secondes et **nanosecondes**)
print(pd.date_range("2023-01-01", "2023-01-31", periods=8)
```
Un problème potentiel de cette méthode, c'est justement qu'on n'obtient pas toujours des dates exactes à minuit.
Cela peut s'arranger, en utilisant la fonction avec un autre argument.
TODO: Reformuler ce truc
---
Si on utilise la même fonction, mais en utilisant l'argument `freq` au lieu de l'argument `periods` (on ne peut
pas utiliser les deux en même temps), on peut demander à Pandas de générer une suite de dates, répondant à certains critères, documentés
[ici](https://pandas.pydata.org/docs/user_guide/timeseries.html#offset-aliases)
```{.python .numberLines}
import pandas as pd
# Génère une liste de dates, contenant la date de début et de fin, une par jour du calendrier
print(pd.date_range("2023-01-01", "2023-01-31", freq="D")) # D signifie chaque jour de la semaine
print(pd.date_range("2023-01-01", "2023-01-31", freq="W-SUN")) # W-SUN signifie chaque dimanche
print(pd.date_range("2023-01-01", "2023-01-31", freq="W-MON")) # W-MON signifie chaque lundi
print(pd.date_range("2023-01-01", "2023-01-31", freq="W-TUE")) # W-MON signifie chaque mardi
print(pd.date_range("2023-01-01", "2023-01-02", freq="H")) # H signifie chaque heure
```
---
## Les données absentes (`Not a Number`)
Dans un objet `Series` ou un objet `DataFrame`, toutes les cellules n'ont pas forcément de données.
Prenons un exemple au hasard : une table contenant des informations d'identité, et qui contient des colonnes telles que `nom`, `prenom`, et `nom de naissance`, peut présenter des lignes de contenu où la colonne `nom de naissance` ne sera pas renseignée.
Lorsque c'est le cas, Pandas représente le contenu de la cellule comme étant `NaN` (not a number), une donnée qui en Python standard serait la valeur `None`{.python}.
Dans de nombreux calculs fournis par Pandas, il est souvent facile d'exclure les cellules `NaN` des
résultats, voire de retirer des informations selon l'état de remplissage de cellules.
---
## Gestion des données manquantes (NaN)
Pandas offre plusieurs méthodes pour gérer les données manquantes, identifiées par la valeur `NaN` (Not a Number) :
- `isna()` ou `isnull()` : ces deux méthodes retournent un masque booléen indiquant les entrées manquantes.
- `notna()` ou `notnull()` : ces méthodes fonctionnent comme les précédentes, mais renvoient `True` pour les entrées non manquantes.
- `dropna()` : cette méthode permet de supprimer les lignes (ou colonnes, selon l'axe choisi) contenant des entrées manquantes.
- `fillna()` : cette méthode permet de remplir les entrées manquantes avec une valeur spécifiée.
---
Exemple d'utilisation :
```{.python .numberLines}
import pandas as pd
import numpy as np
# Création d'un DataFrame avec des valeurs manquantes
df = pd.DataFrame({
"A": [1, 2, np.nan],
"B": [5, np.nan, np.nan],
"C": [1, 2, 3]
})
print(df)
```
---
```{.python .numberLines}
# Utilisation de dropna() pour supprimer les lignes avec des valeurs NaN
df_drop = df.dropna()
# On peut aussi choisir quelles colonnes considérer pour la vérification des valeurs NaN
# Supprimer les lignes entières où on a NaN dans la colonne "A" uniquement
df_drop2 = df.dropna(subset=["A"])
print(df_drop)
print(df_drop2)
```
---
Pour remplacer des valeurs `NaN` dans un `DataFrame`, on a plusieurs possibilités :
```{.python .numberLines}
# Utilisation de fillna() pour remplir les valeurs manquantes
# Ici, on remplace TOUTES les valeurs NaN du DataFrame par une valeur choisie
df_fill = df.fillna(value='FILL VALUE')
# Si on souhaite choisir la valeur par défaut par colonne
# Ici, on indique qu'il faut remplacer les valeurs NaN de la colonne "A"
# par 0, et les valeurs NaN de la colonne "B" par -1
df_fill2 = df.fillna(value={"A": 0, "B": -1})
print(df_fill)
print(df_fill2)
```
---
Pour avoir une carte des valeurs `NaN` ou équivalentes dans un `DataFrame`, on a les méthodes `isna()` :
```{.python .numberLines}
# Utilisation de isna() pour récupérer un DataFrame de valeurs booléennes.
# On aura un `True` pour les valeurs `None`, `NaN`, `NaT` etc.
df_empty_map = df.isna()
print(df_empty_map)
```
---
## Gestion des index
Nous avons déjà vu comment utiliser des index avec un `DataFrame`. Il existe plusieurs méthodes utiles pour gérer ces index :
- `reset_index()` : Permet de réinitialiser l'index à un index par défaut (0, 1, 2, ...).
- `set_index()` : Utiliser une colonne différente pour l'index.
Exemple d'utilisation :
```{.python .numberLines}
import pandas as pd
# Création d'un DataFrame avec un index
df = pd.DataFrame({"A": [1, 2, 3], "B": [4, 5, 6]}, index=["x", "y", "z"])
print(df)
```
---
```{.python .numberLines}
# Réinitialisation de l'index
df = ...
# df.reset_index() renvoie un nouveau DataFrame avec l'index mis à jour.
df_reset = df.reset_index()
# df.reset_index(inplace=True) modifie le DataFrame en modifiant son index.
df.reset_index(inplace=True)
print(df_reset)
# Utilisation d'une autre colonne pour l'index
# Les valeurs de la colonne "A" seront utilisées comme index pour référencer les lignes
df_set = df.set_index("A")
print(df_set)
```
---
## Filtrage et modifications d'un dataframe
Pandas offre une multitude de méthodes pour filtrer et modifier un `DataFrame`. Par exemple, il est possible de sélectionner des colonnes ou des lignes spécifiques, de filtrer selon certaines conditions, ou encore de modifier des valeurs.
---
Exemple d'utilisation :
```{.python .numberLines}
import pandas as pd
# Création d'un DataFrame
df = pd.DataFrame({"A": [1, 2, 3, 4], "B": [5, 6, 7, 8], "C": ["a", "b", "c", "d"]})
# Sélection d'une colonne
print(df["A"])
# Sélection de plusieurs colonnes
print(df[["A", "B"]])
# Filtrage selon une condition
# On peut filtrer avec des conditions sans passer par df.iloc
# Mais ce sera plus explicite d'utiliser df.iloc
print(df.loc[df["A"] > 2]) # Récupérer les lignes où A > 2
print(df.loc[(df["A"] > 2) & (df["B"] == 8)]) # Récupérer les lignes où A > 2 et B = 8
print(df.loc[~(df["A"] > 2) & (df["B"] == 8)]) # Récupérer les lignes où A n'est pas > 2 et B = 8
# Modification de valeurs :
# Changer les lignes de df où la colonne "A" contient une
# valeur supérieure à 2, et y mettre la valeur 10 dans les colonnes B et C
df.loc[df["A"] > 2, ["B", "C"]] = 10
# Si je veux mettre deux valeurs différentes dans B et C lorsque la condition
# est vérifiée, j'écrirai plutôt
df.loc[df["A"] > 2, ["B", "C"]] = (9, 12)
print(df)
```
---
### Tri des lignes d'un `DataFrame`
Il est fréquent de vouloir réorganiser les données d'un `DataFrame` via un tri sur une ou plusieurs colonnes.
Le type propose naturellement une méthode nommée `.sort_values()`
---
## Slicing et lignes de `DataFrame`
Nous avons vu qu'il est possible de filtrer un `DataFrame` via des critères avancés. De façon plus générale, le filtrage de lignes peut se faire avec deux attributs :
- `df.iloc` : extraire des lignes individuelles par leur numéro séquentiel dans le dataframe;
- `df.loc` : extraire des lignes individuelles par la valeur assignée comme index aux lignes du document.
Les deux méthodes s'utilisent généralement de la même manière, en faisant attention à utiliser des alias d'index dans le
cas de `df.loc`.
---
### Extraire une ligne d'un `DataFrame`
Au même titre que dans une liste Python, il est très simple d'extraire une ligne d'un objet `DataFrame` :
```python
import pandas as pd
df = pd.DataFrame({"A": [1, 2, 3, 4], "B": [5, 6, 7, 8], "C": ["a", "b", "c", "d"]}, index=["l1", "l2", "l3", "l4"])
row_a = df.loc["l1"] # extraire la première ligne
row_b = df.iloc[0] # extraire la première ligne
print(row_a, row_b)
```
---
### Extraire plusieurs lignes distinctes d'un `DataFrame`
Impossible avec des types standard en Python, il est très simple avec Pandas d'extraire des lignes distinctes d'un objet `DataFrame` :
```python
import pandas as pd
df = pd.DataFrame({"A": [1, 2, 3, 4], "B": [5, 6, 7, 8], "C": ["a", "b", "c", "d"]}, index=["l1", "l2", "l3", "l4"])
rows_a = df.iloc[[0, 2]] # extraire les 1e et 3e lignes
print(rows_a)
```
---
### Filtrer les lignes d'un `DataFrame` avec des booléens
On peut filtrer les lignes d'un objet `DataFrame` en passant une liste de booléens. Pour chaque booléen valant `True`, la ligne
correspondante sera conservée dans la sortie :
```python
import pandas as pd
df = pd.DataFrame({"A": [1, 2, 3, 4], "B": [5, 6, 7, 8], "C": ["a", "b", "c", "d"]}, index=["l1", "l2", "l3", "l4"])
rows_a = df.iloc[[False, True, True, False]] # extraire les 2e et 3e lignes
print(rows_a)
```
Cette possibilité de filtrer via une liste de booléens, associée au fonctionnement des opérateurs sur les `Series`,
permet des choses très intéressantes pour facilement filtrer des `DataFrame`
---
#### Filtrer avec des conditions
Utiliser des opérateurs de comparaison sur une colonne de données renverra toujours une série de même longueur, contenant
des booléens indiquant si la comparaison était vraie pour chaque valeur.
```python
import pandas as pd
df = pd.DataFrame({
"A": [1, -2, 3, -4],
"B": [5, 6, 7, 8],
"C": ["ant", "ball", "chord", "double"]
}, index=["l1", "l2", "l3", "l4"])
# Masque de valeurs positives
positive_a = df["A"] >= 0
# L'opposé d'une condition utilise l'opérateur
# de complément binaire, le ~
exclude_cond = ~df["A"] == 0
# Masque de valeurs dans une plage
range_a = df["A"].between(0, 100, inclusive="both")
```
---
Les séries de booléens résultantes peuvent naturellement assemblées entre elles via des opérations logiques, booléennes :
```python
import pandas as pd
df = pd.DataFrame({"A": [1, -2, 3, -4], "B": [5, 6, 7, 8], "C": ["ant", "ball", "chord", "double"]}, index=["l1", "l2", "l3", "l4"])
multi_condition = (df["A"] >= 0) & (df["B"] > 6) # lignes où A est positif et B est > 6
print(multi_condition)
```
---
#### Conditions sur des chaînes de caractères
Il est facile d'utiliser des opérateurs classiques pour tester des conditions sur des
colonnes d'un objet `DataFrame`. Par exemple :
```python
filtered_df = df[df["column"] > 1]
```
Si l'on souhaite filtrer sur une colonne textuelle, on peut être intéressé
de pouvoir considérer une partie d'une chaîne, ou bien ignorer la casse du texte.
Pour cela nous avons sur les objets `Series` un attribut `.str` :
```python
filtered_df = df[df["column"].str.contains("(?i)texte")]
```
La méthode comprend les expressions régulières pour plus de contrôle sur les valeurs à tester.
---
#### Conditions avec des fonctions
Il peut arriver qu'une condition de test à appliquer à une colonne nécessite autre chose que simplement le contenu
brut de la colonne; par exemple, le nombre de caractères d'une colonne textuelle pourrait être utilisé pour
discriminer les résultats. Dans ce cas de figure, on utilisera plutôt la méthode `apply` de la série :
```python
import pandas as pd
df = pd.DataFrame(
{"A": [1, -2, 3, -4], "B": [5, 6, 7, 8], "C": ["ant", "ball", "chord", "double"]},
index=["l1", "l2", "l3", "l4"]
)
# Dire si une ligne de C a plus de 4 caractères
# La fonction renvoie une série de valeurs booléennes
long_c = df["C"].apply(lambda val: len(val) > 4)
print(long_c)
print(df.loc[long_c])
```
---
### Slicing sur les lignes
La syntaxe normale du slicing fonctionne sur les lignes d'un `DataFrame`, ce qui vous permet de récupérer simplement
une partition des lignes du `DataFrame` original.
```python
import pandas as pd
df = pd.DataFrame(
{"A": [1, -2, 3, -4], "B": [5, 6, 7, 8], "C": ["ant", "ball", "chord", "double"]},
index=["l1", "l2", "l3", "l4"]
)
partition = df.iloc[0:2]
print(partition)
```
---
### Plus : Multiple slicing sur les lignes
La syntaxe normale du slicing fonctionne sur les lignes d'un `DataFrame`, ce qui vous permet de récupérer simplement
une partition des lignes du `DataFrame` original. Un outil de `numpy` nous permet d'appliquer un slicing multiple sur les
lignes d'un `DataFrame` :
```python
import pandas as pd
import numpy as np
df = pd.DataFrame(
{"A": [1, -2, 3, -4], "B": [5, 6, 7, 8], "C": ["ant", "ball", "chord", "double"]},
index=["l1", "l2", "l3", "l4"]
)
partition = df.iloc[np.r_[0:1, 2:4]] # extraire les lignes 0 et 2 à 3 avec du slice
print(partition)
```
---
## Grouper les contenus d'un DataFrame pour calculs
Pandas propose des méthodes pour grouper les données selon certains critères et effectuer des calculs sur ces groupes.
C'est ce qu'on appelle souvent l'opération de `group by` (groupement).
Cela est particulièrement utile pour l'analyse de données.
Exemple d'utilisation :
```{.python .numberLines}
import pandas as pd
import numpy as np
# Création d'un DataFrame
df = pd.DataFrame({
"A": ["foo", "bar", "foo", "bar", "foo", "bar", "foo", "foo"],
"B": ["one", "one", "two", "three", "two", "two", "one", "three"],
"C": np.random.randn(8),
"D": np.random.randn(8)
})
# Grouper par colonne "A" et calculer la somme sur chaque groupe
grouped = df.groupby("A").sum()
print(grouped)
```
---
## Fusion de dataframes
Pandas propose plusieurs méthodes pour combiner des `DataFrame`, comme `concat()`, `merge()`, ou `join()`. Ces opérations sont similaires à celles que l'on peut réaliser avec des bases de données SQL.
Exemple d'utilisation :
```{.python .numberLines}
import pandas as pd
# Création de deux DataFrame
df1 = pd.DataFrame({
"A": ["A0", "A1", "A2", "A3"],
"B": ["B0", "B1", "B2", "B3"],
"C": ["C0", "C1", "C2", "C3"],
"D": ["D0", "D1", "D2", "D3"]
})
df2 = pd.DataFrame({
"A": ["A4", "A5", "A6", "A7"],
"B": ["B4", "B5", "B6", "B7"],
"C": ["C4", "C5", "C6", "C7"],
"D": ["D4", "D5", "D6", "D7"]
})
# Concaténation des deux DataFrame
result = pd.concat([df1, df2])
print(result)
```
---
## Manipulation des formats de dates
Pandas offre une multitude de méthodes pour manipuler les dates.
Vous pouvez convertir des chaînes de caractères en dates, extraire des composants spécifiques d'une date (comme l'année, le mois, le jour, etc.), ou encore effectuer des calculs avec des dates.
Exemple d'utilisation :
```{.python .numberLines}
import pandas as pd
# Création d'un DataFrame avec une colonne date
df = pd.DataFrame({
"date": ["2023-01-01", "2023-01-02", "2023-01-03"],
"value": [1, 2, 3]
})
# Afficher les types des colonnes pour s'assurer que la colonne de date a le bon type
print(df.dtypes) # Regardez le résultat pour être sûr
# Si le type est incorrect, faire la conversion de la colonne vers le type datetime
df["date"] = pd.to_datetime(df["date"])
# Extraction de l'année, du mois, et du jour dans 3 nouvelles colonnes
df["year"] = df["date"].dt.year
df["month"] = df["date"].dt.month
df["day"] = df["date"].dt.day
print(df)
```
---
## Mesures statistiques variées sur les DataFrames
Pandas propose plusieurs méthodes pour calculer des statistiques sur un `DataFrame`, comme la moyenne (`mean()`), la somme (`sum()`), l'écart-type (`std()`), le minimum (`min()`), le maximum (`max()`), et bien d'autres.
Exemple d'utilisation :
```{.python .numberLines}
import pandas as pd
# Création d'un DataFrame
df = pd.DataFrame({
"A": [1, 2, 3, 4, 5],
"B": [6, 7, 8, 9, 10]
})
# Calcul de la moyenne, de la somme, et de l'écart-type sur chaque colonne
mean = df.mean() # Ceci est une `Series` avec une valeur calculée par colonne
sum = df.sum() # Ceci aussi est une `Series`
std = df.std() # Pareil
print(mean)
print(sum)
print(std)
```
---
### Comment ajouter une `Series` comme nouvelle ligne d'un `DataFrame`
Cela peut être intéressant d'ajouter à un `DataFrame` une ligne qui provient d'un objet `Series` (par exemple, les résultats de moyennes, sommes ou écarts-types etc.) Pour cela, c'est assez simple, il faut se servir de l'attribut `.loc` d'un `DataFrame` :
```{.python .numberLines}
import pandas as pd
# Création d'un DataFrame
df = pd.DataFrame({"A": [1, 2, 3, 4, 5], "B": [6, 7, 8, 9, 10]})
# Calcul de la moyenne, de la somme, et de l'écart-type sur chaque colonne
mean = df.mean() # Ceci est une `Series` avec une valeur calculée par colonne
# Ajouter la `Series` dans `mean` comme nouvelle ligne dans `df` dont l'index est "moyenne"
df.loc["moyenne"] = mean
df.loc["hasard"] = [99, 25] # On peut même définir des lignes avec des valeurs dans une liste
print(df) # Une nouvelle ligne "moyenne" devrait être présente dans le DataFrame
```
---
### Noms des méthodes les plus fréquemment utilisées
Pour faire des calculs statistiques sur les colonnes d'un `DataFrame`, les méthodes disponibles de
base dans Pandas ont des noms pas toujours faciles à deviner :
- `df.mean()` : Calcule la moyenne des colonnes
- `df.sum()` : Calcule la somme des colonnes
- `df.std()` : Calcule l'écart-type (racine de la variance)
- `df.var()` : Calcule la variance
- `df.median()` : Valeur centrale des éléments d'une série
- `df.quantile(q)` : Calcule un quantile (q entre 0.0 et 1.0)
- `df.mode()` : Renvoie un DataFrame des valeurs modales de chaque colonne
- `df.max()` et `df.min()` : Calculer les extrêmes
- `df.cum*()` : Fonctions cumulatives qui renvoient des DataFrame
---
#### Mode d'un `DataFrame`
Le mode d'un `DataFrame` renvoie un `DataFrame` avec toutes les colonnes, et autant de lignes qu'il y a de valeurs modales par colonne. (_Une valeur modale est la valeur qui revient le plus souvent dans une série_)
Si j'ai un `DataFrame` avec une colonne `A` et une colonne `B`, et que `A` possède une seule valeur modale qui est `10`, et que `B` possède 3 valeurs modales qui sont `"pomme"`, `"citron"`, et `"ananas"`, le résultat du mode du `DataFrame` ressemblera à ça :
```
A B
10 pomme
NaN citron
NaN ananas
```
Les colonnes où il y a moins de valeurs modales sont remplies avec des `NaN`.
---
## Faire des liaisons entre plusieurs `DataFrame`
Des fois, on est obligé de récupérer des informations différentes depuis plusieurs `DataFrame` qui ont des données en commun. Par exemple :
- Une table d'assurés sociaux : `numero_secu`, `prenom`, `nom`, `naissance`...
- Une table de remboursements médicaments : `num_secu`, `montant`, `date`, `categorie`...
Si on veut travailler sur le `DataFrame` des remboursements, en y ajoutant les informations de prénom, nom et date de naissance pour chaque remboursement, on peut faire une fusion avec Pandas.
```{.python .numberLines}
import pandas as pd
# Imaginez que ces deux fichiers factices correspondent à nos deux Dataframes
df_assures = pd.read_csv("assures.csv")
df_remboursements = pd.read_csv("remboursements.csv")
# On veut partir du DataFrame des remboursements pour y ajouter
# des colonnes prises depuis la liste des assurés.
resultat = df_remboursements.merge(df_assures, how="left", on_left="num_secu", on_right="numero_secu")
print(resultat)
```
---
Le résultat qui est affiché contient toutes les colonnes des assurés et des remboursements, et contient autant de lignes que dans la table de "gauche", à savoir `df_remboursements`.
Les colonnes sont `numero_secu`, `prenom`, `nom`, `naissance`, `num_secu`, `montant`, `date`, `categorie`.
Les colonnes `num_secu` et `numero_secu` auront les memes valeurs, car c'est justement sur ces colonnes qu'on a fait la fusion. Attention cependant !
---
### Stratégie de fusion de `DataFrame`
La méthode `.merge()` accepte un argument `how=` qui permet de préciser comment le résultat de la fusion doit etre généré. Les valeurs possibles de cet argument sont : `"left"`, `"right"`, `"inner"`, `"outer"`.
Si je possède les `DataFrame` suivants :
---
**Personnes**
```
id prenom nom naissance
0 1 Jacques Dupont 1959
1 2 Paul Biya 1947
2 3 Marie Curie 1861
3 5 Jules César 0
```
---
**Transactions**
```
id_transaction id_client montant
0 1 1 100
1 2 1 500
2 3 2 -90
3 4 2 200
4 5 1 30
5 6 3 15
6 7 2 -60
7 8 1 -10
8 9 1 250
9 10 2 170
10 11 3 90
15 16 4 77
```
---
#### Stratégie `left`
Si j'utilise la stratégie `"left"`, alors toutes les lignes du `DataFrame` de gauche (`df_remboursements`) seront conservées, et on y ajoutera les colonnes du `DataFrame` de droite, en y choisissant comme données celles de la ligne
où la colonne `"numero_secu"` a la meme valeur que dans la colonne `"num_secu"`.
Si il n'existe pas de ligne dans le `DataFrame` de droite où la colonne `"numero_secu"` a une valeur qui correspond, les
colonnes ajoutées contiendront `NaN`. En gros : on conservera tous les enregistrements de **gauche**, en ajoutant des `NaN` à droite s'il n'y a aucune correspondance trouvée.
---
Résultat :
```
id_transaction id_client montant id prenom nom naissance
0 1 1 100 1.0 Jacques Dupont 1959.0
1 2 1 500 1.0 Jacques Dupont 1959.0
2 3 2 -90 2.0 Paul Biya 1947.0
3 4 2 200 2.0 Paul Biya 1947.0
4 5 1 30 1.0 Jacques Dupont 1959.0
5 6 3 15 3.0 Marie Curie 1861.0
6 7 2 -60 2.0 Paul Biya 1947.0
7 8 1 -10 1.0 Jacques Dupont 1959.0
8 9 1 250 1.0 Jacques Dupont 1959.0
9 10 2 170 2.0 Paul Biya 1947.0
10 16 4 77 NaN NaN NaN NaN
```
---
#### Stratégie `right`
Si j'utilise la stratégie `"right"`, alors toutes les lignes du `DataFrame` de droite (`df_assures`)
seront conservées, et on y ajoutera les colonnes du `DataFrame` de gauche, en y choisissant comme données celles de la ligne
où la colonne `"num_secu"` a la meme valeur que dans la colonne `"num_secu"`. Si plusieurs lignes correspondent, on ajoutera autant de lignes que nécessaire.
Si il n'existe pas de ligne dans le `DataFrame` de gauche où la colonne `"num_secu"` a une valeur qui correspond, les colonnes ajoutées contiendront `NaN`.
---
Résultat :
```
id_transaction id_client montant id prenom nom naissance
0 1.0 1.0 100.0 1 Jacques Dupont 1959
1 2.0 1.0 500.0 1 Jacques Dupont 1959
2 5.0 1.0 30.0 1 Jacques Dupont 1959
3 8.0 1.0 -10.0 1 Jacques Dupont 1959
4 9.0 1.0 250.0 1 Jacques Dupont 1959
5 3.0 2.0 -90.0 2 Paul Biya 1947
6 4.0 2.0 200.0 2 Paul Biya 1947
7 7.0 2.0 -60.0 2 Paul Biya 1947
8 10.0 2.0 170.0 2 Paul Biya 1947
9 6.0 3.0 15.0 3 Marie Curie 1861
10 NaN NaN NaN 5 Jules César 0
```
---
#### Stratégie `inner`
Avec la stratégie `inner` on ne garde que les lignes où les colonnes utilisées pour faire la fusion ont une correspondance
de l'autre coté de de la fusion. En gros, on n'ajoute jamais dans le résultat de ligne ou on aurait des colonnes avec `NaN`.
---
Résultat :
```
id_transaction id_client montant id prenom nom naissance
0 1 1 100 1 Jacques Dupont 1959
1 2 1 500 1 Jacques Dupont 1959
2 5 1 30 1 Jacques Dupont 1959
3 8 1 -10 1 Jacques Dupont 1959
4 9 1 250 1 Jacques Dupont 1959
5 3 2 -90 2 Paul Biya 1947
6 4 2 200 2 Paul Biya 1947
7 7 2 -60 2 Paul Biya 1947
8 10 2 170 2 Paul Biya 1947
9 6 3 15 3 Marie Curie 1861
```
---
#### Stratégie `outer`
Avec la stratégie `outer` on garde toutes lignes et si les colonnes utilisées pour faire la fusion n'ont pas de correspondance
de l'autre coté de la fusion, on ajoute simplement des colonnes avec `NaN`..
---
Résultat :
```
id_transaction id_client montant id prenom nom naissance
0 1.0 1.0 100.0 1.0 Jacques Dupont 1959.0
1 2.0 1.0 500.0 1.0 Jacques Dupont 1959.0
2 5.0 1.0 30.0 1.0 Jacques Dupont 1959.0
3 8.0 1.0 -10.0 1.0 Jacques Dupont 1959.0
4 9.0 1.0 250.0 1.0 Jacques Dupont 1959.0
5 3.0 2.0 -90.0 2.0 Paul Biya 1947.0
6 4.0 2.0 200.0 2.0 Paul Biya 1947.0
7 7.0 2.0 -60.0 2.0 Paul Biya 1947.0
8 10.0 2.0 170.0 2.0 Paul Biya 1947.0
9 6.0 3.0 15.0 3.0 Marie Curie 1861.0
10 16.0 4.0 77.0 NaN NaN NaN NaN
11 NaN NaN NaN 5.0 Jules César 0.0
```
---
### Appliquer un Pivot
La méthode `pivot` des `DataFrame` permet de récupérer une représentation d'un `DataFrame`, réorganisé autour de certaines colonnes/index.
Par exemple, si nous avons les données suivantes :
```{.python .numberLines}
import pandas as pd
df = pd.DataFrame({
'country': ['fra', 'fra', 'fra', 'fra', 'bel', 'bel', 'bel', "bel"],
'district': ['north', 'east', 'west', 'south', 'north', 'east', 'west', 'south'],
'population': [10_000, 30_000, 50_000, 70_000, 20_000, 40_000, 60_000, 80_000],
'extra': ['h', 'i', 'j', 'k', 'l', 'm', 'n', 'o']
})
```
---
Si nous appliquons le pivot suivant sur ces données :
```{.python .numberLines}
df2 = df.pivot(index='district', columns='country', values='population')
```
Nous obtiendrons les données suivantes :
```
country bel fra
district
east 40000 30000
north 20000 10000
south 80000 70000
west 60000 50000
```
---
L'argument `values` est facultatif. S'il est omis, alors le `DataFrame` résultant
contiendra des colonnes de type `MultiIndex`, un index dont les entrées sont composées de `tuples`.
Ici, au lieu d'avoir deux colonnes de pays pour la colonne `population`, nous aurons deux colonnes de pays pour la colonne `population`, deux colonnes de pays pour la colonne `extra`, et ainsi de suite pour toutes les colonnes non utilisées en tant qu'index ou colonnes.
Les noms de ces colonnes seront :
```
("population", "bel")
("population", "fra")
("extra", "bel")
("extra", "fra")
```