Manipulez vos données avec pandas

Cet article est le numéro 3 de 3 articles de la série Introduction à la Data Science avec Python

L’une des choses les plus importantes en data science ; c’est de pouvoir explorer, transformer, visualiser, comprendre vos données afin d’en retirer le maximum d’informations.  C’est donc le rôle de pandas une bibliothèque python permettant l’analyse et la manipulation de données.

Pandas est donc un puissant outil d’analyse et de manipulation de données (open source) facile à utiliser, écrit en python.

Dans cet article, nous présenterons les structures de données de bases dans pandas et montrerons comment les utiliser afin de manipuler aisément nos données.

File:Pandas logo.png - Wikimedia Commons
source : https://commons.wikimedia.org/wiki/File:Pandas_logo.png

1.  Installer et importer Pandas

Si vous avez déjà installé la distribution Anaconda (sinon cliquez ici savoir comment l’installer rapidement) il vous suffit d’installer Pandas comme suit :

conda install pandas

ou via l’installateur de paquet python pip comme suit :

pip install pandas

Tout comme nous importons NumPy sous l’alias np, nous importerons Pandas sous l’alias pd (c’est un peu comme une convention) :

import pandas as pd

2. Les éléments de bases  : les Series et les DataFrames

En effet, comme toute autre bibliothèque, pandas a ses propres structures de données que sont les series et les dataFrames. Ces structures de données peuvent être considérées comme des versions améliorées de tableaux structurés de NumPy.

Les lignes suivantes ont alors tous leurs sens (si on veut travailler avec pandas il faut bien évidement importer Numpy).

import numpy as np
import pandas as pd

Les Series

Une série dans pandas n’est juste qu’un tableau NumPy à une dimension (voir l’article sur NumPy). Elle peut être créée assez aisément comme suit :

data = pd.Series([1.0, 2.0 , 2.25, 3.5])

Pour accéder aux données contenues dans cette série on utilise l’attribut “values” comme suit :

data.values

et on obtient :

array([1.  , 2.  , 2.25, 3.5 ])

L’une des fonctionnalités assez intéressante de pandas est de pouvoir associer ses propres indexes aux données. Pour ce faire, on utilise l’attribut “index

data.index = ['a','b','c','d']

et on obtient :

a    1.00
b    2.00
c    2.25
d    3.50
dtype: float64

On peut ainsi dire qu’une série pandas est une sorte de dictionnaire (clé/valeur) où les clés sont les indexes et les valeurs sont les données de la série.
Il est donc possible de créer une série à partir d’un dictionnaire python.

population_dict = {'Paris': 2187526,
                   'Marseille': 863310,
                   'Lyon': 516092,
                   'Toulouse': 479553,
                   'Nice': 340017}
population = pd.Series(population_dict)

Les DataFrames

Un dataFrame est une structure de données très importante dans pandas. Un dataFrame est un tableau à deux dimensions. On peut même dire que c’est une collection de “Series pandas”.

DataFrame - serie
DataFrame – serie

Afin d’étayer mes propos, je crée une nouvelle ‘Series’ qui contiendra les superficies (km2) des villes citées dans l’exemple plus haut.

area_dict = {'Paris': 105.4,
                   'Marseille': 240.6,
                   'Lyon': 47.87,
                   'Toulouse': 118.3,
                   'Nice': 71.92}
area = pd.Series(area_dict)

La création d’un dataFrame pandas se fait simplement avec “pd.dataFrame()” comme suit :

cities = pd.DataFrame({'population': population,'area': area})

Dans le cas où on n’a pas de Series sous la main, on peut créer un dataFrame directement à partir du jeu de données.
Supposons qu’on ait le jeu de données suivant :

#un dictionnaire comportant uniquement les données sur la population
#et les superficies de chaque ville
data = { 'population': [2187526,863310,516092,479553,340017],
         'area': [105.4, 240.6,47.87,118.3, 71.92]
}

Pour créer notre dataFrame nous pouvons procéder ainsi

#en précisant les indexes
cities = pd.DataFrame(data,index=['Paris','Marseille','Lyon','Toulouse','Nice'])

On obtient le même dataFrame

 populationaerea
Paris2187526105.40
Marseille863310240.60
Lyon51609247.87
Toulouse479553118.30
Nice34001771.92

3. Les Opérations de base avec Pandas

Pandas nous permet de manipuler aisément les données d’une série ou d’un dataFrame, de lire des fichiers, d’appliquer des fonctions aux données …

Accéder aux données

On reprend l’exemple précédent de notre dataFrame.

On a .head() qui nous permet de d’accéder par défaut aux premières lignes de notre dataFrame. (Si on ne met pas de paramètres il nous retourne les 5 premières lignes).

# Les 3 premières lignes
cities.head(3)

Ensuite, on peut accéder uniquement à une colonne de notre dataFrame ou à un sous-ensemble du dataFrame :

# Seulement la colonne population
cities["population"]
# Le dataFrame à partir de la 3e colonne
cities[2:]

Il est aussi possible d’accéder aux données du tableau par le biais de leur position grâce à .iloc()

# la 1e ligne
cities.iloc[0]
# Les 1e et 2e lignes
cities.iloc[[0, 1]]

Filtrer, trier, ranger

Un truc dans pandas qui est vraiment cool, c’est la possibilité de filtrer les données à la volée.
Ex: On veut voir uniquement les villes dont la population est supérieure à 700 000. On le fait comme suit :

cities[ cities["population"] > 700000 ]

De plus, il est aussi possible d’ordonner le dataFrame en fonction d’une colonne.
En supposant que je voudrai par exemple classer dans l’ordre décroissant les villes en fonction de leur superficie.

cities.sort_values(by="area", ascending=False)

Ouvrir et enregistrer des fichiers avec pandas

Ouvrir un CSV : rien de plus simple !

# On suppose que vous avez un fichier fichier.csv
df = pd.read_csv('fichier.csv')

Pareil pour les fichiers JSON

# On suppose que vous avez un fichier fichier.json
df = pd.read_json('fichier.json')

Il est aussi possible de se connecter à une base de données avec pandas. Cependant nous n’aborderons pas ce point dans cet article.

C’est bien de pouvoir lire des données mais c’est encore mieux de pouvoir les exporter. Pandas nous permet d’exporter notre dataFrame en CSV, JSON…

# Notre dataFrame d'exemple
cities.to_csv('cities.csv') #en csv

cities.to_json('cities.json') #en json

Obtenir des informations sur des données

Nous utiliserons à partir de maintenant un dataset des données historiques de la NBA (disponible ici).

# On importe notre dataset nba.csv
nba = pd.read_csv("nba.csv")

La première chose à faire avec un dataFrame pandas (à mon avis) c’est d’utiliser .info()

nba.info()

Et on obtient un bon paquet d’infos sur notre dataFrame.

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 126314 entries, 0 to 126313
Data columns (total 23 columns):
 #   Column         Non-Null Count   Dtype  
---  ------         --------------   -----  
 0   gameorder      126314 non-null  int64  
 1   game_id        126314 non-null  object 
 2   lg_id          126314 non-null  object 
 3   _iscopy        126314 non-null  int64  
 4   year_id        126314 non-null  int64  
 5   date_game      126314 non-null  object 
 6   seasongame     126314 non-null  int64  
 7   is_playoffs    126314 non-null  int64  
 8   team_id        126314 non-null  object 
 9   fran_id        126314 non-null  object 
 10  pts            126314 non-null  int64  
 11  elo_i          126314 non-null  float64
 12  elo_n          126314 non-null  float64
 13  win_equiv      126314 non-null  float64
 14  opp_id         126314 non-null  object 
 15  opp_fran       126314 non-null  object 
 16  opp_pts        126314 non-null  int64  
 17  opp_elo_i      126314 non-null  float64
 18  opp_elo_n      126314 non-null  float64
 19  game_location  126314 non-null  object 
 20  game_result    126314 non-null  object 
 21  forecast       126314 non-null  float64
 22  notes          5424 non-null    object 
dtypes: float64(6), int64(7), object(10)
memory usage: 22.2+ MB

.info() fournit des détails essentiels sur notre dataFrame pandas, tels que le nombre de lignes et de colonnes, le nombre de valeurs non nulles, le type de données de chaque colonne et la quantité de mémoire utilisée par notre DataFrame.
On peut déjà remarquer à ce niveau que la colonne 22 (notes) a un grand nombre de valeurs nulles.

Une autre information importante de notre tableau c’est la forme du tableau. En gros, le nombre lignes et le nombre de colonnes. On obtient cela par le biais de .shape.

nba.shape
(126314, 23)

Afin de mieux comprendre notre jeu de données, on peut aussi utiliser .describe() qui pourra nous afficher certains détails statistiques de base tels que la distribution des données, la moyenne, la variance, etc.

nba.describe()
gameorder_iscopyyear_idseasongameis_playoffsptselo_ielo_nwin_equivopp_ptsopp_elo_iopp_elo_nforecast
count126314.000000126314.000000126314.000000126314.000000126314.000000126314.000000126314.000000126314.000000126314.000000126314.000000126314.000000126314.000000126314.000000
mean31579.0000000.5000001988.20037443.5337330.063857102.7299821495.2360551495.23605541.707889102.7299821495.2360551495.2360550.500000
std18231.9276430.50000217.58230925.3751780.24449914.814845112.139945112.46168710.62733214.814845112.139945112.4616870.215252
min1.0000000.0000001947.0000001.0000000.0000000.0000001091.6445001085.77440010.1525010.0000001091.6445001085.7744000.020447
25%15790.0000000.0000001975.00000022.0000000.00000093.0000001417.2379751416.99490034.10303593.0000001417.2379751416.9949000.327989
50%31579.0000000.5000001990.00000043.0000000.000000103.0000001500.9455501500.95440042.113357103.0000001500.9455501500.9544000.500000
75%47368.0000001.0000002003.00000065.0000000.000000112.0000001576.0600001576.29162549.635328112.0000001576.0600001576.2916250.672011
max63157.0000001.0000002015.000000108.0000001.000000186.0000001853.1045001853.10450071.112038186.0000001853.1045001853.1045000.979553

4. Traiter les valeurs nulles et manipuler vos données avec pandas

Lorsque nous traitons des données, il est impératif de s’assurer qu’on a des données fiables (pas de valeurs nulles, pas de valeurs aberrantes, …).
Pandas nous propose de nombreux outils pour faire face à ces problèmes.

Traiter les valeurs nulles

En effet, traiter les valeurs peut se présenter comme un grand dilemme. Faut-il supprimer les lignes où il y a des null ? Ou faut-il les remplacer par d’autres valeurs (imputation) ?
Il n’y a pas de remède miracle 🙃 cependant, l’idée sera de ne pas créer un biais dans les données.

Alors comment supprimer les null dans pandas ?

# On supprime les *null* avec dropna
nba_dropna = nba.dropna()
# regardons le nombre de données que nous avons maintenant
nba_dropna.shape
(5424, 23)

On voit qu’on perd beaucoup de lignes si l’on procède ainsi. Analysons les null par colonne.

nba.isnull().sum()
gameorder             0
game_id               0
lg_id                 0
_iscopy               0
year_id               0
date_game             0
seasongame            0
is_playoffs           0
team_id               0
fran_id               0
pts                   0
elo_i                 0
elo_n                 0
win_equiv             0
opp_id                0
opp_fran              0
opp_pts               0
opp_elo_i             0
opp_elo_n             0
game_location         0
game_result           0
forecast              0
notes            120890
dtype: int64

On peut supprimer cette colonne mais nous allons essayer de les garder.

# On essaie de voir ce que contient cette colonne 
nba["notes"].dropna().head(10)
930     at Baltimore's Fifth Regiment Armory
931     at Baltimore's Fifth Regiment Armory
1084                              Tiebreaker
1085                              Tiebreaker
1088                              Tiebreaker
1089                              Tiebreaker
1212                           at Chicago IL
1213                           at Chicago IL
1264                           at Chicago IL
1265                           at Chicago IL
Name: notes, dtype: object

Je crois qu’on a le lieu où s’est déroulé la rencontre. Cette information pourrait être importante. Nous allons faire de l’imputation.

Comment faire de l’imputation dans pandas ?

L’imputation est le processus de remplacement des données manquantes avec des valeurs substituées. (wikpédia)
Lorsqu’il s’agit de données numériques (le plus souvent), on peut remplacer les données manquantes par la moyenne, la médiane
Pour le cas des données textuelles, on peut aller chercher une autre source de données qui nous permettra compléter le dataset.
On peut aussi (le cas des données textuelles) remplacer les null par une valeur par défaut (chose que nous allons faire ici).
On utilise .fillna() pour remplacer les valeurs nulles.

# On remplace les *null* par 'unknown'
nba["notes"].fillna("unknown", inplace=True)

Notez qu’en utilisant inplace=True nous avons modifié notre dataFrame de base.

Et lorsqu’on essaie de compter les null . On observe qu’on n’en a plus !

nba.isnull().sum()
gameorder        0
game_id          0
lg_id            0
_iscopy          0
year_id          0
date_game        0
seasongame       0
is_playoffs      0
team_id          0
fran_id          0
pts              0
elo_i            0
elo_n            0
win_equiv        0
opp_id           0
opp_fran         0
opp_pts          0
opp_elo_i        0
opp_elo_n        0
game_location    0
game_result      0
forecast         0
notes            0
dtype: int64

Appliquer des fonctions à son dataFrame pandas

Lorsque je veux itérer sur un dataFrame avec (for , while,…), on se rend compte que l’exécution est parfois lente pour les jeux de données assez volumineux.
Pandas nous propose alors la méthode .apply() qui permettra d’appliquer une fonction sur chaque ligne (ou colonne) de notre dataFrame.
Ex: On a plus haut remplacé les null par unknown; on peut utiliser apply pour remplacer les valeurs unknown par le nom de l’équipe où s’est déroulé la rencontre. On définit une fonction pour effectuer ce traitement puis on l’applique au dataFrame pandas avec .apply().

# Notre fonction
def replaceUnknown(row):
    if row["notes"] == "unknown" :
        if row["game_location"] == "H" : #home
            return row["fran_id"]
        elif row["game_location"] == "A": #away
            return row["opp_fran"]
    return row["notes"]

Puis

nba["notes"]=nba.apply(lambda x : replaceUnknown(x), axis=1)

En effet, la méthode .apply() peut permettre de faire tout ce qu’on veut (pratiquement) avec notre dataFrame pandas.

Ressources complémentaires

La documentation officielle de pandas
10 minutes to pandas
Le repository GitHub de l’article

Conclusion

Nous avons présenté dans cet article la librairie python pandas qui est souvent utilisée dans le monde de la data. Nous avons aussi présenté certaines fonctionnalités de la lib.
Cet article est vraiment introductif à pandas, je vous invite donc à visiter d’autres ressources afin d’améliorer vos connaissances sur le sujet. N’hesitez pas à laisser des commentaires pour les questions et suggestions 🙃.

Series Navigation<< A La Découverte De La Célèbre Librairie NumPy

Laisser un commentaire

Votre adresse email ne sera pas publiée.

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.

Voulez-vous en savoir plus sur la Data Science ?

Inscrivez-vous alors à notre newsletter et vous receverez gratuitement nos derniers articles et actualités ! 
S'INSCRIRE MAINTENANT 
close-link