Introduction à la bibliothèque Pandas

Tutoriel pour apprendre l'analyse des données en Python

Gabor Laszlo Hajba

Traducteur

Laethy

Dans ce tutoriel, nous allons continuer la série de tutoriels présentant NumPy. Nous allons aborder la bibliothèque Pandas, la norme de fait pour le traitement des données avec Python.

Nous avons rencontré certaines limitations lors de l'utilisation de NumPy, par exemple, le chargement à partir d'un fichier CSV exigeait que le contenu de chaque colonne soit une chaîne de caractères ; c'était un problème s'il y avait une colonne contenant des données numériques. Maintenant, nous allons avancer et voir comment Pandas aborde ce problème et comment nous pouvons le résoudre.

Commentez

- Titre : Introduction à la bibliothèque Pandas

- Auteur : Gabor Laszlo Hajba

- Traducteur : Laethy

- Parution : 14 juin 2017

- Licence : Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2017 Gabor Laszlo Hajba . Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. Quelques mots sur Pandas

- II. Fonctionnalités de base

- III. Description des données

- IV. Indexation et partage

- V. Lecture de fichiers CSV

- VI. Conclusion

- VII. Remerciements

I - Quelques mots sur Pandas

La bibliothèque Pandas vise à intégrer les fonctionnalités de NumPy et matplotlib, pour vous donner un outil pratique pour analyser et visualiser les données. La bibliothèque Pandas va au-delà d'une simple intégration, elle améliore aussi l'utilisation.

Pour installer Pandas, exécutez la commande suivante en ligne de commande :

pip install pandas

Cette commande va charger et installer Pandas avec toutes ses dépendances, comme NumPy. Une fois cette bibliothèque installée, nous pouvons commencer notre exploration.

Les structures de données Pandas peuvent contenir tout type d'éléments : Series, DataFrame et Panel. Le facteur commun c'est que les structures de données sont étiquetées.

Dans ce tutoriel, nous utiliserons surtout DataFrame, mais voici une brève présentation de ces structures de données :

- Series - un objet étiqueté en forme de tableau unidimensionnel, capable de contenir n'importe quel type d'objet ;

- DataFrame - une structure de données bidimensionnelle où les colonnes peuvent être de différents types ;

- Panel - une structure de données tridimensionnelle. Vous pouvez les considérer comme des dictionnaires de DataFrames.

II - Fonctionnalités de base

Commençons par découvrir les fonctionnalités de base de la bibliothèque 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

Dans l'exemple ci-dessus, nous avons créé un objet de type series qui contient plusieurs valeurs différentes. Un point intéressant à noter est que nous pouvons faire référence à des éléments de type not-a-number utilisant le symbole nan de NumPy qui nous indique que l'élément n'est pas un nombre, mais il peut être utilisé comme un type numérique. Le type des séries est un objet qui se réfère à ses contenus mixtes, car nous avons une chaîne incluse.

Si nous utilisons uniquement des types numériques, nous obtenons un dtype de base de 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 si les données intègrent un nan, la série sera traitée comme une série de nombres.

Mais le type qui nous intéresse n'est pas Series, mais DataFrames, car cela ressemble à un tableau à deux dimensions semblable à 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étrage par défaut affiche l'indice 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 voir, l'argument index fournit la liste à utiliser pour les lignes, alors que la liste fournie par l'argument columns peut être utilisée pour modifier les indices des colonnes.

Si nous avons des ensembles de données plus volumineux, la méthode head() peut aussi être utile. Elle affiche les n premières lignes fournies comme argument. Si vous ne fournissez pas d'argument, la valeur par défaut sera 5 :

>>> 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 affiche les n dernières lignes du DataFrame. Si vous omettez l'argument, la valeur par défaut utilisée sera 5 :

>>> 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 voir, les colonnes sont réparties sur plusieurs lignes si elles sont trop larges, mais cela ne nous dérangera pas pour la suite, car nous n'afficherons pas nos données sur la console, mais nous utiliserons des visualisations pour afficher le résultat.

III - Description des données

La fonction describe est la fonctionnalité que nous allons fréquemment utiliser pour analyser un nouvel ensemble de données  :

>>> 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 * 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 dans l'ensemble de données : le nombre d'éléments (count), leur moyenne, l'écart-type (std), les valeurs minimales et maximales et certaines autres valeurs.

IV - Indexation et partage

Pour sectionner une partie d'un Dataframe, il faut utiliser 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 implémenter les méthodes head() et tail() en utilisant 5 comme le nombre de lignes par défaut avec iloc aussi :

>>> 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 * 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 * 25 colonnes

Mais Pandas va plus loin et nous permet d'accéder aux données via des labels (étiquettes) dans DataFrames. Cet exemple n'est pas très spectaculaire, car les labels de l'ensemble des données sont identiques à celles de leur position. Cependant, en utilisant la méthode rename, nous pouvons facilement renommer les colonnes du DataFrame et nous pouvons utiliser ces indices 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

Le point le plus intéressant est la fonction lambda fournie pour les noms des colonnes. C'est parce que vous avez besoin soit d'un paramètre semblable à un dictionnaire, soit d'une fonction qui renomme les labels. La création d'un dictionnaire alourdirait considérablement l'exemple, si bien qu'une fonction anonyme simple est une solution idéale. La fonction chr() renvoie la représentation en caractères du nombre fourni, chr(65) est égal au 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 de 0 à 24.

Voilà quelque chose qui nous sera bien utile quand nous traiterons les données de fichiers CSV.

V - Lecture de fichiers CSV

À présent nous allons aborder certains exemples du monde réel. Nous utiliserons le même jeu de données que lors du dernier article sur NumPy. Si vous ne l'avez pas lu ou simplement ne voulez pas le chercher, le fichier est disponible au téléchargement : Les noms de bébé les plus fréquents selon le sexe et le groupe ethnique de la mère, New York City.

Je l'ai enregistré sous le nom baby_names.csv.

Il contient des prénoms donnés aux bébés de la ville de New York, selon le sexe et l'appartenance ethnique. Commençons par lire 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 fait 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 le format Strings. Cela signifie que les colonnes contenant des valeurs numériques ont des types numériques dans le DataFrame. Nous pouvons vérifier cela 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() sélectionne uniquement les colonnes qui ont des données numériques. Naturellement, certaines informations n'ont aucun sens, comme la colonne RNK (seul le count est utile) ou la moyenne et la fréquence dans BRTH_YR.

Vous avez peut-être remarqué que les colonnes du DataFrame contiennent 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'indice de la colonne à laquelle vous souhaitez accéder.

Les DataFrames peuvent être triés selon des colonnes ou des axes et la forme sera maintenue après le tri. Maintenant, nous avons un ensemble de données réel, alors essayons le tri. Voyons 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 ne tient compte que du minimum et du maximum sur la base des données de chaque année et ne regroupe pas les mêmes noms sur plusieurs années) :

>>> 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 voir, nous avons identifié un problème dans notre jeu de données : nous avons des entrées qui ont le même contenu. Cela rend nos données moins utilisables en l'état et nous devons les retraiter pour obtenir un résultat exploitable. Mais c'est une bonne chose en science des données et nous allons examiner une méthode de filtrage des doublons.

VI - Conclusion

Nous avons vu que la bibliothèque Pandas est la suite logique de NumPy dans l'analyse de données avec Python, car elle nous permet de gérer les données de manière optimale. Cependant, nous ne pouvons pas ignorer NumPy parce que Pandas s'appuie sur NumPy et Matplotlib pour nous donner un point de vue unique vous permettant d'effectuer votre analyse de données et votre visualisation. Pandas apporte vraiment un plus si nous importons des fichiers CSV avec un contenu mixte : vous n'avez pas à vous soucier des conversions.

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 Laethy pour la traduction de ce tutoriel, Lolo78 pour la revue de la traduction et Claude Leloup pour la correction orthographique.