Dictionnaires et Pandas#
Vidéo: Dictionnaires#
Les dictionnaires#
Un dictionnaire ressemble à une liste mais il est plus général. Alors que les indices d’une liste doivent être des entiers, dans un dictionnaire, les indices peuvent être de types différents.
Un dictionnaire contient une collection d’indices, appelés clés, et une collection de valeurs. À chaque clé correspond une valeur, ce que l’on appelle une paire clé-valeur, ou item.
La syntaxe pour définir un dictionnaire est :
my_dict = {
"key1": "value1",
"key2": "value2",
}
Définissons 2 listes nous donnant les pays européens et leur capitale :
pays = ['allemagne', 'autriche', 'belgique', 'bulgarie', 'chypre', 'croatie', 'danemark', 'espagne', 'estonie',
'finlande', 'france', 'grèce', 'hongrie', 'irlande', 'italie', 'lettonie', 'lituanie', 'luxembourg',
'malte', 'pays-bas', 'pologne', 'portugal', 'république tchèque', 'roumanie', 'slovaquie', 'slovénie', 'suède']
capitales = ['berlin', 'vienne', 'bruxelles', 'sofia', 'nicosie', 'zagreb', 'copenhague', 'madrid', 'tallinn',
'helsinki', 'paris', 'athènes', 'budapest', 'dublin', 'rome', 'riga', 'vilnius', 'luxembourg', 'la valette',
'amsterdam', 'varsovie', 'lisbonne', 'prague', 'bucarest', 'bratislava', 'ljubljana', 'stockholm']
Nous voulons trouver la capitale de la Hongrie. Nous pouvons utiliser la méthode de liste index()
:
# Trouver l'indice de 'hongrie'
ind_hongrie = pays.index('hongrie')
# Afficher la capitale de la Hongrie
print(capitales[ind_hongrie].capitalize())
Budapest
Maintenant, définissons un dictionnaire contenant les même informations :
europe = {
'allemagne' : 'berlin',
'autriche' : 'vienne',
'belgique' : 'bruxelles',
'bulgarie' : 'sofia',
'chypre' : 'nicosie',
'croatie' : 'zagreb',
'danemark' : 'copenhague',
'espagne' : 'madrid',
'estonie' : 'tallinn',
'finlande' : 'helsinki',
'france' : 'paris',
'grèce' : 'athènes',
'hongrie' : 'budapest',
'irlande' : 'dublin',
'italie' : 'rome',
'lettonie' : 'riga',
'lituanie' : 'vilnius',
'luxembourg' : 'luxembourg',
'malte' : 'la valette',
'pays-bas' : 'amsterdam',
'pologne' : 'varsovie',
'portugal' : 'lisbonne',
'république tchèque' : 'prague',
'roumanie' : 'bucarest',
'slovaquie' : 'bratislava',
'slovénie' : 'ljubljana',
'suède' : 'stockholm'
}
Pour accéder à la valeur associée à une clé donnée, il suffit alors d’utiliser la clé en indice du dictionnaire. Ainsi, pour afficher la capitale de la Hongrie :
europe['hongrie']
'budapest'
Il est facile avec un dictionnaire d’ajouter ou enlever une paire clé-valeur, ou bien de changer une valeur :
# Ajouter la Turqie au dictionnaire
europe['turquie'] = 'istanbul'
# Vérifier que le pays est bien contenu dans le dictionnaire
'turquie' in europe
True
# Changer le nom de la capitale de la Turquie (et oui ce n'est pas Istanbul...)
europe['turquie'] = 'ankara'
# Afficher la capitale de la Turquie
print(europe['turquie'].capitalize())
Ankara
# Enlever la paire clé-valeur du dictionnaire
del(europe['turquie'])
# Vérifier que le pays n'est pas contenu dans le dictionnaire
'turquie' in europe
False
Vidéo: L’objet DataFrame#
Pandas DataFrames#
Pandas est un module de Python pour l’analyse et la manipulation de données. L’objet de base de Pandas est le DataFrame, qui est un tableau similaire aux feuilles Excel ou LibreOffice que vous devez connaître. Le module Pandas fonctionne en harmonie avec les modules Numpy et Matplotlib vus dans les cours précédents.
Voyons comment créer un DataFrame à partir d’un dictionnaire. Nous allons reprendre les listes créées plus haut sur les pays et capitales européennes, les transformer en tableaux Numpy, et y ajouter d’autres informations :
# Importation du module Numpy
import numpy as np
# Importation du module Pandas
import pandas as pd
# Création des tableaux Numpy à partir des listes des pays et des capitales : type string
np_pays = np.array(pays)
np_capitales = np.array(capitales)
# Liste avec les populations des pays européens : type integer
np_population = np.array([82162000, 8700471, 11289853, 7153784, 848319, 4190669, 5659715, 46438422, 1315944,
5401267, 66661621, 10793526, 9830485, 4658530, 60665551, 1968957, 2888558, 576249,
434403, 16979120, 37967209, 10341330, 10553853, 19759968, 5426252, 2064188, 9851017],
dtype = 'int64')
# Liste avec les dates d'adhésion des pays à l'UE : type datetime
np_date = np.array(['1957', '1995', '1957', '2007', '2004', '2013', '1973', '1986', '2004', '1995',
'1957', '1981', '2004', '1973', '1957', '2004', '2004', '1957', '2004', '1957',
'2004', '1986', '2004', '2007', '2004', '2004', '1995'],
dtype = 'datetime64[Y]')
Maintenant, créons un dictionnaire en utilisant les tableaux Numpy dans les paires clé-valeur :
dic_europe = {
"pays" : np_pays,
"capitale" : np_capitales,
"population" : np_population,
"date d'adhésion" : np_date
}
Pour créer le DataFrame à partir du dictionnaire il suffit d’utiliser la fonction DataFrame()
de Pandas :
# Création du DataFrame
df_europe = pd.DataFrame(data = dic_europe)
# Affichage du DataFrame
df_europe
pays | capitale | population | date d'adhésion | |
---|---|---|---|---|
0 | allemagne | berlin | 82162000 | 1957-01-01 |
1 | autriche | vienne | 8700471 | 1995-01-01 |
2 | belgique | bruxelles | 11289853 | 1957-01-01 |
3 | bulgarie | sofia | 7153784 | 2007-01-01 |
4 | chypre | nicosie | 848319 | 2004-01-01 |
5 | croatie | zagreb | 4190669 | 2013-01-01 |
6 | danemark | copenhague | 5659715 | 1973-01-01 |
7 | espagne | madrid | 46438422 | 1986-01-01 |
8 | estonie | tallinn | 1315944 | 2004-01-01 |
9 | finlande | helsinki | 5401267 | 1995-01-01 |
10 | france | paris | 66661621 | 1957-01-01 |
11 | grèce | athènes | 10793526 | 1981-01-01 |
12 | hongrie | budapest | 9830485 | 2004-01-01 |
13 | irlande | dublin | 4658530 | 1973-01-01 |
14 | italie | rome | 60665551 | 1957-01-01 |
15 | lettonie | riga | 1968957 | 2004-01-01 |
16 | lituanie | vilnius | 2888558 | 2004-01-01 |
17 | luxembourg | luxembourg | 576249 | 1957-01-01 |
18 | malte | la valette | 434403 | 2004-01-01 |
19 | pays-bas | amsterdam | 16979120 | 1957-01-01 |
20 | pologne | varsovie | 37967209 | 2004-01-01 |
21 | portugal | lisbonne | 10341330 | 1986-01-01 |
22 | république tchèque | prague | 10553853 | 2004-01-01 |
23 | roumanie | bucarest | 19759968 | 2007-01-01 |
24 | slovaquie | bratislava | 5426252 | 2004-01-01 |
25 | slovénie | ljubljana | 2064188 | 2004-01-01 |
26 | suède | stockholm | 9851017 | 1995-01-01 |
On voit que :
les clés du dictionnaire ont été utilisées pour nommer les colonnes du DataFrame,
les listes ont été utilisées pour remplir chaque colonne
les lignes sont étiquetées, par défaut avec des entiers à partir de l’indice 0 (mais il est possible d’utiliser d’autres types pour les étiquettes, comme pour les clés d’un dictionnaire, ce que nous verrons au paragraphe sur l’objet index)
Nous pouvons vérifier le type du DataFrame avec la fonction type()
:
# Affichage du type
print(type(df_europe))
<class 'pandas.core.frame.DataFrame'>
Les attributs de DataFrame sont :
Attributs |
Description |
---|---|
|
nombre de dimensions |
|
forme, nombre d’éléments dans chaque dimension |
|
nombre d’éléments |
print('ndim :', df_europe.ndim)
print('shape :', df_europe.shape)
print('size :', df_europe.size)
ndim : 2
shape : (27, 4)
size : 108
Les DataFrame contiennent parfois beaucoup de lignes. On peut afficher seulement les premières ou dernières lignes avec les méthodes head()
et tail()
:
# Afficher les 2 premières lignes
df_europe.head(n = 2)
pays | capitale | population | date d'adhésion | |
---|---|---|---|---|
0 | allemagne | berlin | 82162000 | 1957-01-01 |
1 | autriche | vienne | 8700471 | 1995-01-01 |
# Afficher les 2 dernières lignes
df_europe.tail(n = 2)
pays | capitale | population | date d'adhésion | |
---|---|---|---|---|
25 | slovénie | ljubljana | 2064188 | 2004-01-01 |
26 | suède | stockholm | 9851017 | 1995-01-01 |
Vidéo: Les objets Series et Index#
L’objet Series#
Il est possible d’extraire une colonne en mettant en indice le nom de la colonne :
# Extraire la colonne pays du DataFrame
col_pays = df_europe['pays']
# Ou de manière similaire
col_pays = df_europe.pays
# Affichage
print(col_pays)
0 allemagne
1 autriche
2 belgique
3 bulgarie
4 chypre
5 croatie
6 danemark
7 espagne
8 estonie
9 finlande
10 france
11 grèce
12 hongrie
13 irlande
14 italie
15 lettonie
16 lituanie
17 luxembourg
18 malte
19 pays-bas
20 pologne
21 portugal
22 république tchèque
23 roumanie
24 slovaquie
25 slovénie
26 suède
Name: pays, dtype: object
On voit que l’on a extrait la colonne mais aussi les étiquettes des lignes et d’autres informations telles que l’étiquette de la colonne. Une colonne extraite de cette manière est un objet Series de Pandas :
print(type(col_pays))
<class 'pandas.core.series.Series'>
On accède aux valeurs d’un objet Series de la même manière qu’un dictionnaire, c’est-à-dire en écrivant l’étiquette en indice de l’objet :
# Afficher la valeur de la "Series" col_pays correspondant a l'étiquette 23
print(col_pays[23])
roumanie
Pour accéder à une ligne, il faut utiliser un accesseur .loc[]
:
# Extraire la ligne 23 du DataFrame
ligne_23 = df_europe.loc[23]
# Affichage
print(ligne_23)
pays roumanie
capitale bucarest
population 19759968
date d'adhésion 2007-01-01 00:00:00
Name: 23, dtype: object
Une ligne extraite de cette façon est aussi un objet Series :
type(ligne_23)
pandas.core.series.Series
Les noms des colonnes ont été utilisés pour étiqueter les lignes de ce nouvel objet, et l’étiquette de la ligne a été utilisée pour nommer l’objet.
Exercice#
Créer un objet Series contenant la colonne date d’adhésion
Créer un objet Series contenant la ligne 12
Show code cell source
# 1
col_adhesion = df_europe["date d'adhésion"]
# 2
ligne_12 = df_europe.loc[12]
L’objet Index#
On peut extraire les étiquettes des lignes et des colonnes avec .index
et .columns
:
# Étiquettes des lignes
print(df_europe.index)
RangeIndex(start=0, stop=27, step=1)
# Étiquette des lignes
print(df_europe.columns)
Index(['pays', 'capitale', 'population', 'date d'adhésion'], dtype='object')
Ces étiquettes sont des objet Pandas appelés Index. Ce sont des tableaux Numpy immuables, c’est-à-dire qu’on ne peut pas modifier leurs éléments.
On peut par contre changer un index entier :
# Modifier les étiquettes des lignes du DataFrame en les faisant commencer à 1
df_europe.index = np.arange(1, 28)
# Afficher la premiere ligne
df_europe.head(n = 1)
pays | capitale | population | date d'adhésion | |
---|---|---|---|---|
1 | allemagne | berlin | 82162000 | 1957-01-01 |
Vidéo: Manipulation des données#
Accéder et modifier les données#
On peut accéder aux données du DataFrame pour les extraire ou les modifier grâce à des accesseurs :
Accesseur |
Description |
---|---|
|
accepte les étiquettes des lignes ou colonnes et retourne un objet Series ou DataFrame |
|
accepte les indices des lignes ou colonnes et retourne un objet Series ou DataFrame |
|
accepte les étiquettes des lignes ou colonnes et retourne une valeur unique |
|
accepte les indices des lignes ou colonnes et retourne une valeur unique |
Comme pour les tableaux Numpy, les accesseurs supportent l’indexation et le tranchage. Voyons quelques exemples :
# Extraire les capitales des 3 premiers pays
print(df_europe.loc[1:4, 'capitale'])
1 berlin
2 vienne
3 bruxelles
4 sofia
Name: capitale, dtype: object
Attention, avec .loc[]
on a utilisé les étiquettes des lignes, décalées de 1 par rapport à leur indice. Pour extraire le même objet Series avec iloc[]
, on écrira :
# Extraire les capitales des 3 premiers pays
print(df_europe.iloc[0:4, 1])
1 berlin
2 vienne
3 bruxelles
4 sofia
Name: capitale, dtype: object
Note :
lorsqu’on accède par étiquettes, la notation m:n signifie \([m,n]\),
lorsqu’on accède par indices, la notation m:n signifie \([m,n[\).
Attention aux erreurs !
Comme pour les tableaux Numpy, on peut trancher en fournissant une liste :
# Extraire les noms et les populations des pays 3 à 7
df_europe.loc[3:7, ['pays', 'population']]
pays | population | |
---|---|---|
3 | belgique | 11289853 |
4 | bulgarie | 7153784 |
5 | chypre | 848319 |
6 | croatie | 4190669 |
7 | danemark | 5659715 |
Avec l’accesseur iloc[]
on écrira :
# Extraire les noms et les populations des pays 3 à 7
df_europe.iloc[2:7, [0, 2]]
pays | population | |
---|---|---|
3 | belgique | 11289853 |
4 | bulgarie | 7153784 |
5 | chypre | 848319 |
6 | croatie | 4190669 |
7 | danemark | 5659715 |
Pour accéder à une valeur en particulier :
# Nom du pays 23
print(df_europe.at[23, 'pays'])
# Ou avec les indices
print(df_europe.iat[22, 0])
république tchèque
république tchèque
Exercice#
Extraire les date d’adhésion des pays avec les étiquettes 12 et 25
Extraire la population du pays avec l’étiquette 6
Show code cell source
# 1
print(df_europe.loc[[12, 25], "date d'adhésion"])
12 1981-01-01
25 2004-01-01
Name: date d'adhésion, dtype: datetime64[s]
# 2
print(df_europe.at[6, "population"])
4190669
Insérer et supprimer des données#
Pour insérer une nouvelle ligne, on peut créer un objet Series et l’ajouter au DataFrame avec la méthode .append()
. Supposons que la Turquie joigne l’Union Européenne en 2026 :
# Création de l'objet Series
ligne_turquie = pd.Series(data = ['turquie', 'ankara', 83154997, np.datetime64('2026', 'Y')],
index = df_europe.columns, name = 28)
# Affichage
print(ligne_turquie)
pays turquie
capitale ankara
population 83154997
date d'adhésion 2026
Name: 28, dtype: object
# Ajout de la ligne à l'objet DataFrame
df_europe = df_europe.append(ligne_turquie)
# Affichage des 2 dernières lignes
df_europe.tail(n = 2)
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
/tmp/ipykernel_2146/893767574.py in ?()
----> 2 # Ajout de la ligne à l'objet DataFrame
3 df_europe = df_europe.append(ligne_turquie)
4
5 # Affichage des 2 dernières lignes
/opt/hostedtoolcache/Python/3.8.18/x64/lib/python3.8/site-packages/pandas/core/generic.py in ?(self, name)
5985 and name not in self._accessors
5986 and self._info_axis._can_hold_identifiers_and_holds_name(name)
5987 ):
5988 return self[name]
-> 5989 return object.__getattribute__(self, name)
AttributeError: 'DataFrame' object has no attribute 'append'
Plusieurs remarques :
Pour créer l’objet Series on a utilisé l’option
index
pour nommer les colonnes, et l’optionname
pour lui donner un nomL’étiquette de la nouvelle ligne de l’objet DataFrame est le nom de l’objet Series
On aurait pu ignorer ce nom en utilisant l’option de la méthode
append()
:ignore_index = True
La méthode .drop()
permet de supprimer des lignes :
# Supprimer la ligne avec l'étiquette 28
df_europe = df_europe.drop([28])
# Affichage des 2 dernières lignes
df_europe.tail(n = 2)
Il est beaucoup plus simple d’ajouter une colonne à un DataFrame. On peut le faire de la même manière que l’on ajoute une paire clé-valeur à un dictionnaire. Par exemple, ajoutons la colonne qui donne le nombre de sièges au parlement européen pour chaque pays de l’UE :
# Nombre de sièges au parlement
np_sieges = np.array([96, 18, 21, 17, 6, 11, 13, 54, 6, 13, 74, 21, 21, 11, 73, 8, 11, 6, 6, 26, 51, 21, 21, 32, 13, 8, 20])
# Ajout de la colonne
df_europe['sièges'] = np_sieges
# Affichage des 2 premières lignes
df_europe.head(n = 2)
Vidéo: Opérations, tri et sauvegarde#
Opérateurs arithmétiques et fonctions#
Il est possible d’utiliser les opérateurs arithmétiques usuels et les fonctions Numpy sur les colonnes d’un objet DataFrame. Par exemple, calculons le pourcentage de la population dans chaque pays par rapport à la population totale :
# Population totale
pop_tot = df_europe['population'].sum()
# Pourcentage de la population
pop_pourcentage = df_europe['population'] / pop_tot * 100
# Affichage
print(pop_pourcentage)
Exercice#
Nous voulons connaître le poids de chaque pays au parlement par rapport à son nombre d’habitants.
Calculer le nombre de sièges au parlement par habitant dans chaque pays, c’est-à-dire :
Normaliser le résultat et l’exprimer en pourcentage, c’est-à-dire :
Ajouter une nouvelle colonne au DataFrame
df_europe
avec le nom'poids'
qui contient \(\bar{N}_i\)Afficher les colonnes
'pays'
et'poids'
du DataFramedf_europe
pour les 20 premières lignes
Show code cell source
# Nombre de siège par habitant
siege_pourcentage = df_europe['sièges'] / df_europe['population']
# Normalisation
siege_pourcentage = siege_pourcentage / siege_pourcentage.sum() * 100
# Ajout d'une colonne poids
df_europe['poids'] = siege_pourcentage
# Affichage
df_europe.loc[1:20, ['pays', 'poids']]
Trier un DataFrame#
Il est très facile de trier un objet DataFrame avec la méthode .sort_values()
. Par exemple, on voit qu’en classant les pays d’abord par nombre de sièges croissant, puis, pour un même nombre de sièges, par poids décroissant, on déduit que plus le nombre de sièges et la population augmentent, plus le poids d’un pays au parlement diminue :
df_europe.sort_values(by = ['sièges', 'poids'], ascending = [True, False])
Sauvegarder un DataFrame#
Il est possible de sauver un objet DataFrame sous la forme d’un fichier csv. C’est un format pratique qui peut être lu par la plupart des tableurs comme LibreOffice Calc ou Microsoft Excel. Il faut utiliser la métode .to_csv()
:
df_europe.to_csv("europe.csv")
Cette méthode crée un fichier nommé data.csv dans le répertoire de travail (allez vérifier !), avec le contenu du DataFrame :
,pays,capitale,population,date d'adhésion,sièges,poids
1,allemagne,berlin,82162000,1957-01-01,96,1.3703356988657123
2,autriche,vienne,8700471,1995-01-01,18,2.4263669538020842
3,belgique,bruxelles,11289853,1957-01-01,21,2.1815127149779783
4,bulgarie,sofia,7153784,2007-01-01,17,2.7870184226008328
5,chypre,nicosie,848319,2004-01-01,6,8.295045974023676
6,croatie,zagreb,4190669,2013-01-01,11,3.078478088741746
7,danemark,copenhague,5659715,1973-01-01,13,2.6938631589897866
8,espagne,madrid,46438422,1986-01-01,54,1.363776011827881
9,estonie,tallinn,1315944,2004-01-01,6,5.347374284648732
10,finlande,helsinki,5401267,1995-01-01,13,2.822763201464005
11,france,paris,66661621,1957-01-01,74,1.301914879970682
12,grèce,athènes,10793526,1981-01-01,21,2.28182689046492
13,hongrie,budapest,9830485,2004-01-01,21,2.5053654900782893
14,irlande,dublin,4658530,1973-01-01,11,2.7693033411117423
15,italie,rome,60665551,1957-01-01,73,1.4112613727889787
16,lettonie,riga,1968957,2004-01-01,8,4.765193013788716
17,lituanie,vilnius,2888558,2004-01-01,11,4.466201715066578
18,luxembourg,luxembourg,576249,1957-01-01,6,12.211466060050068
19,malte,la valette,434403,2004-01-01,6,16.198886991198936
20,pays-bas,amsterdam,16979120,1957-01-01,26,1.7959114169499812
21,pologne,varsovie,37967209,2004-01-01,51,1.5753905797479404
22,portugal,lisbonne,10341330,1986-01-01,21,2.381604481215885
23,république tchèque,prague,10553853,2004-01-01,21,2.3336460977552242
24,roumanie,bucarest,19759968,2007-01-01,32,1.8992865050895604
25,slovaquie,bratislava,5426252,2004-01-01,13,2.8097658805528902
26,slovénie,ljubljana,2064188,2004-01-01,8,4.545351557537583
27,suède,stockholm,9851017,1995-01-01,20,2.381089216689604
Il est alors possible, plus tard, de charger le contenu du fichier csv avec la méthode .read_csv()
:
df_europe_from_csv = pd.read_csv("europe.csv", index_col = 0, encoding = 'utf-8')
L’option encoding
peut être nécessaire sur certains ordinateurs, pas sur d’autres.
Un problème de sauvegarder en csv est que si une des colonnes contient des objets python, il peuvent être convertis en un autre type au moment de l’écriture et de la lecture. Dans l’exemple ci-dessus, la colonne date d'adhésion
n’est plus un objet datetime64
au moment de la lecture : il a été converti en objet str
.
A = df_europe_from_csv["date d'adhésion"][1]
print(A)
print(type(A))
Une façon de garder toutes les propriétés des objets python est de les sauver dans un format que l’on appelle pickle :
# Sauvegarde d'un DataFrame dans un fichier pickle
df_europe.to_pickle('europe.pkl')
# Lecture du fichier pickle
df_europe_from_pickle = pd.read_pickle('./europe.pkl')
On voit alors que les dates sont restées des objets datetime64
:
df_europe_from_pickle["date d'adhésion"].head(n = 2)