I. Quelques mots sur Pandas▲
Pandas vise à intégrer les fonctionnalités de NumPy et de matplotlib afin de vous fournir un outil pratique pour l'analyse et la visualisation de données. À côté de l'intégration, l'expérience utilisateur est améliorée.
Pour installer Pandas, sur Debian Stretch 9, exécuter simplement la commande suivante sur votre ligne de commande :
sudo apt install python3-pandas
Cela chargera et installera pandas avec toutes ses dépendances, comme NumPy. Après cela, nous pouvons commencer notre exploration.
Les structures de données de pandas sont capables de contenir des éléments de tout type : Series, DataFrame et Panel.
Le facteur commun est que les structures de données sont étiquetées.
Nous utiliserons la plupart du temps des DataFrames dans cette série de tutoriels, mais voici une brève introduction à chacun d’entre eux :
- Serie : objet étiqueté de type tableau unidimensionnel capable de contenir n'importe quel type d'objet ;
- DataFrame : structure de données étiquetée en deux dimensions dans laquelle les colonnes peuvent être de types différents ;
- Panel : une structure de données en trois dimensions. Vous pouvez les considérer comme des dictionnaires de DataFrames.
II. Fonctionnalité de base▲
Commençons par explorer les fonctionnalités de base de Pandas.
>>>
import
pandas as
pd
>>>
import
numpy as
np
>>>
series =
pd.Series
(
[1
,2
,3
,4
,5
, np.nan, "a string"
, 6
])
>>>
series
0
1
1
2
2
3
3
4
4
5
5
NaN
6
a string
7
6
dtype: object
Note de la rédaction
Pour afficher la sortie dans le terminal, créer un fichier test.py
touch test.py
nano test.py
Ajouter les 4 premières lignes, sans les >>>
Modifier la 4e ligne pour print
(
series)
Dans l'exemple ci-dessus, nous avons créé un objet Series contenant beaucoup de valeurs différentes.
Une chose intéressante à noter est que nous pouvons faire référence à des éléments non numériques en utilisant le symbole nan (Not A Number) de NumPy qui nous indique que l'élément n'est pas un nombre mais qu'il peut être utilisé comme un type numérique indiquant qu'il ne s'agit pas d'un nombre.
Le type de la série est object qui fait référence à son contenu mélangé car nous avons une chaîne incluse.
Si nous utilisons uniquement des types numériques, nous obtenons le type de base NumPy, float pour notre série :
>>>
series =
pd.Series
(
[1
,2
,np.nan, 4
])
>>>
series
0
1.0
1
2.0
2
NaN
3
4.0
dtype: float64
Peu importe que les données incluent un nan, la série sera traitée comme une série de chiffres.
Les séries ne sont pas ce qui nous intéresse, mais les DataFrames, car cela ressemble à un tableau 2D similaire à un fichier CSV ou à une table de base de données relationnelle :
>>>
df =
pd.DataFrame
(
np.array
(
[1
,2
,3
,4
,5
,6
]).reshape
(
2
,3
))
>>>
df
0
1
2
0
1
2
3
1
4
5
6
>>>
df.dtypes
0
int32
1
int32
2
int32
dtype: object
Le paramètre par défaut affiche l'index numérique des lignes et des colonnes, mais il peut être modifié pour donner plus de sens aux données :
>>>
df =
pd.DataFrame
(
np.array
(
[1
,2
,3
,4
,5
,6
]).reshape
(
2
,3
), columns=
list(
'ABC'
), index=
list(
'XY'
))
>>>
df
A B C
X 1
2
3
Y 4
5
6
Comme vous pouvez le constater, l'argument d'index fournit la liste à utiliser pour les lignes, tandis que la liste fournie via l'argument de colonnes peut être utilisée pour modifier les index de colonne.
Si nous avons de plus grands ensembles de données, la méthode head peut également être utile.
Elle affiche les n premières lignes fournies en tant qu'argument.
Si vous ne fournissez pas d'argument, la valeur par défaut de 5 sera utilisée :
>>>
df2 =
pd.DataFrame
(
np.arange
(
1
, 7501
).reshape
(
500
,15
))
>>>
df2.head
(
2
)
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
>>>
df2.head
(
)
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
2
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
3
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
4
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
Le contraire est la méthode tail. Elle montre les n dernières lignes du DataFrame. Si vous omettez l’argument, la valeur par défaut de 5 sera utilisée :
>>>
df2.tail
(
)
0
1
2
3
4
5
6
7
8
9
10
11
\
495
7426
7427
7428
7429
7430
7431
7432
7433
7434
7435
7436
7437
496
7441
7442
7443
7444
7445
7446
7447
7448
7449
7450
7451
7452
497
7456
7457
7458
7459
7460
7461
7462
7463
7464
7465
7466
7467
498
7471
7472
7473
7474
7475
7476
7477
7478
7479
7480
7481
7482
499
7486
7487
7488
7489
7490
7491
7492
7493
7494
7495
7496
7497
12
13
14
495
7438
7439
7440
496
7453
7454
7455
497
7468
7469
7470
498
7483
7484
7485
499
7498
7499
7500
>>>
df2.tail
(
1
)
0
1
2
3
4
5
6
7
8
9
10
11
\
499
7486
7487
7488
7489
7490
7491
7492
7493
7494
7495
7496
7497
12
13
14
499
7498
7499
7500
Comme vous pouvez le constater, les colonnes sont divisées en plusieurs lignes si elles sont trop larges, mais cela ne nous dérangera plus à l'avenir car nous n'afficherons pas nos données sur la console, mais nous utiliserons des visualisations pour montrer ce que nous pouvons collecter.
III. Description des données▲
C'est la fonctionnalité que nous utiliserons très souvent si nous avons un nouvel ensemble de données à analyser :
>>>
df3 =
pd.DataFrame
(
np.arange
(
1
, 100
, 0.12
).reshape
(
33
,25
))
>>>
df3.describe
(
)
0
1
2
3
4
5
\
count 33.000000
33.000000
33.000000
33.000000
33.000000
33.000000
mean 49.000000
49.120000
49.240000
49.360000
49.480000
49.600000
std 29.008619
29.008619
29.008619
29.008619
29.008619
29.008619
min 1.000000
1.120000
1.240000
1.360000
1.480000
1.600000
25
%
25.000000
25.120000
25.240000
25.360000
25.480000
25.600000
50
%
49.000000
49.120000
49.240000
49.360000
49.480000
49.600000
75
%
73.000000
73.120000
73.240000
73.360000
73.480000
73.600000
max 97.000000
97.120000
97.240000
97.360000
97.480000
97.600000
6
7
8
9
... 15
\
count 33.000000
33.000000
33.000000
33.000000
... 33.000000
mean 49.720000
49.840000
49.960000
50.080000
... 50.800000
std 29.008619
29.008619
29.008619
29.008619
... 29.008619
min 1.720000
1.840000
1.960000
2.080000
... 2.800000
25
%
25.720000
25.840000
25.960000
26.080000
... 26.800000
50
%
49.720000
49.840000
49.960000
50.080000
... 50.800000
75
%
73.720000
73.840000
73.960000
74.080000
... 74.800000
max 97.720000
97.840000
97.960000
98.080000
... 98.800000
16
17
18
19
20
21
\
count 33.000000
33.000000
33.000000
33.000000
33.000000
33.000000
mean 50.920000
51.040000
51.160000
51.280000
51.400000
51.520000
std 29.008619
29.008619
29.008619
29.008619
29.008619
29.008619
min 2.920000
3.040000
3.160000
3.280000
3.400000
3.520000
25
%
26.920000
27.040000
27.160000
27.280000
27.400000
27.520000
50
%
50.920000
51.040000
51.160000
51.280000
51.400000
51.520000
75
%
74.920000
75.040000
75.160000
75.280000
75.400000
75.520000
max 98.920000
99.040000
99.160000
99.280000
99.400000
99.520000
22
23
24
count 33.000000
33.000000
33.000000
mean 51.640000
51.760000
51.880000
std 29.008619
29.008619
29.008619
min 3.640000
3.760000
3.880000
25
%
27.640000
27.760000
27.880000
50
%
51.640000
51.760000
51.880000
75
%
75.640000
75.760000
75.880000
max 99.640000
99.760000
99.880000
[8 lignes x 25 colonnes]
Comme vous pouvez le constater, l'appel de la méthode describe sur le DataFrame affiche un bref résumé sur chaque colonne de l'ensemble de données :
Le nombre d'éléments (count), leur moyenne, l'écart type (std), les valeurs minimale et maximale et certaines valeurs dans l'intervalle.
IV. Indexation et découpage▲
Le découpage en tranches de données fonctionne avec l'attribut iloc :
>>>
df3 =
pd.DataFrame
(
np.arange
(
1
, 100
, 0.12
).reshape
(
33
,25
))
>>>
df3.iloc[:5
,:10
]
0
1
2
3
4
5
6
7
8
9
0
1.0
1.12
1.24
1.36
1.48
1.6
1.72
1.84
1.96
2.08
1
4.0
4.12
4.24
4.36
4.48
4.6
4.72
4.84
4.96
5.08
2
7.0
7.12
7.24
7.36
7.48
7.6
7.72
7.84
7.96
8.08
3
10.0
10.12
10.24
10.36
10.48
10.6
10.72
10.84
10.96
11.08
4
13.0
13.12
13.24
13.36
13.48
13.6
13.72
13.84
13.96
14.08
Dans l'exemple ci-dessus, nous avons sélectionné les 5 premières lignes et les 10 premières colonnes.
Nous pouvons aussi implémenter les méthodes head() et tail() en utilisant le nombre de lignes par défaut de 5 avec iloc :
>>>
df3.iloc[-
5
:] # df3.tail(5)
0
1
2
3
4
5
6
7
8
9
... \
28
85.0
85.12
85.24
85.36
85.48
85.6
85.72
85.84
85.96
86.08
...
29
88.0
88.12
88.24
88.36
88.48
88.6
88.72
88.84
88.96
89.08
...
30
91.0
91.12
91.24
91.36
91.48
91.6
91.72
91.84
91.96
92.08
...
31
94.0
94.12
94.24
94.36
94.48
94.6
94.72
94.84
94.96
95.08
...
32
97.0
97.12
97.24
97.36
97.48
97.6
97.72
97.84
97.96
98.08
...
15
16
17
18
19
20
21
22
23
24
28
86.8
86.92
87.04
87.16
87.28
87.4
87.52
87.64
87.76
87.88
29
89.8
89.92
90.04
90.16
90.28
90.4
90.52
90.64
90.76
90.88
30
92.8
92.92
93.04
93.16
93.28
93.4
93.52
93.64
93.76
93.88
31
95.8
95.92
96.04
96.16
96.28
96.4
96.52
96.64
96.76
96.88
32
98.8
98.92
99.04
99.16
99.28
99.4
99.52
99.64
99.76
99.88
[5 lignes x 25 colonnes]
>>>
df3.iloc[:5
] # df3.head(5)
0
1
2
3
4
5
6
7
8
9
... \
0
1.0
1.12
1.24
1.36
1.48
1.6
1.72
1.84
1.96
2.08
...
1
4.0
4.12
4.24
4.36
4.48
4.6
4.72
4.84
4.96
5.08
...
2
7.0
7.12
7.24
7.36
7.48
7.6
7.72
7.84
7.96
8.08
...
3
10.0
10.12
10.24
10.36
10.48
10.6
10.72
10.84
10.96
11.08
...
4
13.0
13.12
13.24
13.36
13.48
13.6
13.72
13.84
13.96
14.08
...
15
16
17
18
19
20
21
22
23
24
0
2.8
2.92
3.04
3.16
3.28
3.4
3.52
3.64
3.76
3.88
1
5.8
5.92
6.04
6.16
6.28
6.4
6.52
6.64
6.76
6.88
2
8.8
8.92
9.04
9.16
9.28
9.4
9.52
9.64
9.76
9.88
3
11.8
11.92
12.04
12.16
12.28
12.4
12.52
12.64
12.76
12.88
4
14.8
14.92
15.04
15.16
15.28
15.4
15.52
15.64
15.76
15.88
[5 lignes x 25 colonnes]
Mais pandas ajoute une étape supplémentaire et nous permet d’accéder aux données via des étiquettes dans les DataFrames.
Dans cet exemple, ce n’est pas très spectaculaire car les étiquettes de l’ensemble de données sont les mêmes que leur position.
Cependant, en utilisant la méthode de changement de nom, vous pouvez facilement renommer les colonnes du DataFrame et utiliser ces index pour accéder aux valeurs :
>>>
df4 =
df3.rename
(
columns=
lambda
c: chr(
65
+
c))
>>>
df4.loc[:5
, 'A'
:'D'
]
A B C D
0
1.0
1.12
1.24
1.36
1
4.0
4.12
4.24
4.36
2
7.0
7.12
7.24
7.36
3
10.0
10.12
10.24
10.36
4
13.0
13.12
13.24
13.36
5
16.0
16.12
16.24
16.36
>>>
df4.loc[:5
, (
'A'
,'D'
)]
A D
0
1.0
1.36
1
4.0
4.36
2
7.0
7.36
3
10.0
10.36
4
13.0
13.36
5
16.0
16.36
La partie la plus intéressante est la fonction lambda fournie pour les noms des colonnes.
En effet, vous avez besoin d'un paramètre de type dictionnaire ou d'une fonction qui renomme les étiquettes.
Créer un dictionnaire serait un exemple, une fonction anonyme simple est donc idéale.
La fonction chr() renvoie la représentation sous forme de caractères du nombre fourni, chr (65) égale le caractère A.
Le paramètre c est l'en-tête actuel de l'axe (dans ce cas, les colonnes), qui est un nombre compris entre 0 et 24.
Ceci est une bonne chose pour une utilisation future où nous traitons des données à partir de fichiers CSV.
V. Lecture de fichiers CSV▲
Le moment est venu de donner des exemples concrets.
Nous utiliserons le même ensemble de données déjà connu dans le dernier article de NumPy.
Si vous ne l'avez pas lu ou si vous ne voulez simplement pas le consulter, voici le fichier à télécharger : Noms de bébé les plus populaires selon le sexe et le groupe ethnique de la mère, New York.
Je l'ai enregistré sous le nom baby_names.csv.
Il contient des noms donnés aux bébés de la ville de New York, en fonction du sexe et de l’appartenance ethnique. Commençons par lire dans le fichier :
>>>
import
numpy as
np
>>>
import
pandas as
pd
>>>
baby_names =
pd.read_csv
(
'baby_names.csv'
)
>>>
baby_names.head
(
)
BRTH_YR GNDR ETHCTY NM CNT RNK
0
2011
FEMALE HISPANIC GERALDINE 13
75
1
2011
FEMALE HISPANIC GIA 21
67
2
2011
FEMALE HISPANIC GIANNA 49
42
3
2011
FEMALE HISPANIC GISELLE 38
51
4
2011
FEMALE HISPANIC GRACE 36
53
>>>
baby_names.tail
(
)
BRTH_YR GNDR ETHCTY NM CNT RNK
13957
2014
MALE WHITE NON HISPANIC Yousef 18
94
13958
2014
MALE WHITE NON HISPANIC Youssef 24
88
13959
2014
MALE WHITE NON HISPANIC Yusuf 16
96
13960
2014
MALE WHITE NON HISPANIC Zachary 90
39
13961
2014
MALE WHITE NON HISPANIC Zev 49
65
Vous pouvez voir que la lecture d'un fichier CSV se produit avec la fonction read_csv.
Le résultat est un DataFrame et, contrairement à NumPy, nous n'avons pas eu à dire à Pandas de lire toutes les données sous forme de chaînes.
Cela signifie que nous avons des colonnes contenant des valeurs numériques sous forme de types numériques dans le DataFrame.
Nous pouvons le vérifier si nous regardons la description du DataFrame :
>>>
baby_names.describe
(
)
BRTH_YR CNT RNK
count 13962.000000
13962.000000
13962.000000
mean 2011.880318
34.531657
56.944349
std 1.134940
40.176370
25.361691
min 2011.000000
10.000000
1.000000
25
%
2011.000000
13.000000
38.000000
50
%
2011.000000
20.000000
59.000000
75
%
2013.000000
36.000000
78.000000
max 2014.000000
426.000000
102.000000
Vous pouvez voir que describe() ne sélectionne que les colonnes contenant des données numériques.
Naturellement, certaines informations n’ont aucun sens, comme la colonne RNK (seul le nombre est utile) ou la moyenne et la valeur standard dans BRTH_YR.
Vous avez peut-être remarqué que les colonnes du DataFrame ont des étiquettes identiques aux en-têtes du fichier CSV.
C'est une fonctionnalité intéressante dans Pandas qui devient utile lorsque vous n'avez pas besoin de connaître l'index de la colonne à laquelle vous souhaitez accéder.
Les DataFrames peuvent être triés le long de colonnes ou d'axes et la forme sera conservée après le tri.
Nous avons maintenant un véritable ensemble de données, alors essayons de trier.
Trouvons les cinq premiers noms avec le nombre le plus bas et le plus élevé dans toutes les données (rappelez-vous : nous avons des années de 2011 à 2014. Le tri comptera uniquement les valeurs min et max en fonction des données de chaque année et ne résumera pas les mêmes noms par année) :
>>>
baby_names.head
(
)
BRTH_YR GNDR ETHCTY NM CNT RNK
0
2011
FEMALE HISPANIC GERALDINE 13
75
1
2011
FEMALE HISPANIC GIA 21
67
2
2011
FEMALE HISPANIC GIANNA 49
42
3
2011
FEMALE HISPANIC GISELLE 38
51
4
2011
FEMALE HISPANIC GRACE 36
53
>>>
baby_names.sort_values
(
by=
'CNT'
).head
(
)
BRTH_YR GNDR ETHCTY NM CNT RNK
8744
2012
FEMALE WHITE NON HISP MAE 10
83
6746
2011
FEMALE WHITE NON HISPANIC LEILA 10
81
2389
2011
MALE HISPANIC ALLAN 10
94
11009
2013
MALE ASIAN AND PACIFIC ISLANDER Martin 10
57
11013
2013
MALE ASIAN AND PACIFIC ISLANDER Maximilian 10
57
>>>
baby_names.sort_values
(
by=
'CNT'
, ascending=
False
).head
(
)
BRTH_YR GNDR ETHCTY NM CNT RNK
1504
2011
MALE HISPANIC JAYDEN 426
1
5430
2011
MALE HISPANIC JAYDEN 426
1
7393
2011
MALE HISPANIC JAYDEN 426
1
3505
2011
MALE HISPANIC JAYDEN 426
1
9385
2012
MALE HISPANIC JAYDEN 364
1
Comme vous pouvez le constater, nous avons identifié un problème dans l'ensemble de données.
Nous avons des entrées qui ont le même contenu.
Cela rend nos données moins utilisables telles quelles et nous devons les ranger pour obtenir de véritables informations.
Mais c’est la bonne chose en science des données et nous examinerons une approche permettant de filtrer les doublons.
VI. Conclusion▲
Nous avons vu que Pandas est la prochaine étape de l'analyse de données avec Python après NumPy, car il nous permet de mieux gérer les données.
Cependant, nous ne pouvons pas ignorer NumPy car Pandas s'appuie sur NumPy et matplotlib pour nous donner un point unique où vous pouvez analyser et visualiser vos données.
Pandas tire vraiment parti de cette fonctionnalité si nous importons des fichiers CSV avec un contenu mixte.
Vous n'avez pas à vous soucier des conversions.
Dans le prochain article, nous allons nettoyer nos données des doublons et procéder à quelques analyses pour visualiser notre ensemble de données avec matplotlib afin de fournir des informations à l’utilisateur.
VII. Remerciements▲
Nous remercions Gabor Laszlo Hajba de nous avoir autorisés à publier son tutoriel Introduction to Pandas - Data Analysis in Python.
Nous tenons également à remercier ZerooCool pour la traduction de ce tutoriel, Christophe pour la validation technique et Genthial pour la relecture orthographique