Pandas est une librairie très connue pour l'analyse et la manipulation de données en Python. Elle est notamment très utilisée en data science. Son nom est une référence à la fois à "Panel Data" et "Python Data Analysis".
Pandas va nous permettre par exemple d'analyser de grandes quantités de données et d'en déduire des statistiques, ou alors de rendre plus lisibles des grands ensembles de données, ce qui est très important en data science.
Pandas vous permet de modéliser simplement des données d'apparence complexe, par exemple pour modéliser des données boursières avec le prix d'ouverture, de fermeture, la date d'ouverture, etc... Elle vous permet également de calculer des corrélations entre données, ou bien de calculer une moyenne, un maximum, un minimum...
La première chose à faire afin d'utiliser Pandas, c'est de l'installer. Pour cela on utilise pip
comme pour toute autre installation de librairie. Ouvrez votre interface en ligne de commande (cmd.exe) et exécutez la commande suivante :
pip install pandas
Il faut maintenant importer la librairie. Ouvrez votre interpréteur Python et importez la de la façon suivante :
import pandas
Note : Pandas est souvent importée avec l'alias pd
en Python, de cette façon, par convention, je vous conseille de faire pareil :
import pandas as pd
Une Serie est un tableau d'une dimension pouvant contenir n'importe quel type de données. C'est comme une colonne ou une ligne. Voici comment créer une Serie, par exemple à partir d'une liste :
>>> my_list = ['a', 'b', 'c']
>>> my_serie = pd.Series(my_list)
>>> my_serie
0 a
1 b
2 c
dtype: object
Lorsque l'on affiche une Serie, la première colonne affichée est la colonne correspondant aux index, et la seconde correspond aux valeurs de notre Serie. Le type est également affiché (dtype). Mais maintenant, que peut-on faire avec notre Serie ?
On peut afficher ses valeurs. Par défaut, si aucun index n'est spécifié, l'index de chaque valeur est son numéro. On accède donc aux valeurs d'une série en utilisant les index, ici ce sera donc de cette façon :
>>> my_serie[0]
'a'
>>> my_serie[2]
'c'
On peut également modifier l'index par défaut lors de l'initialisation d'une Serie.
>>> my_serie = pd.Series(my_list, index=['x', 'y', 'z'])
>>> my_serie
x a
y b
z c
dtype: object
On passe simplement en second paramètre notre liste d'index, qui doit donc être de même taille que celle de la liste de nos valeurs. Maintenant, pour accéder à une valeur de la Serie, on utilise son index comme au dessus, sauf que cette fois les index ont changé.
>>> my_serie['x']
'a'
>>> my_serie['y']
'b'
Note : on peut toujours accéder aux valeurs d'une série dont les index ont changé en utilisant les numéros comme plus haut, par exemple, my_serie[0]
ne génèrera pas d'erreur, même en ayant changé les index.
On peut également initialiser une série en utilisant un dictionnaire. Dans ce cas, le clés seront les index, et les valeurs seront les valeurs de la série.
>>> my_dict = {'x': 'a', 'y': 'b', 'z': 'c'}
>>> my_serie = pd.Series(my_dict)
>>> my_serie
x a
y b
z c
dtype: object
On peut également spécifier le paramètre index
pour n'utiliser que les valeurs dont on aura précisé l'index.
>>> my_serie = pd.Series(my_dict, index=['x', 'z'])
>>> my_serie
x a
z c
dtype: object
C'est tout pour les séries. Pour l'instant, vous trouvez peut-être qu'elles ressemblent à des listes un peu particulières, et c'est le cas, mais nous verrons plus tard en quoi elles sont utiles.
Un DataFrame est un peu comme un assemblage de plusieurs Series. C'est un tableau multi-dimensionnel. On peut créer un DataFrame de plusieurs façons. L'une d'elles est d'utiliser un dictionnaire dont les clés sont les noms des colonnes, et les valeurs sont des listes correspondant aux valeurs des colonnes. Voici un exemple pour comprendre :
>>> data = {"names": ["Jean", "Yves", "Paul"], "ages": [41, 45, 32]}
>>> df = pd.DataFrame(data)
>>> df
names ages
0 Jean 41
1 Yves 45
2 Paul 32
On peut également initialiser un DataFrame en utilisant une liste de listes. Dans ce cas chaque sous-liste correspondra à une rangée du DataFrame.
>>> data = [["Jean", 41], ["Yves", "45"], ["Paul", 32]]
>>> df = pd.DataFrame(data)
>>> df
0 1
0 Jean 41
1 Yves 45
2 Paul 32
Remarquez que dans cet exemple, les colonnes n'ont pas de noms et sont donc nommées avec l'index par défaut. Pour leur donner un nom, on utlise l'argument columns
lors de l'initialisation du DataFrame, et on passe en paramètre une liste correspondant aux index des colonnes.
>>> df = pd.DataFrame(data, columns=["names", "ages"])
>>> df
names ages
0 Jean 41
1 Yves 45
2 Paul 32
Pour accéder aux données contenues dans un DataFrame, il y a plusieurs moyens. D'abord, on peut utiliser la méthode loc[index]
(mettez bien des crochets et non des parenthèses). Cette méthode nous renvoie une Serie dont les index sont les colonnes du DataFrame, et les valeurs sont les valeurs de ces colonnes, à la ligne dont l'index est passé en paramètres. Un exemple en reprenant le DataFrame ci-dessus :
>>> df.loc[0]
names Jean
ages 41
Name: 0, dtype: object
On récupère la ligne d'index 0 sous forme de Serie. On peut également récupérer plusieurs lignes en même temps. Dans ce cas, au lieu de récupérer une Serie on récupère un DataFrame.
>>> df.loc[[0, 1]]
names ages
0 Jean 41
1 Yves 45
De la même façon que pour les Serie, on peut modifier les index des lignes d'un DataFrame. On utilise l'argument index
à l'initialisation.
>>> df = pd.DataFrame(data, columns=["names", "ages"], index=['a', 'b', 'c'])
>>> df
names ages
a Jean 41
b Yves 45
c Paul 32
Et pour accéder aux index nommés, on utilise simplement loc(index)
comme précédemment.
>>> df.loc['b']
names Yves
ages 45
Name: b, dtype: object
Finalement, une autre façon d'initialiser un DataFrame est d'utiliser un fichier csv. Pour cela, on utilise la fonction pd.read_csv(chemin)
où chemin
est le chemin pour accéder au fichier qu'on souhaite ouvrir. Si votre fichier est situé dans le même répertoire que votre script, le chemin sera alors simplement son nom. Si vous le souhaitez, vous pouvez télécharger le fichier data.csv mis à votre disposition afin d'essayer la méthode read_csv
.
Si jamais vous avez téléchargé le fichier, mettez le n'importe où, à la racine de C:/ par exemple, et ouvrez le avec Pandas :
>>> df = pd.read_csv("C:/data.csv")
>>> df
Duration Pulse Maxpulse Calories
0 60 110 130 409.1
1 60 117 145 479.0
2 60 103 135 340.0
3 45 109 175 282.4
4 45 117 148 406.0
.. ... ... ... ...
164 60 105 140 290.8
165 60 110 145 300.0
166 60 115 145 310.2
167 75 120 150 320.4
168 75 125 150 330.4
[169 rows x 4 columns]
Notre DataFrame est trop long, donc tout n'est pas affiché. Seules les 5 premières et les 5 dernières colonnes sont affichées. Si l'on souhaite modifier ce comportement, on peut modifier la variable suivante : pd.options.display.max_rows
qui définit le nombre maximal de lignes à afficher.
pd.options.display.max_rows = 9999
Ici, on affichera au maximum 9999 lignes, ce qui est largement suffisant je pense.
On peut également utiliser la fonction pd.read_json(chemin)
afin de convertir les données d'un fichier JSON en DataFrame. Si vos données JSON sont sous forme de dictionnaire, vous savez déjà comment créer un DataFrame à partir d'un dictionnaire.
Vous savez maintenant comment créer des Series et des DataFrames. La prochaine étape est donc de les utiliser afin de manipuler correctement des données.
En data science, une fois qu'on a collecté nos données, il est important de les comprendre et de les analyser. C'est ce que nous allons voir, grossièrement. Pour les exemples, on va considérer le DataFrame étudié à la fin de la section précédente, chargé à partir d'un fichier csv.
D'abord, on aimerait obtenir une vue rapide des éléments de ce DataFrame. On utilise pour cela la méthode head(lignes)
de notre DataFrame en lui passant en paramètre le nombre de lignes qu'on souhaite visualiser.
>>> df.head(10)
Duration Pulse Maxpulse Calories
0 60 110 130 409.1
1 60 117 145 479.0
2 60 103 135 340.0
3 45 109 175 282.4
4 45 117 148 406.0
5 60 102 127 300.0
6 60 110 136 374.0
7 45 104 134 253.3
8 30 109 133 195.1
9 60 98 124 269.0
Ici, on affiche les 10 dernières lignes de notre DataFrame. Pour afficher les 10 dernières lignes, on utilise la méthode tail
qui s'utilise de la même façon.
>>> df.tail(10)
Duration Pulse Maxpulse Calories
159 30 80 120 240.9
160 30 85 120 250.4
161 45 90 130 260.4
162 45 95 130 270.0
163 45 100 140 280.9
164 60 105 140 290.8
165 60 110 145 300.0
166 60 115 145 310.2
167 75 120 150 320.4
168 75 125 150 330.4
Maintenant, on aimerait obtenir quelques informations sur notre DataFrame. On utilise la méthode info
.
>>> df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 169 entries, 0 to 168
Data columns (total 4 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 Duration 169 non-null int64
1 Pulse 169 non-null int64
2 Maxpulse 169 non-null int64
3 Calories 164 non-null float64
dtypes: float64(1), int64(3)
memory usage: 5.4 KB
Cela nous indique que notre DataFrame contient 169 lignes, ordonnées de 0 à 168, et qu'il contient 4 colonnes, avec leur nom, leur index, le nombre de valeurs non nulles, et le type de valeur stockée pour chaque colonne. Finalement, on obtient également l'emplacement pris en mémoire par notre DataFrame. Remarquez que pour la colonne "Calories", le nombre de valeurs non nulles est de 164, et non 169. Cela signifie que 5 valeurs sont nulles.
Si on veut des informations plutôt statistiques sur notre DataFrame, on dispose de méthodes pour calculer la moyenne, la médiane, ou pour trouver la valeur apparaissant le plus souvent par exemple.
>>> df.mean() # Moyenne
Duration 68.4375
Pulse 103.5000
Maxpulse 128.5000
Calories 304.6800
dtype: float64
>>> df.median() # Médiane
Duration 60.0
Pulse 102.5
Maxpulse 127.5
Calories 291.2
dtype: float64
>>> df.mode() # Valeurs les plus fréquentes
Duration Date Pulse Maxpulse Calories
0 60 '2020/12/12' 100 120 300.0
Si l'on souhaite utiliser ces méthodes, mais sur une colonne uniquement du DataFrame, ont peut récupérer la colonne de cette façon :
>>> df["Calories"]
...
>>> df["Calories"].mean()
304.68
Nous avons vu précédemment qu'un DataFrame pouvait contenir des valeurs nulles. Ces valeurs sont des valeurs qu'on souhaite éviter et qu'on pourrait définir comme de la mauvaise donnée. Un autre exemple de mauvaise donnée serait des valeurs au mauvais format. Il est donc important de bien nettoyer les données avec lesquelles on souhaite travailler avant toute chose. Nous allons utiliser pour la suite un DataFrame contenant quelques imperfections, vous pouvez le télécharger ici : dirtydata.csv
>>> df = pd.read_csv("C:/dirtydata.csv")
>>> df
Duration Date Pulse Maxpulse Calories
0 60 '2020/12/01' 110 130 409.1
1 60 '2020/12/02' 117 145 479.0
2 60 '2020/12/03' 103 135 340.0
3 45 '2020/12/04' 109 175 282.4
4 45 '2020/12/05' 117 148 406.0
5 60 '2020/12/06' 102 127 300.0
6 60 '2020/12/07' 110 136 374.0
7 450 '2020/12/08' 104 134 253.3
8 30 '2020/12/09' 109 133 195.1
9 60 '2020/12/10' 98 124 269.0
10 60 '2020/12/11' 103 147 329.3
11 60 '2020/12/12' 100 120 250.7
12 60 '2020/12/12' 100 120 250.7
13 60 '2020/12/13' 106 128 345.3
14 60 '2020/12/14' 104 132 379.3
15 60 '2020/12/15' 98 123 275.0
16 60 '2020/12/16' 98 120 215.2
17 60 '2020/12/17' 100 120 300.0
18 45 '2020/12/18' 90 112 NaN
19 60 '2020/12/19' 103 123 323.0
20 45 '2020/12/20' 97 125 243.0
21 60 '2020/12/21' 108 131 364.2
22 45 NaN 100 119 282.0
23 60 '2020/12/23' 130 101 300.0
24 45 '2020/12/24' 105 132 246.0
25 60 '2020/12/25' 102 126 334.5
26 60 20201226 100 120 250.0
27 60 '2020/12/27' 92 118 241.0
28 60 '2020/12/28' 103 132 NaN
29 60 '2020/12/29' 100 132 280.0
30 60 '2020/12/30' 102 129 380.3
31 60 '2020/12/31' 92 115 243.0
>>> df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 32 entries, 0 to 31
Data columns (total 5 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 Duration 32 non-null int64
1 Date 31 non-null object
2 Pulse 32 non-null int64
3 Maxpulse 32 non-null int64
4 Calories 30 non-null float64
dtypes: float64(1), int64(3), object(1)
memory usage: 1.4+ KB
Ce DataFrame contient des mauvaises données (valeurs nulles, lignes dupliquées, valeurs mal formatées, valeur étranges), nous allons voir comment le traiter.
Pour commencer, nous allons nous débarrasser des lignes contenant des valeurs nulles. Pour cela, on utilise la méthode dropna
qui fait tout le boulot.
>>> df = df.dropna()
>>> df.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 29 entries, 0 to 31
Data columns (total 5 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 Duration 29 non-null int64
1 Date 29 non-null object
2 Pulse 29 non-null int64
3 Maxpulse 29 non-null int64
4 Calories 29 non-null float64
dtypes: float64(1), int64(3), object(1)
memory usage: 1.4+ KB
On constate que toutes nos colonnes contiennent maintenant le même nombre de valeurs non nulles. Remarquez qu'on déclare un nouveau DataFrame. En effet, df.dropna()
ne modifie pas le DataFrame sur lequel on travaille, mais en renvoie simplement une version modifiée. On peut modifier ce comportement en ajoutant l'argument inplace=True
, qui permet de préciser qu'on souhaite modifier le DataFrame sur lequel on travaille et non en renvoyer une copie modifiée.
>>> df.dropna(inplace=True)
# Identique à df = df.dropna()
On aurait également pu faire autrement, par exemple en remplaçant les valeurs nulles par une autre valeur plutôt que de les supprimer. En effet, si notre DataFrame est petit et contient beaucoup de valeurs nulles, supprimer les rangées les contenant rend le DataFrame encore plus petit, et donc pas forcément représentatif de ce qu'on modélise. Pour remplacer les valeurs nulles, on utilise la méthode fillna(valeur)
.
>>> df.fillna(100, inplace=True)
# Remplace les valeurs nulles par 100
On peut également préciser les colonnes sur lesquelles on veut effectuer ces actions, si jamais on ne veut pas effectuer la même chose partout.
>>> df["Calories"].fillna(100, inplace = True)
Si vous regardez notre DataFrame, vous remarquez qu'à la ligne 26, pour la colonne "Date", nous n'avons pas une date mais un nombre. On aimerait convertir ce nombre en date.
On utilise pour cela la fonction pd.to_datetime(data)
qui convertit des données en date.
>>> df['Date'] = pd.to_datetime(df['Date'])
Quelque fois, des mauvaises données peuvent être simplement des données qui ne sont pas représentatives, ou bien des données qui ont mal été rentrées. Par exemple, quelqu'un peut très bien avoir écrit à l'ordinateur 300 au lieu de 3.00. Et cela fausse tout, il faut donc se débarrasser de ces données. Dans notre DataFrame, à la ligne 7, la valeur de la colonne "Duration" est 450, ce qui est trop élevé par rapport aux autres lignes. On doit donc se débarrasser de cette valeur. Vous vous souvenez comment on sélectionne une ligne dans un DataFrame ? On utilise la méthode loc
. Ici, on va préciser en plus un second paramètre qui correspond à la colonne à laquelle on souhaite accéder.
>>> df.loc[7, "Duration"] # On accède à la ligne 7 de la colonne "Duration"
450
>>> df.loc[7, "Duration"] = 45
Mais on ne peut pas faire cela à la main pour toutes les valeurs si jamais notre DataFrame est trop grand. On doit donc le parcourir, et éliminer ces valeurs éventuellement. Pour cela, on peut utiliser une boucle :
>>> for index in df.index:
... if df.loc[index, "Duration"] > 110:
... df.loc[index, "Duration"] = 110
# Met à 110 toutes les valeurs supérieures à 110
On aurait pu également supprimer les rangées où les valeurs sont incorrectes, plutôt que de les modifier, pour cela on utilise la méthode drop(index)
qui supprime une rangée en fonction de l'index :
>>> for index in df.index:
... if df.loc[index, "Duration"] > 120:
... df.drop(index, inplace = True)
La dernière étape est de supprimer les lignes en doubles qui auraient pu être insérées par erreur par exemple. La méthode duplicated
nous renvoie une Serie contenant en index les index de notre DataFrame, et en valeurs un booléen égal à True si la ligne est un doublon, False sinon.
>>> df.duplicated()
0 False
1 False
2 False
3 False
4 False
5 False
6 False
7 False
8 False
9 False
10 False
11 False
12 True
13 False
14 False
15 False
16 False
17 False
19 False
20 False
21 False
23 False
24 False
25 False
26 False
27 False
29 False
30 False
31 False
dtype: bool
Une méthode existe pour supprimer les doublons :
>>> df.drop_duplicates(inplace = True)
Voilà, vous en connaissez maintenant un petit peu plus sur la librairie Pandas. On peut faire beaucoup d'autres choses, le but de l'article n'est pas de présenter une liste exhaustive des fonctionnalités, pour cela vous avez la documentation, mais au moins vous savez dans les grandes lignes comment fonctionne cette librairie qu'il est PRIMORDIAL de connaitre en Python.
Si cet article vous a plus, n'hésitez pas à laisser un commentaire ! Si vous avez des questions, que vous avez besoin de précisions ou que vous avez des retours à me faire, vous pouvez également me contacter via l'onglet disponible sur le site !