Pour essayer les exemples présents dans ce tutoriel :
Le package Pandas
est l’une des briques centrales de l’écosystème de
la data science. Le DataFrame,
objet central dans des langages comme R
ou Stata
, a longtemps était un grand absent dans l’écosystème Python
.
Pourtant, grâce à Numpy
, toutes les briques de base étaient présentes
mais méritaient d’être réagencées pour convenir aux besoins
des data scientists.
Wes McKinney, lorsqu’il a créé Pandas
en s’appuyant sur Numpy
a ainsi introduit cet objet devenu banal qu’est le DataFrame.
Pandas
est rapidement
devenu un incontournable de la data-science. L’ouvrage
de référence de McKinney (2012) présente de manière plus
ample ce package. Les deux prochains chapitres, celui-ci et le
suivant, vont présenter quelques éléments structurants
de ce package. Ce chapitre prend la forme d’un tutoriel
alors que l’exercice suivant est plutôt un ensemble d’exercices.
A l’issue du chapitre précédent, grâce à des croisements
de données, nous diposerons d’une base fine sur les empreintes
carbones des Français1.
Ce tutoriel vise à introduire aux concepts
de base de ce package par l’exemple et à introduire à certaines
des tâches les plus fréquentes de (re)structuration
des données du data scientist. Il ne s’agit pas d’un ensemble
exhaustif de commandes : Pandas
est un package tentaculaire
qui permet de réaliser la même opération de nombreuses manières.
Nous nous concentrerons ainsi sur les éléments les plus pertinents
dans le cadre d’une introduction à la data science et laisserons
les utilisateurs intéressés approfondir leurs connaissances
dans les ressources foisonnantes qu’il existe sur le sujet.
Dans ce tutoriel Pandas
, nous allons utiliser :
- Les émissions de gaz à effet de serre estimées au niveau communal par l’ADEME. Le jeu de données est
disponible sur data.gouv
et requêtable directement dans
Python
avec cet url ;
Le chapitre suivant permettra de mettre en application des éléments présents dans ce chapitre avec les données ci-dessus associées à des données de contexte au niveau communal.
Nous suivrons les conventions habituelles dans l’import des packages :
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
Pour obtenir des résultats reproductibles, on peut fixer la racine du générateur pseudo-aléatoire.
123) np.random.seed(
Au cours de cette démonstration des principales fonctionalités de Pandas
, et
lors du chapitre suivant,
je recommande de se référer régulièrement aux ressources suivantes :
- L’aide officielle de
Pandas
. Notamment, la page de comparaison des langages qui est très utile ; - Ce tutoriel,
pensé certes pour les utilisateurs d’
Observable Javascript
, mais qui offre de nombreux exemples intéressants pour les afficionados dePandas
; - La cheatsheet suivante, issue de ce post
1 Logique de Pandas
L’objet central dans la logique Pandas
est le DataFrame
.
Il s’agit d’une structure particulière de données
à deux dimensions, structurées en alignant des lignes et colonnes.
Contrairement à une matrice, les colonnes
peuvent être de types différents.
Un DataFrame
est composé des éléments suivants :
- l’indice de la ligne ;
- le nom de la colonne ;
- la valeur de la donnée ;
Le concept de tidy data, popularisé par Hadley Wickham via ses packages R
,
est parfaitement pertinent pour décrire la structure d’un DataFrame Pandas
.
Les trois règles des données tidy sont les suivantes :
- Chaque variable possède sa propre colonne ;
- Chaque observation possède sa propre ligne ;
- Une valeur, matérialisant une observation d’une variable, se trouve sur une unique cellule.
Pandas
propose ainsi un schéma de données assez familier aux utilisateurs
de logiciels statistiques comme R
. A l’instar des
principaux paradigmes de traitement de la données comme le tidyverse (R
),
la grammaire de Pandas
est héritière de la logique SQL
.
La philosophie est très proche : on effectue des opérations
de sélection de ligne, de colonne, des tris de ligne en fonction
de valeurs de certaines colonnes, des traitements standardisés sur des
variables, etc. De manière générale, on privilégie les traitements
faisant appel à des noms de variables à des numéros de ligne ou de
colonne.
Les principales manipulations sont les suivantes:
Que vous soyez familiers de SQL
ou de R
, vous retrouverez une logique
similaire à celle que vous connaissez quoique les noms puissent diverger:
df.loc[df['y']=='b']
s’écrira peut-être df %>% filter(y=='b')
(R
)
ou SELECT * FROM df WHERE y == 'b'
(SQL
) mais la logique
est la même.
Pandas
propose énormément de fonctionnalités pré-implémentées.
Il est vivement recommandé, avant de se lancer dans l’écriture d’uneA
fonction, de se poser la question de son implémentation native dans Numpy
, Pandas
, etc.
La plupart du temps, s’il existe une solution implémentée dans une librairie, il convient
de l’utiliser car elle sera plus efficace que celle que vous mettrez en oeuvre.
2 Les Series
En fait, un DataFrame est une collection d’objets appelés pandas.Series
.
Ces Series
sont des objets d’une dimension qui sont des extensions des
array-unidimensionnels Numpy
. En particulier, pour faciliter le traitement
de données catégorielles ou temporelles, des types de variables
supplémentaires sont disponibles dans Pandas
par rapport à
Numpy
(categorical
, datetime64
et timedelta64
). Ces
types sont associés à des méthodes optimisées pour faciliter le traitement
de ces données.
Il ne faut pas négliger l’attribut dtype
d’un objet
pandas.Series
car cela a une influence déterminante sur les méthodes
et fonctions pouvant être utilisées (on ne fait pas les mêmes opérations
sur une donnée temporelle et une donnée catégorielle) et le volume en
mémoire d’une variable (le type de la variable détermine le volume
d’information stockée pour chaque élément ; être trop précis est parfois
néfaste).
Il existe plusieurs types possibles pour un pandas.Series
.
Le type object
correspond aux types Python str
ou mixed
.
Il existe un type particulier pour les variables dont le nombre de valeurs
est une liste finie et relativement courte, le type category
.
Il faut bien examiner les types de son DataFrame, et convertir éventuellement
les types lors de l’étape de data cleaning.
2.1 Indexation
La différence essentielle entre une Series
et un objet Numpy
est l’indexation.
Dans Numpy
,
l’indexation est implicite ; elle permet d’accéder à une donnée (celle à
l’index situé à la position i).
Avec une Series
, on peut bien sûr utiliser un indice de position mais on peut
surtout faire appel à des indices plus explicites.
Par exemple,
= pd.Series([1.0, 1.5, 1], index=["chat", "chien", "koala"])
taille
taille.head()
chat 1.0
chien 1.5
koala 1.0
dtype: float64
Cette indexation permet d’accéder à des valeurs de la Series
via une valeur de l’indice. Par
exemple, taille['koala']
:
"koala"] taille[
1.0
L’existence d’indice rend le subsetting particulièrement aisé, ce que vous pouvez expérimenter dans les exercices :
Pour transformer un objet pandas.Series
en array numpy
,
on utilise la méthode values
. Par exemple, taille.values
:
taille.values
array([1. , 1.5, 1. ])
Un avantage des Series
par rapport à un array numpy
est que
les opérations sur les Series
alignent
automatiquement les données à partir des labels.
Avec des Series
labélisées, il n’est ainsi pas nécessaire
de se poser la question de l’ordre des lignes.
L’exemple dans la partie suivante permettra de s’en assurer.
2.2 Valeurs manquantes
Par défaut, les valeurs manquantes sont affichées NaN
et sont de type np.nan
(pour
les valeurs temporelles, i.e. de type datatime64
, les valeurs manquantes sont
NaT
).
On a un comportement cohérent d’agrégation lorsqu’on combine deux DataFrames
(ou deux colonnes).
Par exemple,
= pd.DataFrame(
x "prix": np.random.uniform(size=5), "quantite": [i + 1 for i in range(5)]},
{=["yaourt", "pates", "riz", "tomates", "gateaux"],
index
) x
prix | quantite | |
---|---|---|
yaourt | 0.696469 | 1 |
pates | 0.286139 | 2 |
riz | 0.226851 | 3 |
tomates | 0.551315 | 4 |
gateaux | 0.719469 | 5 |
= pd.DataFrame(
y "prix": [np.nan, 0, 1, 2, 3], "quantite": [i + 1 for i in range(5)]},
{=["tomates", "yaourt", "gateaux", "pates", "riz"],
index
) y
prix | quantite | |
---|---|---|
tomates | NaN | 1 |
yaourt | 0.0 | 2 |
gateaux | 1.0 | 3 |
pates | 2.0 | 4 |
riz | 3.0 | 5 |
+ y x
prix | quantite | |
---|---|---|
gateaux | 1.719469 | 8 |
pates | 2.286139 | 6 |
riz | 3.226851 | 8 |
tomates | NaN | 5 |
yaourt | 0.696469 | 3 |
donne bien une valeur manquante pour la ligne tomates
. Au passage, on peut remarquer que l’agrégation
a tenu compte des index.
Il est possible de supprimer les valeurs manquantes grâce à dropna()
.
Cette méthode va supprimer toutes les lignes où il y a au moins une valeur manquante.
Il est aussi possible de supprimer seulement les colonnes où il y a des valeurs manquantes
dans un DataFrame avec dropna()
avec le paramètre axis=1
(par défaut égal à 0).
Il est également possible de remplir les valeurs manquantes grâce à la méthode fillna()
.
3 Le DataFrame Pandas
Le DataFrame
est l’objet central de la librairie pandas
.
Il s’agit d’une collection de pandas.Series
(colonnes) alignées par les index.
Les types des variables peuvent différer.
Un DataFrame
non-indexé a la structure suivante :
= pd.DataFrame(
df "taille": [1.0, 1.5, 1], "poids": [3, 5, 2.5]}, index=["chat", "chien", "koala"]
{
) df.reset_index()
index | taille | poids | |
---|---|---|---|
0 | chat | 1.0 | 3.0 |
1 | chien | 1.5 | 5.0 |
2 | koala | 1.0 | 2.5 |
Alors que le même DataFrame
indexé aura la structure suivante :
= pd.DataFrame(
df "taille": [1.0, 1.5, 1], "poids": [3, 5, 2.5]}, index=["chat", "chien", "koala"]
{
) df.head()
taille | poids | |
---|---|---|
chat | 1.0 | 3.0 |
chien | 1.5 | 5.0 |
koala | 1.0 | 2.5 |
3.1 Les attributs et méthodes utiles
Pour présenter les méthodes les plus pratiques pour l’analyse de données, on peut partir de l’exemple des consommations de CO2 communales issues des données de l’Ademe. Cette base de données est exploitée plus intensément dans le chapitre d’exercice (prochain chapitre).
L’import de données depuis un fichier plat se fait avec la fonction read_csv
:
= pd.read_csv(
df "https://koumoul.com/s/data-fair/api/v1/datasets/igt-pouvoir-de-rechauffement-global/convert"
) df
INSEE commune | Commune | Agriculture | Autres transports | Autres transports international | CO2 biomasse hors-total | Déchets | Energie | Industrie hors-énergie | Résidentiel | Routier | Tertiaire | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 01001 | L'ABERGEMENT-CLEMENCIAT | 3711.425991 | NaN | NaN | 432.751835 | 101.430476 | 2.354558 | 6.911213 | 309.358195 | 793.156501 | 367.036172 |
1 | 01002 | L'ABERGEMENT-DE-VAREY | 475.330205 | NaN | NaN | 140.741660 | 140.675439 | 2.354558 | 6.911213 | 104.866444 | 348.997893 | 112.934207 |
2 | 01004 | AMBERIEU-EN-BUGEY | 499.043526 | 212.577908 | NaN | 10313.446515 | 5314.314445 | 998.332482 | 2930.354461 | 16616.822534 | 15642.420313 | 10732.376934 |
3 | 01005 | AMBERIEUX-EN-DOMBES | 1859.160954 | NaN | NaN | 1144.429311 | 216.217508 | 94.182310 | 276.448534 | 663.683146 | 1756.341319 | 782.404357 |
4 | 01006 | AMBLEON | 448.966808 | NaN | NaN | 77.033834 | 48.401549 | NaN | NaN | 43.714019 | 398.786800 | 51.681756 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
35793 | 95676 | VILLERS-EN-ARTHIES | 1628.065094 | NaN | NaN | 165.045396 | 65.063617 | 11.772789 | 34.556067 | 176.098160 | 309.627908 | 235.439109 |
35794 | 95678 | VILLIERS-ADAM | 698.630772 | NaN | NaN | 1331.126598 | 111.480954 | 2.354558 | 6.911213 | 1395.529811 | 18759.370071 | 403.404815 |
35795 | 95680 | VILLIERS-LE-BEL | 107.564967 | NaN | NaN | 8367.174532 | 225.622903 | 534.484607 | 1568.845431 | 22613.830247 | 12217.122402 | 13849.512001 |
35796 | 95682 | VILLIERS-LE-SEC | 1090.890170 | NaN | NaN | 326.748418 | 108.969749 | 2.354558 | 6.911213 | 67.235487 | 4663.232127 | 85.657725 |
35797 | 95690 | WY-DIT-JOLI-VILLAGE | 1495.103542 | NaN | NaN | 125.236417 | 97.728612 | 4.709115 | 13.822427 | 117.450851 | 504.400972 | 147.867245 |
35798 rows × 12 columns
Note
Dans un processus de production, où normalement on connait les types des variables du DataFrame
qu’on va importer,
il convient de préciser les types avec lesquels on souhaite importer les données
(argument dtype
, sous la forme d’un dictionnaire).
Cela est particulièrement important lorsqu’on désire utiliser une colonne
comme une variable textuelle mais qu’elle comporte des attributs proches d’un nombre
qui vont inciter pandas
à l’importer sous forme de variable numérique.
Par exemple, une colonne [00001,00002,...]
risque d’être importée comme une variable numérique, ignorant l’information des premiers 0 (qui peuvent pourtant la distinguer de la séquence 1, 2, etc.). Pour s’assurer que pandas
importe sous forme textuelle la variable, on peut utiliser dtype = {"code": "str"}
Sinon, on peut importer le csv, et modifier les types avec astype()
.
Avec astype
, on peut gérer les erreurs de conversion avec le paramètre errors
.
L’affichage des DataFrames dans les notebooks est assez ergonomique.
Les premières et dernières lignes s’affichent
automatiquement. Pour des tables de valorisation présentes dans un
rapport ou un article de recherche, le chapitre suivant
présente great_tables
qui offre de très riches
fonctionnalités de mise en forme des tableaux.
Pour afficher une partie ciblée des données, on peut aussi faire:
head
qui permet, comme son nom l’indique, de n’afficher que les premières lignes ;tail
qui permet, comme son nom l’indique, de n’afficher que les dernières lignessample
qui permet d’afficher un échantillon aléatoire de n lignes. Cette méthode propose de nombreuses options.
Warning
Il faut faire attention au display
et aux
commandes qui révèlent des données (head
, tail
, etc.)
dans un notebook qui exploite
des données confidentielles lorsqu’on utilise
le logiciel de contrôle de version Git
(cf. chapitres dédiés).
En effet, on peut se
retrouver à partager des données, involontairement, dans l’historique
Git
. Comme cela sera expliqué dans le chapitre dédié à Git
,
un fichier, nommé le .gitignore
, suffit pour créer quelques règles
évitant le partage involontaire de données avec Git
.
3.2 Dimensions et structure du DataFrame
Les premières méthodes utiles permettent d’afficher quelques
attributs d’un DataFrame
.
df.axes
[RangeIndex(start=0, stop=35798, step=1),
Index(['INSEE commune', 'Commune', 'Agriculture', 'Autres transports',
'Autres transports international', 'CO2 biomasse hors-total', 'Déchets',
'Energie', 'Industrie hors-énergie', 'Résidentiel', 'Routier',
'Tertiaire'],
dtype='object')]
df.columns
Index(['INSEE commune', 'Commune', 'Agriculture', 'Autres transports',
'Autres transports international', 'CO2 biomasse hors-total', 'Déchets',
'Energie', 'Industrie hors-énergie', 'Résidentiel', 'Routier',
'Tertiaire'],
dtype='object')
df.index
RangeIndex(start=0, stop=35798, step=1)
Pour connaître les dimensions d’un DataFrame, on peut utiliser quelques méthodes pratiques :
df.ndim
2
df.shape
(35798, 12)
df.size
429576
Pour déterminer le nombre de valeurs uniques d’une variable, plutôt que chercher à écrire soi-même une fonction,
on utilise la
méthode nunique
. Par exemple,
"Commune"].nunique() df[
33338
Comme évoqué précédemment, Pandas
propose énormément de méthodes utiles.
Voici un premier résumé, accompagné d’un comparatif avec R
:
Opération | pandas | dplyr (R ) |
data.table (R ) |
---|---|---|---|
Récupérer le nom des colonnes | df.columns |
colnames(df) |
colnames(df) |
Récupérer les indices | df.index |
unique(df[,get(key(df))]) |
|
Récupérer les dimensions | df.shape |
dim(df) |
dim(df) |
Récupérer le nombre de valeurs uniques d’une variable | df['myvar'].nunique() |
df %>% summarise(distinct(myvar)) |
df[,uniqueN(myvar)] |
3.3 Statistiques agrégées
Pandas
propose une série de méthodes pour faire des statistiques
agrégées de manière efficace.
On peut, par exemple, appliquer des méthodes pour compter le nombre de lignes,
faire une moyenne ou une somme de l’ensemble des lignes
En premier lieu, on peut faire une somme, pour chaque variable, des
valeurs présentes dans celle-ci. Pandas
implémente
celle-ci par le biais de la méthode sum
:
sum(numeric_only=True) df.
Agriculture 8.790969e+07
Autres transports 6.535446e+06
Autres transports international 2.223857e+07
CO2 biomasse hors-total 6.351931e+07
Déchets 1.470358e+07
Energie 2.285203e+07
Industrie hors-énergie 8.357368e+07
Résidentiel 6.384140e+07
Routier 1.264932e+08
Tertiaire 3.956273e+07
dtype: float64
Il est également possible de faire une moyenne:
=True) df.mean(numeric_only
Agriculture 2459.975760
Autres transports 654.919940
Autres transports international 7692.344960
CO2 biomasse hors-total 1774.381550
Déchets 410.806329
Energie 662.569846
Industrie hors-énergie 2423.127789
Résidentiel 1783.677872
Routier 3535.501245
Tertiaire 1105.165915
dtype: float64
A noter que les valeurs manquantes sont automatiquement
éliminées de la statistique. Pour connaître le nombre de
valeurs non manquantes pour chaque colonne, la
méthode count
est disponible:
df.count()
INSEE commune 35798
Commune 35798
Agriculture 35736
Autres transports 9979
Autres transports international 2891
CO2 biomasse hors-total 35798
Déchets 35792
Energie 34490
Industrie hors-énergie 34490
Résidentiel 35792
Routier 35778
Tertiaire 35798
dtype: int64
Cependant, cette dernière ne distingue pas les valeurs dupliquées. Parfois, il est pratique de connaître le nombre de valeurs uniques d’une variable afin de savoir si elle dispose de beaucoup de modalités ou non:
df.nunique()
INSEE commune 35798
Commune 33338
Agriculture 35576
Autres transports 9963
Autres transports international 2883
CO2 biomasse hors-total 35798
Déchets 11016
Energie 1453
Industrie hors-énergie 1889
Résidentiel 35791
Routier 35749
Tertiaire 8663
dtype: int64
Les méthodes statistiques préimplementées vont au-delà de simples sommes ou moyennes. Il est possible de faire, par exemple, des calculs de quantiles sur les différentes colonnes d’un jeu de données
=[0.1, 0.25, 0.5, 0.75, 0.9], numeric_only=True) df.quantile(q
Agriculture | Autres transports | Autres transports international | CO2 biomasse hors-total | Déchets | Energie | Industrie hors-énergie | Résidentiel | Routier | Tertiaire | |
---|---|---|---|---|---|---|---|---|---|---|
0.10 | 382.620882 | 25.034578 | 4.430792 | 109.152816 | 14.811230 | 2.354558 | 6.911213 | 50.180933 | 199.765410 | 49.289082 |
0.25 | 797.682631 | 52.560412 | 10.050967 | 197.951108 | 25.655166 | 2.354558 | 6.911213 | 96.052911 | 419.700460 | 94.749885 |
0.50 | 1559.381285 | 106.795928 | 19.924343 | 424.849988 | 54.748653 | 4.709115 | 13.822427 | 227.091193 | 1070.895593 | 216.297718 |
0.75 | 3007.883903 | 237.341501 | 32.983111 | 1094.749825 | 110.820941 | 51.800270 | 152.046694 | 749.469293 | 3098.612157 | 576.155869 |
0.90 | 5442.727470 | 528.349529 | 59.999169 | 3143.759029 | 190.695774 | 367.311008 | 1154.172630 | 2937.699671 | 8151.047248 | 1897.732565 |
Le résultat est un tableau en 2 dimensions: une ligne par statistique
demandée pour chaque colonne numérique de notre jeu de données. Cela
peut permettre de se représenter rapidement la distribution des données.
Néanmoins c’est un produit assez brut ; pour avoir une meilleure
compréhension de la distribution de chaque colonne, une table stylisée
(obtenue par great_tables
) ou un graphique est probablement
plus pertinent.
Warning
La version 2.0 de Pandas
a introduit un changement
de comportement dans les méthodes d’agrégation.
Il est dorénavant nécessaire de préciser quand on désire
effectuer des opérations si on désire ou non le faire
exclusivement sur les colonnes numériques. C’est pour cette
raison qu’on exlicite ici l’argument numeric_only = True
.
Ce comportement
était par le passé implicite.
Il faut toujours regarder les options de ces fonctions en termes de valeurs manquantes, car ces options sont déterminantes dans le résultat obtenu.
Le tableau suivant récapitule le code équivalent pour avoir des
statistiques sur toutes les colonnes d’un dataframe en R
.
Opération | pandas | dplyr (R ) |
data.table (R ) |
---|---|---|---|
Nombre de valeurs non manquantes | df.count() |
df %>% summarise_each(funs(sum(!is.na(.)))) |
df[, lapply(.SD, function(x) sum(!is.na(x)))] |
Moyenne de toutes les variables | df.mean() |
df %>% summarise_each(funs(mean((., na.rm = TRUE)))) |
df[,lapply(.SD, function(x) mean(x, na.rm = TRUE))] |
La méthode describe
permet de sortir un tableau de statistiques
agrégées :
df.describe()
Agriculture | Autres transports | Autres transports international | CO2 biomasse hors-total | Déchets | Energie | Industrie hors-énergie | Résidentiel | Routier | Tertiaire | |
---|---|---|---|---|---|---|---|---|---|---|
count | 35736.000000 | 9979.000000 | 2.891000e+03 | 35798.000000 | 35792.000000 | 3.449000e+04 | 3.449000e+04 | 35792.000000 | 35778.000000 | 35798.000000 |
mean | 2459.975760 | 654.919940 | 7.692345e+03 | 1774.381550 | 410.806329 | 6.625698e+02 | 2.423128e+03 | 1783.677872 | 3535.501245 | 1105.165915 |
std | 2926.957701 | 9232.816833 | 1.137643e+05 | 7871.341922 | 4122.472608 | 2.645571e+04 | 5.670374e+04 | 8915.902379 | 9663.156628 | 5164.182507 |
min | 0.003432 | 0.000204 | 3.972950e-04 | 3.758088 | 0.132243 | 2.354558e+00 | 1.052998e+00 | 1.027266 | 0.555092 | 0.000000 |
25% | 797.682631 | 52.560412 | 1.005097e+01 | 197.951108 | 25.655166 | 2.354558e+00 | 6.911213e+00 | 96.052911 | 419.700460 | 94.749885 |
50% | 1559.381285 | 106.795928 | 1.992434e+01 | 424.849988 | 54.748653 | 4.709115e+00 | 1.382243e+01 | 227.091193 | 1070.895593 | 216.297718 |
75% | 3007.883903 | 237.341501 | 3.298311e+01 | 1094.749825 | 110.820941 | 5.180027e+01 | 1.520467e+02 | 749.469293 | 3098.612157 | 576.155869 |
max | 98949.317760 | 513140.971691 | 3.303394e+06 | 576394.181208 | 275500.374439 | 2.535858e+06 | 6.765119e+06 | 410675.902028 | 586054.672836 | 288175.400126 |
3.4 Méthodes relatives aux valeurs manquantes
Les méthodes relatives aux valeurs manquantes peuvent être mobilisées en conjonction des méthodes de statistiques agrégées. C’est utile lorsqu’on désire obtenir une idée de la part de valeurs manquantes dans un jeu de données
sum() df.isnull().
INSEE commune 0
Commune 0
Agriculture 62
Autres transports 25819
Autres transports international 32907
CO2 biomasse hors-total 0
Déchets 6
Energie 1308
Industrie hors-énergie 1308
Résidentiel 6
Routier 20
Tertiaire 0
dtype: int64
On trouvera aussi la référence à isna()
qui est la même méthode que isnull()
.
4 Graphiques rapides
Les méthodes par défaut de graphiques (approfondies dans la partie visualisation) sont pratiques pour produire rapidement un graphique, notamment après des opérations complexes de maniement de données.
En effet, on peut appliquer la méthode plot()
directement à une pandas.Series
:
"Déchets"].plot() df[
Par défaut, la visualisation obtenue est une série. Ce n’est
pas forcément celle attendue puisqu’elle n’est adaptée
qu’à des données temporelles. Souvent, on s’intéresse à un histogramme.
Pour cela, il suffit d’ajouter l’argument kind = 'hist'
:
"Déchets"].hist() df[
Avec des données dont la distribution est non normalisée, celui-ci peut être peu instructif. Le log peut être une solution pour remettre à une échelle comparable certaines valeurs extrêmes:
"Déchets"].plot(kind="hist", logy=True) df[
La sortie est un objet matplotlib
. La customisation de ces
figures est ainsi
possible (et même désirable car les graphiques matplotlib
sont, par défaut, assez rudimentaires).
Cependant, il s’agit d’une méthode rapide pour la construction
de figures qui nécessite du travail pour une visualisation
finalisée. Cela passe par un travail approfondi sur l’objet
matplotlib
ou l’utilisation d’une librairie plus haut
niveau pour la représentation graphique (seaborn
, plotnine
, plotly
, etc.)
Le chapitre consacré à la visualisation présentera succinctement ces différents
paradigmes de visualisation. Ceux-ci ne dispensent pas de faire
preuve de bon sens dans le choix du graphique utilisé
pour représenter une statistique descriptive (cf. cette conférence d’Eric Mauvière ).
5 Accéder à des éléments d’un DataFrame
5.1 Sélectionner des colonnes
En SQL, effectuer des opérations sur les colonnes se fait avec la commande
SELECT
. Avec Pandas
,
pour accéder à une colonne dans son ensemble on peut
utiliser plusieurs approches :
dataframe.variable
, par exempledf.Energie
. Cette méthode requiert néanmoins d’avoir des noms de colonnes sans espace ou caractères spéciaux, ce qui exclut souvent des jeux de données réels. Elle n’est pas recommandée.dataframe[['variable']]
pour renvoyer la variable sous forme deDataFrame
oudataframe['variable']
pour la renvoyer sous forme deSeries
. Par exemple,df[['Autres transports']]
oudf['Autres transports']
. C’est une manière préférable de procéder.
5.2 Accéder à des lignes
Pour accéder à une ou plusieurs valeurs d’un DataFrame
,
il existe deux manières conseillées de procéder, selon la
forme des indices de lignes ou colonnes utilisées :
df.iloc
: utilise les indices. C’est une méthode moyennement fiable car les indices d’un DataFrame peuvent évoluer au cours d’un traitement (notamment lorsqu’on fait des opérations par groupe).df.loc
: utilise les labels. Cette méthode est recommandée.
Warning
Les bouts de code utilisant la structure df.ix
sont à bannir car la fonction est deprecated et peut
ainsi disparaître à tout moment.
iloc
va se référer à l’indexation de 0 à N où N est égal à df.shape[0]
d’un
pandas.DataFrame
. loc
va se référer aux valeurs de l’index
de df
.
Par exemple, avec le pandas.DataFrame
df_example
:
= pd.DataFrame(
df_example "month": [1, 4, 7, 10], "year": [2012, 2014, 2013, 2014], "sale": [55, 40, 84, 31]}
{
)= df_example.set_index("month")
df_example df_example
year | sale | |
---|---|---|
month | ||
1 | 2012 | 55 |
4 | 2014 | 40 |
7 | 2013 | 84 |
10 | 2014 | 31 |
df_example.loc[1, :]
donnera la première ligne dedf
(ligne où l’indicemonth
est égal à 1) ;df_example.iloc[1, :]
donnera la deuxième ligne (puisque l’indexation enPython
commence à 0) ;df_example.iloc[:, 1]
donnera la deuxième colonne, suivant le même principe.
6 Principales manipulation de données
L’objectif du TP pandas est de se familiariser plus avec ces commandes à travers l’exemple des données des émissions de C02.
Les opérations les plus fréquentes en SQL
sont résumées par le tableau suivant.
Il est utile de les connaître (beaucoup de syntaxes de maniement de données
reprennent ces termes) car, d’une
manière ou d’une autre, elles couvrent la plupart
des usages de manipulation des données
Opération | SQL | pandas | dplyr (R ) |
data.table (R ) |
---|---|---|---|---|
Sélectionner des variables par leur nom | SELECT |
df[['Autres transports','Energie']] |
df %>% select(Autres transports, Energie) |
df[, c('Autres transports','Energie')] |
Sélectionner des observations selon une ou plusieurs conditions; | FILTER |
df[df['Agriculture']>2000] |
df %>% filter(Agriculture>2000) |
df[Agriculture>2000] |
Trier la table selon une ou plusieurs variables | SORT BY |
df.sort_values(['Commune','Agriculture']) |
df %>% arrange(Commune, Agriculture) |
df[order(Commune, Agriculture)] |
Ajouter des variables qui sont fonction d’autres variables; | SELECT *, LOG(Agriculture) AS x FROM df |
df['x'] = np.log(df['Agriculture']) |
df %>% mutate(x = log(Agriculture)) |
df[,x := log(Agriculture)] |
Effectuer une opération par groupe | GROUP BY |
df.groupby('Commune').mean() |
df %>% group_by(Commune) %>% summarise(m = mean) |
df[,mean(Commune), by = Commune] |
Joindre deux bases de données (inner join) | SELECT * FROM table1 INNER JOIN table2 ON table1.id = table2.x |
table1.merge(table2, left_on = 'id', right_on = 'x') |
table1 %>% inner_join(table2, by = c('id'='x')) |
merge(table1, table2, by.x = 'id', by.y = 'x') |
6.1 Opérations sur les colonnes : select
, mutate
, drop
Les DataFrames
pandas sont des objets mutables en langage Python
,
c’est-à-dire qu’il est possible de faire évoluer le DataFrame
au grès
des opérations mises en oeuvre. L’opération la plus classique consiste à ajouter ou retirer
des variables à la table de données.
= df.copy() df_new
Warning
Attention au comportement de Pandas
lorsqu’on crée une duplication
d’un DataFrame
.
Par défaut, Pandas
effectue une copie par référence. Dans ce
cas, les deux objets (la copie et l’objet copié) restent reliés. Les colonnes
créées sur l’un vont être répercutées sur l’autre. Ce comportement permet de
limiter l’inflation en mémoire de Python
. En faisant ça, le deuxième
objet prend le même espace mémoire que le premier. Le package data.table
en R
adopte le même comportement, contrairement à dplyr
.
Cela peut amener à quelques surprises si ce comportement d’optimisation
n’est pas anticipé. Si vous voulez, par sécurité, conserver intact le
premier DataFrame, faites appel à une copie profonde (deep copy) en
utilisant la méthode copy
, comme ci-dessus.
Attention toutefois, cela a un coût mémoire. Avec des données volumineuses, c’est une pratique à utiliser avec précaution.
La manière la plus simple d’opérer pour ajouter des colonnes est
d’utiliser la réassignation. Par exemple, pour créer une variable
x
qui est le log
de la
variable Agriculture
:
"x"] = np.log(df_new["Agriculture"]) df_new[
Il est possible d’appliquer cette approche sur plusieurs colonnes. Un des intérêts de cette approche est qu’elle permet de recycler le nom de colonnes.
vars = ["Agriculture", "Déchets", "Energie"]
+ "_log" for v in vars]] = np.log(df_new[vars])
df_new[[v df_new
INSEE commune | Commune | Agriculture | Autres transports | Autres transports international | CO2 biomasse hors-total | Déchets | Energie | Industrie hors-énergie | Résidentiel | Routier | Tertiaire | x | Agriculture_log | Déchets_log | Energie_log | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 01001 | L'ABERGEMENT-CLEMENCIAT | 3711.425991 | NaN | NaN | 432.751835 | 101.430476 | 2.354558 | 6.911213 | 309.358195 | 793.156501 | 367.036172 | 8.219171 | 8.219171 | 4.619374 | 0.856353 |
1 | 01002 | L'ABERGEMENT-DE-VAREY | 475.330205 | NaN | NaN | 140.741660 | 140.675439 | 2.354558 | 6.911213 | 104.866444 | 348.997893 | 112.934207 | 6.164010 | 6.164010 | 4.946455 | 0.856353 |
2 | 01004 | AMBERIEU-EN-BUGEY | 499.043526 | 212.577908 | NaN | 10313.446515 | 5314.314445 | 998.332482 | 2930.354461 | 16616.822534 | 15642.420313 | 10732.376934 | 6.212693 | 6.212693 | 8.578159 | 6.906086 |
3 | 01005 | AMBERIEUX-EN-DOMBES | 1859.160954 | NaN | NaN | 1144.429311 | 216.217508 | 94.182310 | 276.448534 | 663.683146 | 1756.341319 | 782.404357 | 7.527881 | 7.527881 | 5.376285 | 4.545232 |
4 | 01006 | AMBLEON | 448.966808 | NaN | NaN | 77.033834 | 48.401549 | NaN | NaN | 43.714019 | 398.786800 | 51.681756 | 6.106949 | 6.106949 | 3.879532 | NaN |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
35793 | 95676 | VILLERS-EN-ARTHIES | 1628.065094 | NaN | NaN | 165.045396 | 65.063617 | 11.772789 | 34.556067 | 176.098160 | 309.627908 | 235.439109 | 7.395148 | 7.395148 | 4.175366 | 2.465791 |
35794 | 95678 | VILLIERS-ADAM | 698.630772 | NaN | NaN | 1331.126598 | 111.480954 | 2.354558 | 6.911213 | 1395.529811 | 18759.370071 | 403.404815 | 6.549122 | 6.549122 | 4.713854 | 0.856353 |
35795 | 95680 | VILLIERS-LE-BEL | 107.564967 | NaN | NaN | 8367.174532 | 225.622903 | 534.484607 | 1568.845431 | 22613.830247 | 12217.122402 | 13849.512001 | 4.678095 | 4.678095 | 5.418865 | 6.281303 |
35796 | 95682 | VILLIERS-LE-SEC | 1090.890170 | NaN | NaN | 326.748418 | 108.969749 | 2.354558 | 6.911213 | 67.235487 | 4663.232127 | 85.657725 | 6.994749 | 6.994749 | 4.691070 | 0.856353 |
35797 | 95690 | WY-DIT-JOLI-VILLAGE | 1495.103542 | NaN | NaN | 125.236417 | 97.728612 | 4.709115 | 13.822427 | 117.450851 | 504.400972 | 147.867245 | 7.309951 | 7.309951 | 4.582194 | 1.549500 |
35798 rows × 16 columns
Il est également possible d’utiliser la méthode assign
. Pour des opérations
vectorisées, comme le sont les opérateurs de numpy
, cela n’a pas d’intérêt.
Cela permet notamment d’enchainer les opérations sur un même DataFrame
(notamment grâce au pipe
que
nous verrons plus loin).
Cette approche utilise généralement
des lambda functions. Par exemple le code précédent (celui concernant une
seule variable) prendrait la forme:
=lambda x: np.log(x["Energie"])) df_new.assign(Energie_log
INSEE commune | Commune | Agriculture | Autres transports | Autres transports international | CO2 biomasse hors-total | Déchets | Energie | Industrie hors-énergie | Résidentiel | Routier | Tertiaire | x | Agriculture_log | Déchets_log | Energie_log | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 01001 | L'ABERGEMENT-CLEMENCIAT | 3711.425991 | NaN | NaN | 432.751835 | 101.430476 | 2.354558 | 6.911213 | 309.358195 | 793.156501 | 367.036172 | 8.219171 | 8.219171 | 4.619374 | 0.856353 |
1 | 01002 | L'ABERGEMENT-DE-VAREY | 475.330205 | NaN | NaN | 140.741660 | 140.675439 | 2.354558 | 6.911213 | 104.866444 | 348.997893 | 112.934207 | 6.164010 | 6.164010 | 4.946455 | 0.856353 |
2 | 01004 | AMBERIEU-EN-BUGEY | 499.043526 | 212.577908 | NaN | 10313.446515 | 5314.314445 | 998.332482 | 2930.354461 | 16616.822534 | 15642.420313 | 10732.376934 | 6.212693 | 6.212693 | 8.578159 | 6.906086 |
3 | 01005 | AMBERIEUX-EN-DOMBES | 1859.160954 | NaN | NaN | 1144.429311 | 216.217508 | 94.182310 | 276.448534 | 663.683146 | 1756.341319 | 782.404357 | 7.527881 | 7.527881 | 5.376285 | 4.545232 |
4 | 01006 | AMBLEON | 448.966808 | NaN | NaN | 77.033834 | 48.401549 | NaN | NaN | 43.714019 | 398.786800 | 51.681756 | 6.106949 | 6.106949 | 3.879532 | NaN |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
35793 | 95676 | VILLERS-EN-ARTHIES | 1628.065094 | NaN | NaN | 165.045396 | 65.063617 | 11.772789 | 34.556067 | 176.098160 | 309.627908 | 235.439109 | 7.395148 | 7.395148 | 4.175366 | 2.465791 |
35794 | 95678 | VILLIERS-ADAM | 698.630772 | NaN | NaN | 1331.126598 | 111.480954 | 2.354558 | 6.911213 | 1395.529811 | 18759.370071 | 403.404815 | 6.549122 | 6.549122 | 4.713854 | 0.856353 |
35795 | 95680 | VILLIERS-LE-BEL | 107.564967 | NaN | NaN | 8367.174532 | 225.622903 | 534.484607 | 1568.845431 | 22613.830247 | 12217.122402 | 13849.512001 | 4.678095 | 4.678095 | 5.418865 | 6.281303 |
35796 | 95682 | VILLIERS-LE-SEC | 1090.890170 | NaN | NaN | 326.748418 | 108.969749 | 2.354558 | 6.911213 | 67.235487 | 4663.232127 | 85.657725 | 6.994749 | 6.994749 | 4.691070 | 0.856353 |
35797 | 95690 | WY-DIT-JOLI-VILLAGE | 1495.103542 | NaN | NaN | 125.236417 | 97.728612 | 4.709115 | 13.822427 | 117.450851 | 504.400972 | 147.867245 | 7.309951 | 7.309951 | 4.582194 | 1.549500 |
35798 rows × 16 columns
Dans les méthodes suivantes, il est possible de modifier le pandas.DataFrame
en place, c’est à dire en ne le réassignant pas, avec le paramètre inplace = True
.
Par défaut, inplace
est égal à False
et pour modifier le pandas.DataFrame
,
il convient de le réassigner.
On peut facilement renommer des variables avec la méthode rename
qui
fonctionne bien avec des dictionnaires (pour renommer des colonnes il faut
préciser le paramètre axis = 1
):
= df_new.rename({"Energie": "eneg", "Agriculture": "agr"}, axis=1) df_new
Enfin, pour effacer des colonnes, on utilise la méthode drop
avec l’argument
columns
:
= df_new.drop(columns=["eneg", "agr"]) df_new
6.2 Réordonner
La méthode sort_values
permet de réordonner un DataFrame
. Par exemple,
si on désire classer par ordre décroissant de consommation de CO2 du secteur
résidentiel, on fera
= df.sort_values("Résidentiel", ascending=False) df
Ainsi, en une ligne de code, on identifie les villes où le secteur résidentiel consomme le plus.
6.3 Filtrer
L’opération de sélection de lignes s’appelle FILTER
en SQL. Elle s’utilise
en fonction d’une condition logique (clause WHERE
). On sélectionne les
données sur une condition logique. Il existe plusieurs méthodes en pandas
.
La plus simple est d’utiliser les boolean mask, déjà vus dans le chapitre
numpy
.
Par exemple, pour sélectionner les communes dans les Hauts-de-Seine, on
peut utiliser le résultat de la méthode str.startswith
(qui renvoie
True
ou False
) directement dans les crochets:
"INSEE commune"].str.startswith("92")].head(2) df[df[
INSEE commune | Commune | Agriculture | Autres transports | Autres transports international | CO2 biomasse hors-total | Déchets | Energie | Industrie hors-énergie | Résidentiel | Routier | Tertiaire | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
35494 | 92012 | BOULOGNE-BILLANCOURT | NaN | 1250.483441 | 34.234669 | 51730.704250 | 964.828694 | 8817.818741 | 25882.493998 | 92216.971456 | 64985.280901 | 60349.109482 |
35501 | 92025 | COLOMBES | NaN | 411.371588 | 14.220061 | 53923.847088 | 698.685861 | 12855.885267 | 50244.664227 | 87469.549463 | 52070.927943 | 41526.600867 |
Pour remplacer des valeurs spécifiques, on utilise la méthode where
ou une
réassignation couplée à la méthode précédente.
Par exemple, pour assigner des valeurs manquantes aux départements du 92, on peut faire cela
= df.copy()
df_copy = df_copy.where(~df["INSEE commune"].str.startswith("92")) df_copy
et vérifier les résultats:
"INSEE commune"].str.startswith("92")].head(2)
df_copy[df[~df["INSEE commune"].str.startswith("92")].head(2) df_copy[
INSEE commune | Commune | Agriculture | Autres transports | Autres transports international | CO2 biomasse hors-total | Déchets | Energie | Industrie hors-énergie | Résidentiel | Routier | Tertiaire | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
12167 | 31555 | TOULOUSE | 1434.045233 | 4482.980062 | 130.792683 | 576394.181208 | 88863.732538 | 91549.914092 | 277062.573234 | 410675.902028 | 586054.672836 | 288175.400126 |
16774 | 44109 | NANTES | 248.019465 | 138738.544337 | 250814.701179 | 193478.248177 | 18162.261628 | 17461.400209 | 77897.138554 | 354259.013785 | 221068.632724 | 173447.582779 |
ou alors utiliser une réassignation plus classique:
= df.copy()
df_copy "INSEE commune"].str.startswith("92")] = np.nan df_copy[df_copy[
Il est conseillé de filtrer avec loc
en utilisant un masque.
En effet, contrairement à df[mask]
, df.loc[mask, :]
permet d’indiquer clairement
à Python que l’on souhaite appliquer le masque aux labels de l’index.
Ce n’est pas le cas avec df[mask]
.
D’ailleurs, lorsqu’on utilise la syntaxe df[mask]
, pandas
renvoie généralement un warning
6.4 Opérations par groupe
En SQL
, il est très simple de découper des données pour
effectuer des opérations sur des blocs cohérents et recollecter des résultats
dans la dimension appropriée.
La logique sous-jacente est celle du split-apply-combine qui est repris
par les langages de manipulation de données, auxquels pandas
ne fait pas exception.
L’image suivante, issue de
ce site
représente bien la manière dont fonctionne l’approche
split
-apply
-combine
Ce tutoriel sur le sujet est particulièrement utile.
Pour donner quelques exemples, on peut créer une variable départementale qui servira de critère de groupe.
"dep"] = df["INSEE commune"].str[:2] df[
En pandas
, on utilise groupby
pour découper les données selon un ou
plusieurs axes. Techniquement, cette opération consiste à créer une association
entre des labels (valeurs des variables de groupe) et des
observations.
Par exemple, pour compter le nombre de communes par département en SQL, on utiliserait la requête suivante :
SELECT dep, count(INSEE commune)
FROM df
GROUP BY dep;
Ce qui, en pandas
, donne:
"dep")["INSEE commune"].count() df.groupby(
dep
01 410
02 805
03 318
04 199
05 168
...
91 196
92 36
93 40
94 47
95 185
Name: INSEE commune, Length: 96, dtype: int64
La syntaxe est quasiment transparente. On peut bien sûr effectuer des opérations par groupe sur plusieurs colonnes. Par exemple,
"dep").mean(numeric_only=True) df.groupby(
Agriculture | Autres transports | Autres transports international | CO2 biomasse hors-total | Déchets | Energie | Industrie hors-énergie | Résidentiel | Routier | Tertiaire | |
---|---|---|---|---|---|---|---|---|---|---|
dep | ||||||||||
01 | 1974.535382 | 100.307344 | 8.900375 | 1736.353087 | 671.743966 | 280.485435 | 1744.567552 | 1346.982227 | 3988.658995 | 1021.089078 |
02 | 1585.417729 | 202.878748 | 17.390638 | 767.072924 | 223.907551 | 76.316247 | 932.135611 | 793.615867 | 1722.240298 | 403.744266 |
03 | 6132.029417 | 240.076499 | 45.429978 | 1779.630883 | 349.746819 | 326.904841 | 1452.423506 | 1401.650215 | 3662.773062 | 705.937016 |
04 | 1825.455590 | 177.321816 | NaN | 583.198128 | 253.975910 | 62.808435 | 313.913553 | 587.116013 | 1962.654370 | 493.609329 |
05 | 1847.508592 | 141.272766 | NaN | 502.012857 | 132.548068 | 34.971220 | 102.649239 | 728.734494 | 2071.010178 | 463.604908 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
91 | 802.793163 | 10114.998156 | 73976.107892 | 3716.906101 | 1496.516194 | 538.761253 | 1880.810170 | 6532.123033 | 10578.452789 | 3866.757200 |
92 | 8.309835 | 362.964554 | 13.132461 | 29663.579634 | 7347.163353 | 6745.611611 | 19627.706224 | 40744.279029 | 33289.456629 | 23222.587595 |
93 | 50.461775 | 1753.443710 | 61188.896632 | 18148.789684 | 6304.173594 | 2570.941598 | 10830.409025 | 32911.305703 | 35818.236459 | 21575.444794 |
94 | 48.072971 | 5474.808839 | 16559.384091 | 14710.744314 | 4545.099181 | 1624.281505 | 9940.192318 | 28444.561597 | 24881.531613 | 16247.876321 |
95 | 609.172047 | 682.143912 | 37984.576873 | 3408.871963 | 1334.032970 | 463.860672 | 1729.692179 | 6684.181989 | 8325.948748 | 4014.985843 |
96 rows × 10 columns
A noter que la variable de groupe, ici dep
, devient, par défaut, l’index
du DataFrame de sortie. Si on avait utilisé plusieurs variables de groupe,
on obtiendrait un objet multi-indexé. Sur la gestion des multi-index
, on
pourra se référer à l’ouvrage Modern Pandas
dont la référence est
donnée en fin de cours.
Tant qu’on n’appelle pas une action sur un DataFrame
par groupe, du type
head
ou display
, pandas
n’effectue aucune opération. On parle de
lazy evaluation. Par exemple, le résultat de df.groupby('dep')
est
une transformation qui n’est pas encore évaluée :
"dep") df.groupby(
<pandas.core.groupby.generic.DataFrameGroupBy object at 0x7f16ca495410>
Il est possible d’appliquer plus d’une opération à la fois grâce à la méthode
agg
. Par exemple, pour obtenir à la fois le minimum, la médiane et le maximum
de chaque département, on peut faire:
= df.select_dtypes(["number"]).columns
numeric_columns + ["dep"]].groupby("dep").agg(
df.loc[:, numeric_columns.tolist() "min", "median", "max"], numeric_only=True
[ )
Agriculture | Autres transports | Autres transports international | CO2 biomasse hors-total | ... | Industrie hors-énergie | Résidentiel | Routier | Tertiaire | |||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
min | median | max | min | median | max | min | median | max | min | ... | max | min | median | max | min | median | max | min | median | max | |
dep | |||||||||||||||||||||
01 | 0.003432 | 1304.519570 | 14402.057335 | 3.307596 | 75.686090 | 617.281080 | 0.297256 | 6.985161 | 2.209492e+01 | 30.571400 | ... | 175185.892467 | 9.607822 | 351.182294 | 57689.832901 | 20.848982 | 1598.934149 | 45258.256406 | 10.049230 | 401.490676 | 30847.366865 |
02 | 0.391926 | 1205.725078 | 13257.716591 | 0.326963 | 130.054615 | 1126.961565 | 0.517437 | 15.492120 | 5.799402e+01 | 28.294993 | ... | 220963.067245 | 7.849347 | 138.819865 | 99038.124236 | 22.936184 | 700.826152 | 49245.101730 | 6.220952 | 130.639994 | 34159.345750 |
03 | 5.041238 | 5382.194339 | 24912.249269 | 24.158870 | 144.403590 | 1433.217868 | 29.958027 | 42.762328 | 8.269019e+01 | 44.825515 | ... | 154061.446374 | 19.441088 | 217.959697 | 75793.882483 | 120.667614 | 1426.905646 | 40957.845304 | 17.705787 | 191.892445 | 31099.772884 |
04 | 30.985972 | 1404.752852 | 11423.535554 | 33.513854 | 158.780500 | 362.637639 | NaN | NaN | NaN | 7.162928 | ... | 16889.531061 | 1.708652 | 133.130946 | 18088.189529 | 30.206298 | 687.390045 | 31438.078325 | 0.957070 | 122.504902 | 16478.024806 |
05 | 38.651727 | 1520.896526 | 13143.465812 | 0.299734 | 139.754980 | 456.042002 | NaN | NaN | NaN | 20.931602 | ... | 4271.129851 | 6.871678 | 211.945147 | 46486.555748 | 57.132270 | 958.506314 | 37846.651181 | 4.785348 | 151.695524 | 23666.235898 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
91 | 0.400740 | 516.908303 | 5965.349174 | 25.785594 | 177.177127 | 513140.971691 | 1.651873 | 14.762210 | 7.858782e+05 | 41.661474 | ... | 50288.560827 | 15.886514 | 2580.902085 | 48464.979708 | 20.260110 | 3610.009634 | 72288.020125 | 36.368643 | 1428.426303 | 38296.204729 |
92 | 0.073468 | 6.505185 | 32.986132 | 7.468879 | 297.529178 | 1250.483441 | 1.104401 | 11.482381 | 3.423467e+01 | 2173.614704 | ... | 95840.512400 | 4122.277198 | 33667.904692 | 92216.971456 | 4968.382962 | 23516.458236 | 113716.853033 | 800.588678 | 18086.633085 | 65043.364499 |
93 | 3.308495 | 3.308495 | 1362.351634 | 24.188172 | 320.755486 | 45251.869710 | 0.171075 | 12.449476 | 1.101146e+06 | 899.762120 | ... | 89135.302368 | 4364.038661 | 31428.227282 | 87927.730552 | 1632.496185 | 22506.758771 | 193039.792609 | 2257.370945 | 20864.923339 | 71918.163984 |
94 | 1.781885 | 1.781885 | 556.939161 | 6.249609 | 294.204166 | 103252.271268 | 0.390223 | 14.944807 | 1.571965e+05 | 928.232154 | ... | 96716.055178 | 2668.358896 | 24372.900300 | 100948.169898 | 1266.101605 | 19088.651049 | 97625.957714 | 1190.115985 | 14054.223449 | 58528.623477 |
95 | 8.779506 | 445.279844 | 2987.287417 | 1.749091 | 80.838639 | 44883.982753 | 0.201508 | 13.149987 | 1.101131e+06 | 13.490977 | ... | 66216.914749 | 11.585833 | 1434.343631 | 104543.465908 | 2.619451 | 3417.197938 | 147040.904455 | 11.484835 | 725.467969 | 61497.821477 |
96 rows × 30 columns
La première ligne est présente pour nous faciliter la récupération des noms de colonnes des variables numériques
6.5 Appliquer des fonctions
pandas
est, comme on a pu le voir, un package très flexible, qui
propose une grande variété de méthodes optimisées. Cependant, il est fréquent
d’avoir besoin de méthodes non implémentées.
Dans ce cas, on recourt souvent aux lambda
functions. Par exemple, si
on désire connaître les communes dont le nom fait plus de 40 caractères,
on peut appliquer la fonction len
de manière itérative:
# Noms de communes superieurs à 40 caracteres
"Commune"].apply(lambda s: len(s) > 40)] df[df[
INSEE commune | Commune | Agriculture | Autres transports | Autres transports international | CO2 biomasse hors-total | Déchets | Energie | Industrie hors-énergie | Résidentiel | Routier | Tertiaire | dep | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
28082 | 70058 | BEAUJEU-SAINT-VALLIER-PIERREJUX-ET-QUITTEUR | 4024.909815 | 736.948351 | 41.943384 | 1253.135313 | 125.101996 | 2.354558 | 6.911213 | 549.734302 | 1288.215480 | 452.693897 | 70 |
4984 | 14621 | SAINT-MARTIN-DE-BIENFAITE-LA-CRESSONNIERE | 1213.333523 | NaN | NaN | 677.571743 | 72.072503 | 63.573059 | 186.602760 | 298.261044 | 1396.353375 | 260.801452 | 14 |
19276 | 51513 | SAINT-REMY-EN-BOUZEMONT-SAINT-GENEST-ET-ISSON | 1927.401921 | NaN | NaN | 595.583152 | 71.675773 | 4.709115 | 13.822427 | 273.826687 | 521.864748 | 259.365848 | 51 |
5402 | 16053 | BORS (CANTON DE BAIGNES-SAINTE-RADEGONDE) | 1919.249545 | NaN | NaN | 165.443226 | 16.265904 | 2.354558 | 6.911213 | 54.561623 | 719.293151 | 58.859777 | 16 |
Cependant, toutes les lambda
functions ne se justifient pas.
Par exemple, prenons
le résultat d’agrégation précédent. Imaginons qu’on désire avoir les résultats
en milliers de tonnes. Dans ce cas, le premier réflexe est d’utiliser
la lambda
function suivante :
= df.select_dtypes(["number"]).columns
numeric_columns
(+ ["dep"]]
df.loc[:, numeric_columns.tolist() "dep")
.groupby("min", "median", "max"])
.agg([apply(lambda s: s / 1000)
. )
Agriculture | Autres transports | Autres transports international | CO2 biomasse hors-total | ... | Industrie hors-énergie | Résidentiel | Routier | Tertiaire | |||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
min | median | max | min | median | max | min | median | max | min | ... | max | min | median | max | min | median | max | min | median | max | |
dep | |||||||||||||||||||||
01 | 0.000003 | 1.304520 | 14.402057 | 0.003308 | 0.075686 | 0.617281 | 0.000297 | 0.006985 | 0.022095 | 0.030571 | ... | 175.185892 | 0.009608 | 0.351182 | 57.689833 | 0.020849 | 1.598934 | 45.258256 | 0.010049 | 0.401491 | 30.847367 |
02 | 0.000392 | 1.205725 | 13.257717 | 0.000327 | 0.130055 | 1.126962 | 0.000517 | 0.015492 | 0.057994 | 0.028295 | ... | 220.963067 | 0.007849 | 0.138820 | 99.038124 | 0.022936 | 0.700826 | 49.245102 | 0.006221 | 0.130640 | 34.159346 |
03 | 0.005041 | 5.382194 | 24.912249 | 0.024159 | 0.144404 | 1.433218 | 0.029958 | 0.042762 | 0.082690 | 0.044826 | ... | 154.061446 | 0.019441 | 0.217960 | 75.793882 | 0.120668 | 1.426906 | 40.957845 | 0.017706 | 0.191892 | 31.099773 |
04 | 0.030986 | 1.404753 | 11.423536 | 0.033514 | 0.158781 | 0.362638 | NaN | NaN | NaN | 0.007163 | ... | 16.889531 | 0.001709 | 0.133131 | 18.088190 | 0.030206 | 0.687390 | 31.438078 | 0.000957 | 0.122505 | 16.478025 |
05 | 0.038652 | 1.520897 | 13.143466 | 0.000300 | 0.139755 | 0.456042 | NaN | NaN | NaN | 0.020932 | ... | 4.271130 | 0.006872 | 0.211945 | 46.486556 | 0.057132 | 0.958506 | 37.846651 | 0.004785 | 0.151696 | 23.666236 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
91 | 0.000401 | 0.516908 | 5.965349 | 0.025786 | 0.177177 | 513.140972 | 0.001652 | 0.014762 | 785.878155 | 0.041661 | ... | 50.288561 | 0.015887 | 2.580902 | 48.464980 | 0.020260 | 3.610010 | 72.288020 | 0.036369 | 1.428426 | 38.296205 |
92 | 0.000073 | 0.006505 | 0.032986 | 0.007469 | 0.297529 | 1.250483 | 0.001104 | 0.011482 | 0.034235 | 2.173615 | ... | 95.840512 | 4.122277 | 33.667905 | 92.216971 | 4.968383 | 23.516458 | 113.716853 | 0.800589 | 18.086633 | 65.043364 |
93 | 0.003308 | 0.003308 | 1.362352 | 0.024188 | 0.320755 | 45.251870 | 0.000171 | 0.012449 | 1101.145545 | 0.899762 | ... | 89.135302 | 4.364039 | 31.428227 | 87.927731 | 1.632496 | 22.506759 | 193.039793 | 2.257371 | 20.864923 | 71.918164 |
94 | 0.001782 | 0.001782 | 0.556939 | 0.006250 | 0.294204 | 103.252271 | 0.000390 | 0.014945 | 157.196520 | 0.928232 | ... | 96.716055 | 2.668359 | 24.372900 | 100.948170 | 1.266102 | 19.088651 | 97.625958 | 1.190116 | 14.054223 | 58.528623 |
95 | 0.008780 | 0.445280 | 2.987287 | 0.001749 | 0.080839 | 44.883983 | 0.000202 | 0.013150 | 1101.131222 | 0.013491 | ... | 66.216915 | 0.011586 | 1.434344 | 104.543466 | 0.002619 | 3.417198 | 147.040904 | 0.011485 | 0.725468 | 61.497821 |
96 rows × 30 columns
En effet, cela effectue le résultat désiré. Cependant, il y a mieux : utiliser
la méthode div
:
import timeit
= df.loc[:, numeric_columns.tolist() + ["dep"] ]
df_numeric %timeit df_numeric.groupby('dep').agg(['min',"median","max"]).div(1000)
%timeit df_numeric.groupby('dep').agg(['min',"median","max"]).apply(lambda s: s/1000)
La méthode div
est en moyenne plus rapide et a un temps d’exécution
moins variable. Dans ce cas, on pourrait même utiliser le principe
du broadcasting de numpy (cf. chapitre numpy) qui offre
des performances équivalentes:
%timeit df_numeric.groupby('dep').agg(['min',"median","max"])/1000
apply
est plus rapide qu’une boucle (en interne, apply
utilise Cython
pour itérer) mais reste moins rapide qu’une solution vectorisée quand
elle existe. Ce site
propose des solutions, par exemple les méthodes isin
ou digitize
, pour
éviter de manuellement créer des boucles lentes.
En particulier, il faut noter que apply
avec le paramètre axis=1
est en générale lente.
7 Joindre des données
Il est commun de devoir combiner des données issues de sources différentes. Nous allons ici nous focaliser sur le cas le plus favorable qui est la situation où une information permet d’apparier de manière exacte deux bases de données (autrement nous serions dans une situation, beaucoup plus complexe, d’appariement flou2).
La situation typique est l’appariement entre deux sources de données selon un identifiant individuel. Ici, il s’agit d’un identifiant de code commune.
Il est recommandé de lire ce guide assez complet sur la question des jointures avec R
qui donne des recommandations également utiles pour un utilisateur de Python
.
On utilise de manière indifférente les termes merge ou join.
Le deuxième terme provient de la syntaxe SQL.
En Pandas
, dans la plupart des cas, on peut utiliser indifféremment df.join
et df.merge
Il est aussi possible de réaliser un merge en utilisant la fonction pandas.concat()
avec axis=1
.
Se référer à la documentation de concat
pour voir les options possibles.
8 Restructurer des données (reshape)
On présente généralement deux types de données :
- format wide : les données comportent des observations répétées, pour un même individu (ou groupe), dans des colonnes différentes
- format long : les données comportent des observations répétées, pour un même individu, dans des lignes différentes avec une colonne permettant de distinguer les niveaux d’observations
Un exemple de la distinction entre les deux peut être emprunté à l’ouvrage de référence d’Hadley Wickham, R for Data Science :
L’aide mémoire suivante aidera à se rappeler les fonctions à appliquer si besoin :
Le fait de passer d’un format wide au format long (ou vice-versa) peut être extrêmement pratique car
certaines fonctions sont plus adéquates sur une forme de données ou sur l’autre.
En règle générale, avec Python
comme avec R
, les formats long sont souvent préférables.
Le chapitre suivant, qui fait office de TP, proposera des applications de ces principes :
9 Pandas
dans une chaîne d’opérations
9.1 Les pipe
En général, dans un projet, le nettoyage de données va consister en un ensemble de
méthodes appliquées à un pandas.DataFrame
.
On a vu que assign
permettait de créer une variable dans un DataFrame
.
Il est également possible d’appliquer une fonction, appelée par exemple my_udf
au
DataFrame grâce à pipe
:
= pd.read_csv(path2data).pipe(my_udf) df
L’utilisation des pipe
rend le code très lisible et peut être très
pratique lorsqu’on enchaine des opérations sur le même
dataset.
9.2 Quelques enjeux de performance
La librairie Dask
intègre la structure de numpy
, pandas
et sklearn
.
Elle a vocation à traiter de données en grande dimension, ainsi elle ne sera pas
optimale pour des données qui tiennent très bien en RAM.
Il s’agit d’une librairie construite sur la parallélisation.
Un chapitre dans ce cours lui est consacré.
Pour aller plus loin, se référer à la documentation de Dask
.
Références
Le site pandas.pydata fait office de référence
Le livre
Modern Pandas
de Tom Augspurger : https://tomaugspurger.github.io/modern-1-intro.html
McKinney, Wes. 2012. Python for Data Analysis: Data Wrangling with Pandas, NumPy, and IPython. " O’Reilly Media, Inc.".
Informations additionnelles
environment files have been tested on.
Latest built version: 2024-04-27
Python version used:
'3.11.6 | packaged by conda-forge | (main, Oct 3 2023, 10:40:35) [GCC 12.3.0]'
Package | Version |
---|---|
affine | 2.4.0 |
aiobotocore | 2.12.2 |
aiohttp | 3.9.3 |
aioitertools | 0.11.0 |
aiosignal | 1.3.1 |
alembic | 1.13.1 |
aniso8601 | 9.0.1 |
annotated-types | 0.6.0 |
appdirs | 1.4.4 |
archspec | 0.2.3 |
astroid | 3.1.0 |
asttokens | 2.4.1 |
attrs | 23.2.0 |
Babel | 2.14.0 |
bcrypt | 4.1.2 |
beautifulsoup4 | 4.12.3 |
black | 24.4.2 |
blinker | 1.7.0 |
blis | 0.7.11 |
bokeh | 3.4.0 |
boltons | 23.1.1 |
boto3 | 1.34.51 |
botocore | 1.34.51 |
branca | 0.7.1 |
Brotli | 1.1.0 |
cachetools | 5.3.3 |
cartiflette | 0.0.2 |
Cartopy | 0.23.0 |
catalogue | 2.0.10 |
cattrs | 23.2.3 |
certifi | 2024.2.2 |
cffi | 1.16.0 |
charset-normalizer | 3.3.2 |
click | 8.1.7 |
click-plugins | 1.1.1 |
cligj | 0.7.2 |
cloudpathlib | 0.16.0 |
cloudpickle | 3.0.0 |
colorama | 0.4.6 |
comm | 0.2.2 |
commonmark | 0.9.1 |
conda | 24.3.0 |
conda-libmamba-solver | 24.1.0 |
conda-package-handling | 2.2.0 |
conda_package_streaming | 0.9.0 |
confection | 0.1.4 |
contextily | 1.6.0 |
contourpy | 1.2.1 |
cryptography | 42.0.5 |
cycler | 0.12.1 |
cymem | 2.0.8 |
cytoolz | 0.12.3 |
dask | 2024.4.1 |
dask-expr | 1.0.10 |
debugpy | 1.8.1 |
decorator | 5.1.1 |
dill | 0.3.8 |
distributed | 2024.4.1 |
distro | 1.9.0 |
docker | 7.0.0 |
duckdb | 0.10.1 |
en-core-web-sm | 3.7.1 |
entrypoints | 0.4 |
et-xmlfile | 1.1.0 |
exceptiongroup | 1.2.0 |
executing | 2.0.1 |
fastjsonschema | 2.19.1 |
fiona | 1.9.6 |
flake8 | 7.0.0 |
Flask | 3.0.2 |
folium | 0.16.0 |
fontawesomefree | 6.5.1 |
fonttools | 4.51.0 |
frozenlist | 1.4.1 |
fsspec | 2023.12.2 |
GDAL | 3.8.4 |
gensim | 4.3.2 |
geographiclib | 2.0 |
geopandas | 0.12.2 |
geoplot | 0.5.1 |
geopy | 2.4.1 |
gitdb | 4.0.11 |
GitPython | 3.1.43 |
google-auth | 2.29.0 |
graphene | 3.3 |
graphql-core | 3.2.3 |
graphql-relay | 3.2.0 |
graphviz | 0.20.3 |
great-tables | 0.5.0 |
greenlet | 3.0.3 |
gunicorn | 21.2.0 |
htmltools | 0.5.1 |
hvac | 2.1.0 |
idna | 3.6 |
imageio | 2.34.1 |
importlib_metadata | 7.1.0 |
importlib_resources | 6.4.0 |
inflate64 | 1.0.0 |
ipykernel | 6.29.3 |
ipython | 8.22.2 |
ipywidgets | 8.1.2 |
isort | 5.13.2 |
itsdangerous | 2.1.2 |
jedi | 0.19.1 |
Jinja2 | 3.1.3 |
jmespath | 1.0.1 |
joblib | 1.3.2 |
jsonpatch | 1.33 |
jsonpointer | 2.4 |
jsonschema | 4.21.1 |
jsonschema-specifications | 2023.12.1 |
jupyter-cache | 1.0.0 |
jupyter_client | 8.6.1 |
jupyter_core | 5.7.2 |
jupyterlab_widgets | 3.0.10 |
kaleido | 0.2.1 |
kiwisolver | 1.4.5 |
kubernetes | 29.0.0 |
langcodes | 3.4.0 |
language_data | 1.2.0 |
lazy_loader | 0.4 |
libmambapy | 1.5.7 |
llvmlite | 0.42.0 |
locket | 1.0.0 |
lxml | 5.2.1 |
lz4 | 4.3.3 |
Mako | 1.3.2 |
mamba | 1.5.7 |
mapclassify | 2.6.1 |
marisa-trie | 1.1.0 |
Markdown | 3.6 |
MarkupSafe | 2.1.5 |
matplotlib | 3.8.3 |
matplotlib-inline | 0.1.6 |
mccabe | 0.7.0 |
menuinst | 2.0.2 |
mercantile | 1.2.1 |
mizani | 0.11.2 |
mlflow | 2.11.3 |
mlflow-skinny | 2.11.3 |
msgpack | 1.0.7 |
multidict | 6.0.5 |
multivolumefile | 0.2.3 |
munkres | 1.1.4 |
murmurhash | 1.0.10 |
mypy | 1.9.0 |
mypy-extensions | 1.0.0 |
nbclient | 0.10.0 |
nbformat | 5.10.4 |
nest_asyncio | 1.6.0 |
networkx | 3.3 |
nltk | 3.8.1 |
numba | 0.59.1 |
numpy | 1.26.4 |
oauthlib | 3.2.2 |
opencv-python-headless | 4.9.0.80 |
openpyxl | 3.1.2 |
OWSLib | 0.28.1 |
packaging | 23.2 |
pandas | 2.2.1 |
paramiko | 3.4.0 |
parso | 0.8.4 |
partd | 1.4.1 |
pathspec | 0.12.1 |
patsy | 0.5.6 |
Pebble | 5.0.7 |
pexpect | 4.9.0 |
pickleshare | 0.7.5 |
pillow | 10.3.0 |
pip | 24.0 |
pkgutil_resolve_name | 1.3.10 |
platformdirs | 4.2.0 |
plotly | 5.19.0 |
plotnine | 0.13.5 |
pluggy | 1.4.0 |
polars | 0.20.18 |
preshed | 3.0.9 |
prometheus_client | 0.20.0 |
prometheus-flask-exporter | 0.23.0 |
prompt-toolkit | 3.0.42 |
protobuf | 4.25.3 |
psutil | 5.9.8 |
ptyprocess | 0.7.0 |
pure-eval | 0.2.2 |
py7zr | 0.20.8 |
pyarrow | 15.0.0 |
pyarrow-hotfix | 0.6 |
pyasn1 | 0.5.1 |
pyasn1-modules | 0.3.0 |
pybcj | 1.0.2 |
pycodestyle | 2.11.1 |
pycosat | 0.6.6 |
pycparser | 2.21 |
pycryptodomex | 3.20.0 |
pydantic | 2.7.1 |
pydantic_core | 2.18.2 |
pyflakes | 3.2.0 |
Pygments | 2.17.2 |
PyJWT | 2.8.0 |
pylint | 3.1.0 |
PyNaCl | 1.5.0 |
pynsee | 0.1.7 |
pyOpenSSL | 24.0.0 |
pyparsing | 3.1.2 |
pyppmd | 1.1.0 |
pyproj | 3.6.1 |
pyshp | 2.3.1 |
PySocks | 1.7.1 |
python-dateutil | 2.9.0 |
python-dotenv | 1.0.1 |
python-magic | 0.4.27 |
pytz | 2024.1 |
pyu2f | 0.1.5 |
pywaffle | 1.1.0 |
PyYAML | 6.0.1 |
pyzmq | 25.1.2 |
pyzstd | 0.15.10 |
QtPy | 2.4.1 |
querystring-parser | 1.2.4 |
rasterio | 1.3.10 |
referencing | 0.34.0 |
regex | 2023.12.25 |
requests | 2.31.0 |
requests-cache | 1.2.0 |
requests-oauthlib | 2.0.0 |
rpds-py | 0.18.0 |
rsa | 4.9 |
Rtree | 1.2.0 |
ruamel.yaml | 0.18.6 |
ruamel.yaml.clib | 0.2.8 |
s3fs | 2023.12.2 |
s3transfer | 0.10.1 |
scikit-image | 0.23.2 |
scikit-learn | 1.4.1.post1 |
scipy | 1.13.0 |
seaborn | 0.13.2 |
setuptools | 69.2.0 |
shapely | 2.0.3 |
six | 1.16.0 |
smart-open | 6.4.0 |
smmap | 5.0.0 |
snuggs | 1.4.7 |
sortedcontainers | 2.4.0 |
soupsieve | 2.5 |
spacy | 3.7.4 |
spacy-legacy | 3.0.12 |
spacy-loggers | 1.0.5 |
SQLAlchemy | 2.0.29 |
sqlparse | 0.4.4 |
srsly | 2.4.8 |
stack-data | 0.6.2 |
statsmodels | 0.14.1 |
tabulate | 0.9.0 |
tblib | 3.0.0 |
tenacity | 8.2.3 |
texttable | 1.7.0 |
thinc | 8.2.3 |
threadpoolctl | 3.4.0 |
tifffile | 2024.4.24 |
tomli | 2.0.1 |
tomlkit | 0.12.4 |
toolz | 0.12.1 |
topojson | 1.8 |
tornado | 6.4 |
tqdm | 4.66.2 |
traitlets | 5.14.2 |
truststore | 0.8.0 |
typer | 0.9.4 |
typing_extensions | 4.11.0 |
tzdata | 2024.1 |
Unidecode | 1.3.8 |
url-normalize | 1.4.3 |
urllib3 | 1.26.18 |
wasabi | 1.1.2 |
wcwidth | 0.2.13 |
weasel | 0.3.4 |
webcolors | 1.13 |
webdriver-manager | 4.0.1 |
websocket-client | 1.7.0 |
Werkzeug | 3.0.2 |
wheel | 0.43.0 |
widgetsnbextension | 4.0.10 |
wordcloud | 1.9.3 |
wrapt | 1.16.0 |
xgboost | 2.0.3 |
xlrd | 2.0.1 |
xyzservices | 2024.4.0 |
yarl | 1.9.4 |
yellowbrick | 1.5 |
zict | 3.0.0 |
zipp | 3.17.0 |
zstandard | 0.22.0 |
View file history
SHA | Date | Author | Description |
---|---|---|---|
c9f9f8a | 2024-04-24 15:09:35 | Lino Galiana | Dark mode and CSS improvements (#494) |
d75641d | 2024-04-22 18:59:01 | Lino Galiana | Editorialisation des chapitres de manipulation de données (#491) |
c03aa61 | 2024-01-16 17:33:18 | Lino Galiana | Exercice sur les chemins relatifs (#483) |
056c606 | 2023-12-20 20:08:25 | linogaliana | Change pandas image |
005d89b | 2023-12-20 17:23:04 | Lino Galiana | Finalise l’affichage des statistiques Git (#478) |
3fba612 | 2023-12-17 18:16:42 | Lino Galiana | Remove some badges from python (#476) |
1684220 | 2023-12-02 12:06:40 | Antoine Palazzolo | Première partie de relecture de fin du cours (#467) |
1f23de2 | 2023-12-01 17:25:36 | Lino Galiana | Stockage des images sur S3 (#466) |
a06a268 | 2023-11-23 18:23:28 | Antoine Palazzolo | 2ème relectures chapitres ML (#457) |
09654c7 | 2023-11-14 15:16:44 | Antoine Palazzolo | Suggestions Git & Visualisation (#449) |
cef6a0d | 2023-10-18 13:18:46 | Lino Galiana | Allègement des actions github (#437) |
97676f5 | 2023-10-14 17:56:44 | Lino Galiana | Du style pour le site (#434) |
7221e7b | 2023-10-10 14:00:44 | Thomas Faria | Relecture Thomas TD Pandas (#431) |
a771183 | 2023-10-09 11:27:45 | Antoine Palazzolo | Relecture TD2 par Antoine (#418) |
ac80862 | 2023-10-07 21:05:25 | Lino Galiana | Relecture antuki (#427) |
7e03cea | 2023-10-04 14:07:17 | Lino Galiana | Clean pandas tutorial and exercises (#417) |
e8d0062 | 2023-09-26 15:54:49 | Kim A | Relecture KA 25/09/2023 (#412) |
154f09e | 2023-09-26 14:59:11 | Antoine Palazzolo | Des typos corrigées par Antoine (#411) |
9a4e226 | 2023-08-28 17:11:52 | Lino Galiana | Action to check URL still exist (#399) |
8082302 | 2023-08-25 17:48:36 | Lino Galiana | Mise à jour des scripts de construction des notebooks (#395) |
3bdf3b0 | 2023-08-25 11:23:02 | Lino Galiana | Simplification de la structure 🤓 (#393) |
c312bdc | 2023-08-11 18:06:25 | Lino Galiana | A few controls for Quarto website (#389) |
5d4874a | 2023-08-11 15:09:33 | Lino Galiana | Pimp les introductions des trois premières parties (#387) |
dde3e93 | 2023-07-21 22:22:05 | Lino Galiana | Fix bug on chapter order (#385) |
3560f1f | 2023-07-21 17:04:56 | Lino Galiana | Build on smaller sized image (#384) |
f146354 | 2023-07-21 18:15:10 | Lino Galiana | Update index.qmd |
f6dde33 | 2023-07-18 22:32:00 | Lino Galiana | Change badges (#376) |
143e706 | 2023-07-18 19:37:28 | Lino Galiana | Améliore la navigation (#375) |
130ed71 | 2023-07-18 19:37:11 | Lino Galiana | Restructure les titres (#374) |
ef28fef | 2023-07-07 08:14:42 | Lino Galiana | Listing pour la première partie (#369) |
64baaf8 | 2023-07-03 17:05:53 | Lino Galiana | Script for branch deploy (#367) |
f21a24d | 2023-07-02 10:58:15 | Lino Galiana | Pipeline Quarto & Pages 🚀 (#365) |
867325e | 2023-06-11 13:56:43 | Lino Galiana | Add numeric_only argument (#359) |
9918817 | 2022-12-30 15:10:59 | Lino Galiana | Retour sur le chapitre DallE / StableDiffusion (#344) |
94e7c0a | 2022-12-29 09:42:35 | Lino Galiana | pip install pynsee (#342) |
a8dd720 | 2022-12-26 21:35:52 | Lino Galiana | Improve aesthetics on Github (#338) |
e2b53ac | 2022-09-28 17:09:31 | Lino Galiana | Retouche les chapitres pandas (#287) |
eb8f922 | 2022-09-22 17:40:43 | Lino Galiana | Corrige bug TP pandas (#276) |
fd439f0 | 2022-09-19 09:37:50 | avouacr | fix ssp cloud links |
3056d41 | 2022-09-02 12:19:55 | avouacr | fix all SSP Cloud launcher links |
8042a16 | 2022-08-24 16:23:36 | Lino Galiana | Box pour les notebooks :sparkles: (#256) |
494a85a | 2022-08-05 14:49:56 | Lino Galiana | Images featured ✨ (#252) |
d201e3c | 2022-08-03 15:50:34 | Lino Galiana | Pimp la homepage ✨ (#249) |
2360ff7 | 2022-08-02 16:29:57 | Lino Galiana | Test wowchemy update (#247) |
d3a5406 | 2022-06-27 17:44:30 | Lino Galiana | Utilisation test du système de référence de quarto (#240) |
1239e3e | 2022-06-21 14:05:15 | Lino Galiana | Enonces (#239) |
48606dd | 2022-05-31 19:05:11 | Lino Galiana | Amélioration rendu dataframe pandas (#229) |
12965ba | 2022-05-25 15:53:27 | Lino Galiana | :launch: Bascule vers quarto (#226) |
9c71d6e | 2022-03-08 10:34:26 | Lino Galiana | Plus d’éléments sur S3 (#218) |
5cac236 | 2021-12-16 19:46:43 | Lino Galiana | un petit mot sur mercator (#201) |
6777f03 | 2021-10-29 09:38:09 | Lino Galiana | Notebooks corrections (#171) |
2a8809f | 2021-10-27 12:05:34 | Lino Galiana | Simplification des hooks pour gagner en flexibilité et clarté (#166) |
5ad057f | 2021-10-10 15:13:16 | Lino Galiana | Relectures pandas & geopandas (#159) |
4870662 | 2021-10-05 08:29:33 | Romain Avouac | fix and simplify pyinsee install (#157) |
0677932 | 2021-10-03 15:32:51 | Lino Galiana | Ajoute un code pour download pynsee (#156) |
2fa78c9 | 2021-09-27 11:24:19 | Lino Galiana | Relecture de la partie numpy/pandas (#152) |
85ba119 | 2021-09-16 11:27:56 | Lino Galiana | Relectures des TP KA avant 1er cours (#142) |
2f4d390 | 2021-09-02 15:12:29 | Lino Galiana | Utilise un shortcode github (#131) |
2e4d586 | 2021-09-02 12:03:39 | Lino Galiana | Simplify badges generation (#130) |
4a317e3 | 2021-08-31 12:38:17 | Lino Galiana | pynsee pour importer des données Insee 🚀 (#127) |
2f7b52d | 2021-07-20 17:37:03 | Lino Galiana | Improve notebooks automatic creation (#120) |
6729a72 | 2021-06-22 18:07:05 | Lino Galiana | Mise à jour badge onyxia (#115) |
4cdb759 | 2021-05-12 10:37:23 | Lino Galiana | :sparkles: :star2: Nouveau thème hugo :snake: :fire: (#105) |
175d377 | 2021-05-04 18:29:26 | Raphaele Adjerad | Quelques manipulations supplémentaires pandas (#106) |
7f9f97b | 2021-04-30 21:44:04 | Lino Galiana | 🐳 + 🐍 New workflow (docker 🐳) and new dataset for modelization (2020 🇺🇸 elections) (#99) |
0a0d034 | 2021-03-26 20:16:22 | Lino Galiana | Ajout d’une section sur S3 (#97) |
6d010fa | 2020-09-29 18:45:34 | Lino Galiana | Simplifie l’arborescence du site, partie 1 (#57) |
66f9f87 | 2020-09-24 19:23:04 | Lino Galiana | Introduction des figures générées par python dans le site (#52) |
76e206c | 2020-09-09 18:02:08 | Lino Galiana | Finalisation du chapitre pandas (#24) |
5c1e76d | 2020-09-09 11:25:38 | Lino Galiana | Ajout des éléments webscraping, regex, API (#21) |
d48e68f | 2020-09-08 18:35:07 | Lino Galiana | Continuer la partie pandas (#13) |
85365ca | 2020-09-05 14:50:10 | linogaliana | ajout badges onyxia |
611be4d | 2020-09-05 14:27:47 | linogaliana | modifs marginales |
0559398 | 2020-09-05 14:22:55 | linogaliana | modifs marginales |
9c12c2c | 2020-09-04 17:39:09 | Lino Galiana | Introduction à pandas (#11) |
Footnotes
A vrai dire, ce n’est pas l’empreinte carbone puisque la base de données correspond à une vision production, pas consommation. Les émissions faites dans une commune pour satisfaire la consommation d’une autre seront imputées à la première là où le concept d’empreinte carbone voudrait qu’on l’impute aux secondes. Il ne s’agit pas, avec cet exercice, de construire une statistique fiable mais plutôt de comprendre la logique de l’association de données pour construire des statistiques descriptives.↩︎
Sur l’appariement flou, se reporter aux chapitres présentant
ElasticSearch
.↩︎
Citation
BibTeX citation:
@book{galiana2023,
author = {Galiana, Lino},
title = {Python Pour La Data Science},
date = {2023},
url = {https://pythonds.linogaliana.fr/},
doi = {10.5281/zenodo.8229676},
langid = {en}
}
For attribution, please cite this work as:
Galiana, Lino. 2023. Python Pour La Data Science. https://doi.org/10.5281/zenodo.8229676.