--- 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") ```