4. Pandas
Python et son écosystème sont parfaitement adaptés au traitement des données (Data Science) au point d’en être devenu l’un des deux standards. L’autre étant le langage R issu du monde des statisticiens.
Le package de référence est Pandas.
Il s’installe avec la commande:
$ python -m pip install pandas
Ce qui va suivre est très largement inspiré de la documentation officielle. Il s’agit d’une introduction au concept de data frame dans l’environnement Python. Pour une documentation exhaustive, on consultera le User Guide.
Pour démarrer, la première chose à faire est d’importer le module pandas. L’alias pd
est traditionnellement utilisé:
import pandas as pd
4.1. Lecture / écriture de données
pandas
est capable de lire un grand nombre de données structurées avec la méthode read_*()
. L’écriture de données dans un fichier utilise la méthode to_*()
.
Pour cette découverte, nous allons travailler avec les Iris de Fisher. Le jeu de données comprend 50 échantillons de chacune des trois espèces d’iris (Iris setosa, Iris virginica et Iris versicolor). Quatre caractéristiques ont été mesurées à partir de chaque échantillon : la longueur et la largeur des sépales et des pétales.
Les données stockées au format csv sont disponibles dans le fichier iris.csv
. La lecture se fait avec la méthode read_csv()
qui prend en argument un fichier csv
et retourne une data frame:
>>> iris = pd.read_csv("iris.csv")
>>> type(iris)
<class 'pandas.core.frame.DataFrame'>
4.2. La DataFrame
Pandas permet l’utilisation de structures de données bi dimensionnelles, de type tableur. Ces structures s’appellent des DataFrame
.
La méthode describe()
renseigne sur les données contenues dans cette data frame. Les statistiques descriptives sont utilisées pour les données numériques:
>>> iris.describe()
sepal.length sepal.width petal.length petal.width
count 150.000000 150.000000 150.000000 150.000000
mean 5.843333 3.057333 3.758000 1.199333
std 0.828066 0.435866 1.765298 0.762238
min 4.300000 2.000000 1.000000 0.100000
25% 5.100000 2.800000 1.600000 0.300000
50% 5.800000 3.000000 4.350000 1.300000
75% 6.400000 3.300000 5.100000 1.800000
max 7.900000 4.400000 6.900000 2.500000
L’attribut values()
retourne l’ensemble structuré des valeurs de la data frame sous la forme d’un numpy.ndarray
.
>>> v = iris.values
>>> type(v)
<class 'numpy.ndarray'>
En utilisant l’opérateur d’indexation:
>>> v[0:10]
array([[5.1, 3.5, 1.4, 0.2, 'Setosa'],
[4.9, 3.0, 1.4, 0.2, 'Setosa'],
[4.7, 3.2, 1.3, 0.2, 'Setosa'],
[4.6, 3.1, 1.5, 0.2, 'Setosa'],
[5.0, 3.6, 1.4, 0.2, 'Setosa'],
[5.4, 3.9, 1.7, 0.4, 'Setosa'],
[4.6, 3.4, 1.4, 0.3, 'Setosa'],
[5.0, 3.4, 1.5, 0.2, 'Setosa'],
[4.4, 2.9, 1.4, 0.2, 'Setosa'],
[4.9, 3.1, 1.5, 0.1, 'Setosa']], dtype=object)
L’attribut dtypes()
renseigne sur le type des données contenues dans la data frame.
>>> iris.dtypes
sepal.length float64
sepal.width float64
petal.length float64
petal.width float64
variety object
dtype: object
L’attribut axes()
contient la nature et la dimension des axes de la data frame.
>>> iris.axes
[RangeIndex(start=0, stop=150, step=1), Index(['sepal.length', 'sepal.width', 'petal.length', 'petal.width',
'variety'],
dtype='object')]
Les attributs ndim()
, size()
et shape()
renseignent également sur la structure de la dataframe.
>>> iris.ndim, iris.size, iris.shape
(2, 750, (150, 5))
4.2.1. Création
Les données sont le plus souvent contenues dans des fichiers externes que l’on lit avec la méthode read_*()
. Il est quelquefois nécessaire de créer la data frame à partir de données calculées.
Une façon simple est d’utiliser le constructeur auquel on passe un dictionnaire pour lequel:
les clés sont les variables d’observation
les valeurs sont les observations structurées sous forme de séquences.
>>> df = pd.DataFrame({ ... "Name": ["Braund, Mr. Owen Harris", ... "Allen, Mr. William Henry", ... "Bonnell, Miss. Elizabeth"], ... "Age": [22, 35, 58], ... "Sex": ["male", "male", "female"]} ... )
>>> df Name Age Sex 0 Braund, Mr. Owen Harris 22 male 1 Allen, Mr. William Henry 35 male 2 Bonnell, Miss. Elizabeth 58 female
La variable entière qui précède chaque ligne est appelée index
ou label
.
4.3. Les Series
Dans la terminologie Pandas, chaque colonne est une Series
et est accessible avec l’opérateur d’indexation. Contrairement à une simple liste Python, une Series
est une structure de données unidimensionnelle pour laquelle tous les éléments ont le même type (dtype
est unique: int64, float64, object, …), ce qui rend les opérations d’accès et de calcul très efficaces. En fait Pandas est construit sur le module Numpy.
Une data frame peut être vue comme une juxtaposition de Series
de même dimension.
Extrayons une colonne de la data frame iris
.
>>> pl = iris["petal.length"]
>>> pl
0 1.4
1 1.4
2 1.3
3 1.5
4 1.4
...
145 5.2
146 5.0
147 5.2
148 5.4
149 5.1
Name: petal.length, Length: 150, dtype: float64
La colonne est bien de type Series
.
>>> type(pl)
<class 'pandas.core.series.Series'>
Une Series
est un objet à part entière et, comme la data frame peut être éventuellement créé avec le constructeur.
>>> ages = pd.Series([22, 35, 58], name="Age")
>>> ages
0 22
1 35
2 58
Name: Age, dtype: int64
>>> type(ages)
<class 'pandas.core.series.Series'>
4.3.1. Les attributs
Les attributs renseignent sur la taille, le type, le contenu, etc… de la Series
.
>>> pl.size, pl.dtype
(150, dtype('float64'))
>>> pl.values
array([1.4, 1.4, 1.3, 1.5, 1.4, 1.7, 1.4, 1.5, 1.4, 1.5, 1.5, 1.6, 1.4,
1.1, 1.2, 1.5, 1.3, 1.4, 1.7, 1.5, 1.7, 1.5, 1. , 1.7, 1.9, 1.6,
1.6, 1.5, 1.4, 1.6, 1.6, 1.5, 1.5, 1.4, 1.5, 1.2, 1.3, 1.4, 1.3,
1.5, 1.3, 1.3, 1.3, 1.6, 1.9, 1.4, 1.6, 1.4, 1.5, 1.4, 4.7, 4.5,
4.9, 4. , 4.6, 4.5, 4.7, 3.3, 4.6, 3.9, 3.5, 4.2, 4. , 4.7, 3.6,
4.4, 4.5, 4.1, 4.5, 3.9, 4.8, 4. , 4.9, 4.7, 4.3, 4.4, 4.8, 5. ,
4.5, 3.5, 3.8, 3.7, 3.9, 5.1, 4.5, 4.5, 4.7, 4.4, 4.1, 4. , 4.4,
4.6, 4. , 3.3, 4.2, 4.2, 4.2, 4.3, 3. , 4.1, 6. , 5.1, 5.9, 5.6,
5.8, 6.6, 4.5, 6.3, 5.8, 6.1, 5.1, 5.3, 5.5, 5. , 5.1, 5.3, 5.5,
6.7, 6.9, 5. , 5.7, 4.9, 6.7, 4.9, 5.7, 6. , 4.8, 4.9, 5.6, 5.8,
6.1, 6.4, 5.6, 5.1, 5.6, 6.1, 5.6, 5.5, 4.8, 5.4, 5.6, 5.1, 5.1,
5.9, 5.7, 5.2, 5. , 5.2, 5.4, 5.1])
4.3.2. Les opérations vectorisées
pandas
autorise la « vectorisation » des opérations sur les séquences, ce qui assure:
une écriture compacte et lisible
une grande efficacité algorithmique
Une liste exhaustive de ce type d’opération ici.
Pour illustrer le concept d’opération vectorisée, on souhaite normaliser les données (cm) pour les rendres compatibles avec le Système International, il faut les passer en mètres. La vectorisation évite d’itérer sur l’ensemble des éléments.
>>> pl_m = pl.mul(0.01)
>>> pl_m
0 0.014
1 0.014
2 0.013
3 0.015
4 0.014
...
145 0.052
146 0.050
147 0.052
148 0.054
149 0.051
Name: petal.length, Length: 150, dtype: float64
4.3.3. La conversion de type
La lecture de données hétérogènes conduit parfois à un type qui n’est pas représentatif de la nature de ces données. Un cas courant est de récupérer des données numériques sous la forme de chaines de caractères qui ne se prêtent donc pas aux opérations numériques.
Pour pandas
, une chaine de caractères est de type object
.
>>> ages = pd.Series(["22", "35", "58"], name="Age")
>>> ages
0 22
1 35
2 58
Name: Age, dtype: object
Les données ont été lues comme des chaînes de caractères et ne sont pas éligibles à un traitement numérique. Pire, l’opération peut se dérouler sans erreur et retourner une valeur aberrante :
>>> ages.mean()
74519.33333333333
Avant traitement, il faut donc convertir le type object
en un type numérique.
Les opérations de conversion sont décrites ici.
>>> ages = ages.astype('int64')
>>> ages
0 22
1 35
2 58
Name: Age, dtype: int64
Le type des données a bien été modifié et maintenant les opérations numériques sont légitimes:
>>> ages.mean()
38.333333333333336
4.3.4. Indexation et itération
L’indexation est l’opération qui permet de sélectionner un sous ensemble des données d’une Series
. L’itération est l’opération qui permet de parcourir tout ou partie des éléments d’une Series
.
Les opérations d’indexation et d’itération sont décrites ici.
>>> pl.at[100]
6.0
Pour cet exemple les index et les positions se confondent et les opérations de type at()
ou iat()
conduisent au même résultat.
On peut également accéder à un sous ensemble de valeurs. Attention, la convention de slice utilisée ici est différente de celle de Python : le dernier indice est inclus.
>>> pl.loc[40:50]
40 1.3
41 1.3
42 1.3
43 1.6
44 1.9
45 1.4
46 1.6
47 1.4
48 1.5
49 1.4
50 4.7
Name: petal.length, dtype: float64
items()
retourne un objet zip
constitué de paires (index, value) sur lequel on peut itérer:
>>> for index, value in pl.loc[40:50].items():
... print(index, '-', value)
...
40 - 1.3
41 - 1.3
42 - 1.3
43 - 1.6
44 - 1.9
45 - 1.4
46 - 1.6
47 - 1.4
48 - 1.5
49 - 1.4
50 - 4.7
keys()
retourne la séquence d’index.
>>> pl.keys()
RangeIndex(start=0, stop=150, step=1)
4.3.5. Application d’une fonction
apply()
permet d’appliquer la fonction passée en argument à chaque élément de la Series
.
>>> import math
>>> pl.loc[40:50].apply(math.sqrt)
40 1.140175
41 1.140175
42 1.140175
43 1.264911
44 1.378405
45 1.183216
46 1.264911
47 1.183216
48 1.224745
49 1.183216
50 2.167948
Name: petal.length, dtype: float64
4.3.6. Maths & Stats
La vectorisation est disponible pour les opérations mathématiques et statistiques.
Les opérations mathématiques et statistiques sont décrites ici.
>>> pl.sum()
563.7
>>> pl.cumsum()
0 1.4
1 2.8
2 4.1
3 5.6
4 7.0
...
145 543.0
146 548.0
147 553.2
148 558.6
149 563.7
Name: petal.length, Length: 150, dtype: float64
>>> pl.max(), pl.min()
(6.9, 1.0)
>>> pl.mean(), pl.median()
(3.7580000000000005, 4.35)
4.4. Sélection de données
Le traitement des données implique des mécanismes de sélection d’un sous ensemble de celles ci. Une information complète sur ce mécanisme est fournie ici.
4.4.1. Sélection de variables
Il s’agit ici de sélectionner une ou plusieurs variables (les colonnes de la dataframe) pour l’ensemble des observations.
La sélection par colonne utilise l’opérateur d’indexation. On passe le nom de colonne (ou la séquence de noms) en argument.
>>> iris["petal.length"]
0 1.4
1 1.4
2 1.3
3 1.5
4 1.4
...
145 5.2
146 5.0
147 5.2
148 5.4
149 5.1
Name: petal.length, Length: 150, dtype: float64
>>> iris[ ["petal.length", "petal.width"]]
petal.length petal.width
0 1.4 0.2
1 1.4 0.2
2 1.3 0.2
3 1.5 0.2
4 1.4 0.2
.. ... ...
145 5.2 2.3
146 5.0 1.9
147 5.2 2.0
148 5.4 2.3
149 5.1 1.8
[150 rows x 2 columns]
On peut forger une séquence de noms de colonnes avec un prédicat. La list comprehension
de Python est particulièrement efficace :
>>> c = [ name for name in iris.columns if "petal" in name ] >>> c ['petal.length', 'petal.width']>>> iris[c] petal.length petal.width 0 1.4 0.2 1 1.4 0.2 2 1.3 0.2 3 1.5 0.2 4 1.4 0.2 .. ... ... 145 5.2 2.3 146 5.0 1.9 147 5.2 2.0 148 5.4 2.3 149 5.1 1.8[150 rows x 2 columns]
4.4.2. Sélection d’observations
Il s’agit ici de sélectionner les observations (les lignes de la dataframe) qui correspondent à un ou plusieurs critères sur les variables.
L’opérateur d’indexation est utilisé. Pour une sélection inconditionnelle, on passe le range d’index en argument.
>>> iris[::2] sepal.length sepal.width petal.length petal.width variety 0 5.1 3.5 1.4 0.2 Setosa 2 4.7 3.2 1.3 0.2 Setosa 4 5.0 3.6 1.4 0.2 Setosa 6 4.6 3.4 1.4 0.3 Setosa 8 4.4 2.9 1.4 0.2 Setosa .. ... ... ... ... ... 140 6.7 3.1 5.6 2.4 Virginica 142 5.8 2.7 5.1 1.9 Virginica 144 6.7 3.3 5.7 2.5 Virginica 146 6.3 2.5 5.0 1.9 Virginica 148 6.2 3.4 5.4 2.3 Virginica[75 rows x 5 columns]
Pour une sélection conditionnelle, on passe un prédicat en argument. Ici on sélectionne toutes les observations (les lignes) pour lesquelles la longueur du pétale est supérieure à 6
:
>>> iris[ iris["petal.length"] > 6 ]
sepal.length sepal.width petal.length petal.width variety
105 7.6 3.0 6.6 2.1 Virginica
107 7.3 2.9 6.3 1.8 Virginica
109 7.2 3.6 6.1 2.5 Virginica
117 7.7 3.8 6.7 2.2 Virginica
118 7.7 2.6 6.9 2.3 Virginica
122 7.7 2.8 6.7 2.0 Virginica
130 7.4 2.8 6.1 1.9 Virginica
131 7.9 3.8 6.4 2.0 Virginica
135 7.7 3.0 6.1 2.3 Virginica
Un prédicat complexe peut être utilisé. Ici on sélectionne toutes les observations (les lignes) pour lesquelles la longueur du pétale est supérieure à 6
et la largeur du sépale inférieure à 3
:
>>> iris[ (iris["petal.length"] > 6) & (iris["sepal.width"] < 3) ]
sepal.length sepal.width petal.length petal.width variety
107 7.3 2.9 6.3 1.8 Virginica
118 7.7 2.6 6.9 2.3 Virginica
122 7.7 2.8 6.7 2.0 Virginica
130 7.4 2.8 6.1 1.9 Virginica
On peut imaginer grouper la sélection des lignes et des colonnes en une seule opération. Mais attention, le faire sans précaution peut conduire à un message d’alerte, même si le résultat est correct:
>>> iris.loc[110:130][ iris["petal.length"] > 6 ]
__main__:1: UserWarning: Boolean Series key will be reindexed to match DataFrame index.
sepal.length sepal.width petal.length petal.width variety
117 7.7 3.8 6.7 2.2 Virginica
118 7.7 2.6 6.9 2.3 Virginica
122 7.7 2.8 6.7 2.0 Virginica
130 7.4 2.8 6.1 1.9 Virginica
L’origine de ce warning est que les tailles des structures de données mises en jeu diffèrent.
L’expression iris["petal.length"] > 6
est un masque booléen de taille la longueur de la data frame iris
:
>>> mask = iris["petal.length"] > 6
>>> mask.dtype
dtype('bool')
>>> len(mask)
150
L’expression iris.loc[110:130]
est un sous ensemble (de taille différente) de la data frame iris
:
>>> len(iris.loc[110:130])
21
On préfèrera traiter le problème en séparant distinctement les deux opérations
>>> subset = iris.loc[110:130]
>>> subset[subset["petal.length"] > 6]
sepal.length sepal.width petal.length petal.width variety
117 7.7 3.8 6.7 2.2 Virginica
118 7.7 2.6 6.9 2.3 Virginica
122 7.7 2.8 6.7 2.0 Virginica
130 7.4 2.8 6.1 1.9 Virginica
4.4.3. La méthode query()
La sélection d’observations est parfaitement réalisée avec l’opérateur d’indexation comme on l’a vu précédemment. Mais la syntaxe est lourde :
>>> iris[ (iris["petal.length"] > 6) & (iris["sepal.width"] < 3) ]
sepal.length sepal.width petal.length petal.width variety
107 7.3 2.9 6.3 1.8 Virginica
118 7.7 2.6 6.9 2.3 Virginica
122 7.7 2.8 6.7 2.0 Virginica
130 7.4 2.8 6.1 1.9 Virginica
On lui préférera l’utilisation de la méthode query()
à laquelle on passe le prédicat exprimé dans une chaîne de caractères.
La chaine de caractère passée en argument est interprétée comme une expression Python. Ici le nom des variables originales est un problème puisque le .
séparateur a une signification en Python. Evaluer l’expression petal.length > 6
signifierait un accès à l’attribut length
de l’objet petal
et conduirait à une erreur.
Renommons les variables (les colonnes) de façon non ambigües :
>>> iris.rename(columns={ "sepal.length": "sepal_length",
... "sepal.width": "sepal_width",
... "petal.length": "petal_length",
... "petal.width": "petal_width"},
inplace = true)
Le paramètre inplace
permet de créer une nouvelle DataFrame
lorsqu’il est False
. C’est la valeur par défaut. S’il est True
la DataFrame
sur laquelle la méthode query()
est appelée est modifiée.
>>> iris sepal_length sepal_width petal_length petal_width variety 0 5.1 3.5 1.4 0.2 Setosa 1 4.9 3.0 1.4 0.2 Setosa 2 4.7 3.2 1.3 0.2 Setosa 3 4.6 3.1 1.5 0.2 Setosa 4 5.0 3.6 1.4 0.2 Setosa .. ... ... ... ... ... 145 6.7 3.0 5.2 2.3 Virginica 146 6.3 2.5 5.0 1.9 Virginica 147 6.5 3.0 5.2 2.0 Virginica 148 6.2 3.4 5.4 2.3 Virginica 149 5.9 3.0 5.1 1.8 Virginica[150 rows x 5 columns]
La sélection d’observation est maintenant plus compacte, donc plus lisible :
>>> iris.query(" petal_length > 6 & sepal_width < 3")
sepal_length sepal_width petal_length petal_width variety
107 7.3 2.9 6.3 1.8 Virginica
118 7.7 2.6 6.9 2.3 Virginica
122 7.7 2.8 6.7 2.0 Virginica
130 7.4 2.8 6.1 1.9 Virginica
Par la suite on privilégiera donc la méthode query()
à l’opérateur d’indexation.
Note
l’utilisation de variables est autorisée à l’intérieur de la chaine qui exprime la requête en préfixant celle ci de @
.
4.5. Création de colonne
Il est parfois nécessaire de construire des données additionnelles à celles lues avec la méthode read_*()
et d’intégrer celles ci à la data frame initiale. Ici encore l’opérateur d’indexation est utilisé.
A titre d’exemple on peut ajouter un rapport de forme pour les pétales et les sépales :
>>> iris["sepal_rf"] = iris["sepal_length"] / iris["sepal_width"] >>> iris sepal_length sepal_width petal_length petal_width variety sepal_rf 0 5.1 3.5 1.4 0.2 Setosa 1.457143 1 4.9 3.0 1.4 0.2 Setosa 1.633333 2 4.7 3.2 1.3 0.2 Setosa 1.468750 3 4.6 3.1 1.5 0.2 Setosa 1.483871 4 5.0 3.6 1.4 0.2 Setosa 1.388889 .. ... ... ... ... ... ... 145 6.7 3.0 5.2 2.3 Virginica 2.233333 146 6.3 2.5 5.0 1.9 Virginica 2.520000 147 6.5 3.0 5.2 2.0 Virginica 2.166667 148 6.2 3.4 5.4 2.3 Virginica 1.823529 149 5.9 3.0 5.1 1.8 Virginica 1.966667[150 rows x 6 columns]
On peut également renommer une ou plusieurs colonnes avec la méthode rename()
.
4.6. Statistiques descriptives
Les méthodes statistiques évoquées pour les Series
s’appliquent évidemment aux data frames.
Lorsqu’on sélectionne une seule colonne, on retrouve le comportement évoqué plus haut :
>>> iris["petal_length"].mean()
3.7580000000000005
Lorsqu’on sélectionne plusieurs colonnes, la méthode statistique est appliquée à chacune :
>>> iris[ ["petal_length", "petal_width"] ].mean()
petal_length 3.758000
petal_width 1.199333
dtype: float64
Rappelons que la méthode describe()
applique un ensemble de méthodes statistiques sur les valeurs numériques d’une data frame, ou d’une portion de cette data frame.
>>> iris[ ["petal_length", "petal_width"] ].describe()
petal_length petal_width
count 150.000000 150.000000
mean 3.758000 1.199333
std 1.765298 0.762238
min 1.000000 0.100000
25% 1.600000 0.300000
50% 4.350000 1.300000
75% 5.100000 1.800000
max 6.900000 2.500000
La méthode agg()
permet de personnaliser les opérations appliquées sur chacune des colonnes :
>>> iris.agg( { "petal_length" : [ 'min', 'max'],
... "petal_width" : [ 'mean', 'median']})
petal_length petal_width
max 6.9 NaN
mean NaN 1.199333
median NaN 1.300000
min 1.0 NaN
On note ici l’utilisation de NaN
(Not a Number) lorsque les résultats ne sont pas disponibles. Il est employé par exemple lorsqu’une donnée est manquante. NaN
fait l’objet d’un traitement particulier dans les opérations sur les data frames. On peut par exemple exclure les observations qui contiennent des données manquantes avec la méthode dropna()
.
4.7. Groupement par catégories
Le dataset iris
contient des données pour trois catégories : Setosa, Versicolor et Virginica. Il est intéressant de disposer d’un mécanisme permettant de faire aisément quelques opérations statistiques sur des groupements de données.
On utilise pour cela la méthode groupby()
en lui passant en argument la variable catégorielle utilisée pour le groupement. Si l’argument est une séquence de variables catégorielles, toutes les combinaisons de groupements sont générées.
>>> iris.groupby("variety").mean()
sepal_length sepal_width petal_length petal_width sepal_rf
variety
Setosa 5.006 3.428 1.462 0.246 1.470188
Versicolor 5.936 2.770 4.260 1.326 2.160402
Virginica 6.588 2.974 5.552 2.026 2.230453
La méthode groupby()
regroupe en fait trois opérations courantes dans le traitement des données (split-apply-combine):
Split sépare les données en sous groupes
Apply applique une fonction indépendamment à chaque groupe
Combine regroupe les résultats dans une data frame
On peut évidemment sélectionner un sous ensemble des données produites par groupby()
avant application de la méthode statistique :
>>> iris.groupby("variety")[ ["petal_length", "petal_width"] ].mean()
petal_length petal_width
variety
Setosa 1.462 0.246
Versicolor 4.260 1.326
Virginica 5.552 2.026
4.8. Compter le nombre d’observations
Il est souvent nécessaires de dénombrer le nombre d’observations relatives à une catégorie.
La méthode value_counts()
est utilisée.
>>> iris["variety"].value_counts()
Setosa 50
Virginica 50
Versicolor 50
Name: variety, dtype: int64
4.9. Restructurer la data frame
Pour faciliter le traitement, il est parfois utile de modifier l’ordonnancement des observations dans une data frame ou de changer sa structure.
La méthode sort_values()
peut être utilisée pour modifier l’ordonnancement sans modifier la structure.
>>> iris.sort_values(by="sepal_width") sepal_length sepal_width petal_length petal_width variety sepal_rf 60 5.0 2.0 3.5 1.0 Versicolor 2.500000 62 6.0 2.2 4.0 1.0 Versicolor 2.727273 119 6.0 2.2 5.0 1.5 Virginica 2.727273 68 6.2 2.2 4.5 1.5 Versicolor 2.818182 41 4.5 2.3 1.3 0.3 Setosa 1.956522 .. ... ... ... ... ... ... 16 5.4 3.9 1.3 0.4 Setosa 1.384615 14 5.8 4.0 1.2 0.2 Setosa 1.450000 32 5.2 4.1 1.5 0.1 Setosa 1.268293 33 5.5 4.2 1.4 0.2 Setosa 1.309524 15 5.7 4.4 1.5 0.4 Setosa 1.295455[150 rows x 6 columns]
On peut vouloir également modifier la structure de la data frame. Il existe deux formats de data frame.
4.9.1. Format long
Le format long
présente les données avec une seule observation par ligne. Il est bien adapté pour le traitement automatisé mais n’est pas très pratique pour l’affichage car il contient un nombre de lignes plus important que le format wide
utilisé jusqu’à présent dans ce cours.
Ce format est requis par certains packages de visualisation pour faciliter l’affichage.
Le passage du format wide
au format long
se fait avec la fonction melt()
en passant dans l’argument id_vars
le nom de la colonne à inclure. Les autres colonnes fournissent les paires (variable, value).
Cette opération crée un nouvel index et la relation entre les 4 variables et le specimen de fleur est perdu. La taille de la data frame créée valide la procédure.
>>> iris_long = pd.melt(iris, id_vars=['variety']) >>> iris_long variety variable value 0 Setosa sepal_length 5.100000 1 Setosa sepal_length 4.900000 2 Setosa sepal_length 4.700000 3 Setosa sepal_length 4.600000 4 Setosa sepal_length 5.000000 .. ... ... ... 745 Virginica sepal.rf 2.233333 746 Virginica sepal.rf 2.520000 747 Virginica sepal.rf 2.166667 748 Virginica sepal.rf 1.823529 749 Virginica sepal.rf 1.966667[750 rows x 3 columns]
On peut souhaiter conserver l’index initial comme une variable d’observation et conserver la relation entre les 4 variables de taille.
>>> iris_long = pd.melt(iris.reset_index(), id_vars=['index']) >>> iris_long index variable value 0 0 sepal_length 5.1 1 1 sepal_length 4.9 2 2 sepal_length 4.7 3 3 sepal_length 4.6 4 4 sepal_length 5 .. ... ... ... 895 145 sepal_rf 2.23333 896 146 sepal_rf 2.52 897 147 sepal_rf 2.16667 898 148 sepal_rf 1.82353 899 149 sepal_rf 1.96667[900 rows x 3 columns]
4.9.2. Format wide
Le format wide
présente les données avec plusieurs observations par ligne. C’est le cas du dataset initial iris
. C’est une présentation compacte adaptée à la présentation textuelle des données.
Le passage du format long
au format wide
se fait avec la méthode pivot()
en passant dans l’argument index
le nom de la colonne utilisée comme index, dans l’argument columns
le nom des colonnes à créer et dans l’argument values
le nom de la colonne contenant les valeurs.
>>> iris_long.pivot(index='index', columns="variable", values="value") variable petal_length petal_width sepal_length sepal_rf sepal_width variety index 0 1.4 0.2 5.1 1.45714 3.5 Setosa 1 1.4 0.2 4.9 1.63333 3 Setosa 2 1.3 0.2 4.7 1.46875 3.2 Setosa 3 1.5 0.2 4.6 1.48387 3.1 Setosa 4 1.4 0.2 5 1.38889 3.6 Setosa ... ... ... ... ... ... ... 145 5.2 2.3 6.7 2.23333 3 Virginica 146 5 1.9 6.3 2.52 2.5 Virginica 147 5.2 2 6.5 2.16667 3 Virginica 148 5.4 2.3 6.2 1.82353 3.4 Virginica 149 5.1 1.8 5.9 1.96667 3 Virginica[150 rows x 6 columns]