Construire des graphiques avec Python

Une partie essentielle du travail du data scientist est d’être en mesure de synthétiser une information dans des représentations graphiques percutantes. Ce chapitre permet de découvrir les enjeux de la représentation de données avec Python, l’écosystème pour faire ceci. Il ouvre également à la représentation interactive de données avec Plotly.

Visualisation
Exercice
Auteur·rice

Lino Galiana

Date de publication

2025-08-24

Pour essayer les exemples présents dans ce tutoriel :
View on GitHub Onyxia Onyxia Open In Colab
Compétences à l’issue de ce chapitre
  • Découvrir l’écosystème matplotlib et seaborn pour la construction de graphiques par enrichissement successif de couches.
  • Découvrir le récent écosystème plotnine, qui est une implémentation en Python du package R ggplot2 pour ce type de représentation et qui, grâce à sa grammaire des graphiques, offre une syntaxe puissante pour construire des visualisations de données.
  • Découvrir le principe des représentations interactives HTML (format web) grâce aux packages plotly et altair.
  • Apprendre les enjeux de la représentation graphique, les compromis nécessaires pour construire un message clair et les limites de certaines représentations classiques.

1 Introduction

Ce chapitre est consacré à la visualisation de données et propose une tâche classique du quotidien des data scientists et data engineers : la construction de figures qui viendront alimenter un tableau de bord (dashboard) analytique permettant d’avoir un regard restrospectif sur un phénomène d’intérêt.

Pour faire ceci, nous allons répliquer quelques figures disponibles en ligne sur le portail open data de la ville de Paris . Comme ces figures ne sont pas toujours parfaites du point de vue des bonnes pratiques graphiques, nous allons parfois essayer de rendre l’information représentée plus facilement accessible en modifiant à la marge les figures.

L’objectif principal de ce chapitre est de présenter, rapidement, les principaux écosystèmes de visualisation disponibles en Python afin d’en mesurer les forces et faiblesses et connaître, pour un objectif donné, les principaux points d’entrée. Il ne s’agit pas de faire un inventaire complet des graphiques pouvant être fait avec Python, ce serait long, assez insipide et peu pertinent car des sites le font déjà très bien à partir d’une grande variété d’exemple, notamment le site python-graph-gallery.com/ . L’objectif est plutôt d’illustrer, par la pratique, quelques enjeux liés à l’utilisation des principales librairies graphiques de Python.

Important

Être capable de construire des visualisations de données intéressantes est une compétence nécessaire à tout data scientist ou chercheur. Pour améliorer la qualité de ces visualisations, il est recommandé de suivre certains conseils donnés par des spécialistes de la dataviz sur la sémiologie graphique.

Les bonnes visualisations de données, comme celles du New York Times, reposent certes sur des outils adaptés (des librairies JavaScript) mais aussi sur certaines règles de représentation qui permettent de comprendre en quelques secondes le message d’une visualisation.

Transmettre une information synthétique de manière limpide à un public ne s’inventant pas, il est recommandé de réfléchir à la réception d’une visualisation et aux messages principaux que celle-ci est censée transmettre. Cette présentation d’Eric Mauvière illustre, avec de nombreux exemples, la manière dont des choix de visualisation affectent la pertinence du message délivré.

Parmi les autres ressources que j’ai trouvées utiles par le passé, ce post de blog de datawrapper (une référence dans le domaine de la visualisation) est très intéressant. Ce post de blog d’Albert Rapp montre également comment construire graduellement une bonne visualisation de données et mérite d’être relu de temps en temps. Enfin, parmi les sites à consulter fréquemment sur le sujet, les ressources proposées sur le blog d’Andrew Heiss valent le détour.

On peut distinguer quelques grandes familles de représentations graphiques: les représentations de distributions propres à une variable, les représentations de relations entre plusieurs variables, les cartes qui permettent de représenter dans l’espace une ou plusieurs variables…

Ces familles se ramifient elles-mêmes en de multiples types de figures. Par exemple, selon la nature du phénomène, les représentations de relations peuvent prendre la forme d’une série temporelle (évolution d’une variable dans le temps), d’un nuage de point (corrélation entre deux variables), d’un diagramme en barre (pour souligner le rapport relatif entre les valeurs d’une variable en fonction d’une autre), etc.

Plutôt qu’un inventaire à la Prévert des types de visualisations possibles, ce chapitre et le suivant vont plutôt proposer quelques visualisations qui pourraient donner envie d’aller plus loin dans l’analyse avant la mise en oeuvre d’une forme de modélisation. Ce chapitre est consacré aux visualisations traditionnelles, le suivant est dédié à la cartographie. Ces deux chapitres font partie d’un tout visant à offrir les premiers éléments pour synthétiser l’information présente dans un jeu de données.

Le pas suivant est d’approfondir le travail de communication et de synthèse par le biais de productions pouvant prendre des formes aussi diverses que des rapports, des publications scientifiques ou articles, des présentations, une application interactive, un site web ou des notebooks comme ceux proposés par ce cours. Le principe général est identique quel que soit le medium utilisé et intéresse particulièrement les data scientists lorsqu’ils font appel à de l’exploitation intensive de données et désirent obtenir un output reproductible. Un jour peut-être j’ajouterai un chapitre à ce propos dans ce cours1.

Utiliser une interface interactive pour visualiser les graphiques

Pour les chapitres de visualisation, il est vivement recommandé d’utiliser Python par le biais d’une interface interactive comme un notebook Jupyter (via VSCode ou Jupyter par exemple, cf. le chapitre de présentation des notebooks ).

Cela permet de visualiser les graphiques immédiatement sous chaque cellule de code, de les ajuster facilement, et de tester des modifications en temps réel.

À l’inverse, si l’on exécute des scripts depuis une console classique (par exemple en écrivant dans un fichier .py et en exécutant ligne à ligne avec MAJ+,ENTREE dans VSCode) les graphiques ne vont pas s’afficher dans une fenêtre popup. Cela nécessite de faire des commandes supplémentaires pour les enregistrer, avant d’ouvrir les exports manuellement et pouvoir corriger le cas échéant le code. L’expérience d’apprentissage en devient plus laborieuse.

Données

Ce chapitre s’appuie sur les données de comptage des passages de vélo dans les points de mesure parisiens diffusés sur le site de l’open data de la ville de Paris.

L’exploitation de l’historique récent a été grandement facilité par la diffusion des données au format Parquet, un format moderne plus pratique que le CSV. Pour en savoir plus sur ce format, vous pouvez consulter les ressources évoquées dans le paragraphe consacré à ce format dans le dernier chapitre de la partie consacrée à la manipulation de données .

Code pour importer les données à partir du format Parquet
import os
import requests
from tqdm import tqdm
import pandas as pd
import duckdb

url = "https://minio.lab.sspcloud.fr/lgaliana/data/python-ENSAE/comptage-velo-donnees-compteurs.parquet"
# problem with https://opendata.paris.fr/api/explore/v2.1/catalog/datasets/comptage-velo-donnees-compteurs/exports/parquet?lang=fr&timezone=Europe%2FParis

filename = 'comptage_velo_donnees_compteurs.parquet'


# DOWNLOAD FILE --------------------------------

# Perform the HTTP request and stream the download
response = requests.get(url, stream=True)

if not os.path.exists(filename):
    # Perform the HTTP request and stream the download
    response = requests.get(url, stream=True)

    # Check if the request was successful
    if response.status_code == 200:
        # Get the total size of the file from the headers
        total_size = int(response.headers.get('content-length', 0))

        # Open the file in write-binary mode and use tqdm to show progress
        with open(filename, 'wb') as file, tqdm(
                desc=filename,
                total=total_size,
                unit='B',
                unit_scale=True,
                unit_divisor=1024,
        ) as bar:
            # Write the file in chunks
            for chunk in response.iter_content(chunk_size=1024):
                if chunk:  # filter out keep-alive chunks
                    file.write(chunk)
                    bar.update(len(chunk))
    else:
        print(f"Failed to download the file. Status code: {response.status_code}")
else:
    print(f"The file '{filename}' already exists.")

# READ FILE AND CONVERT TO PANDAS --------------------------

query = """
SELECT id_compteur, nom_compteur, id, sum_counts, date
FROM read_parquet('comptage_velo_donnees_compteurs.parquet')
"""

# READ WITH DUCKDB AND CONVERT TO PANDAS
df = duckdb.sql(query).df()
id_compteur nom_compteur id sum_counts date
0 100003098-101003098 106 avenue Denfert Rochereau NE-SO 100003098 36 2024-01-01 03:00:00+00:00
1 100003098-101003098 106 avenue Denfert Rochereau NE-SO 100003098 27 2024-01-01 04:00:00+00:00
2 100003098-101003098 106 avenue Denfert Rochereau NE-SO 100003098 10 2024-01-01 06:00:00+00:00

Pour importer les librairies graphiques que nous utiliserons dans ce chapitre, il faut faire

import matplotlib.pyplot as plt
import seaborn as sns
from plotnine import *
Warning

Importer des librairies sous la forme from package import * n’est pas une très bonne pratique.

Néanmoins, pour un package comme plotnine, dont nous allons utiliser de nombreuses fonctions, ce serait un peu fastidieux d’importer les fonctions au cas par cas. De plus, cela permet de réutiliser presque tels quels les exemples de code de la librairie R ggplot, nombreux sur internet avec démonstrations visuelles. from package import * est l’équivalent Python de la pratique library(package) en R.

Puisqu’on va régulièrement recréer des variations de la même figure, nous allons créer des variables pour les labels des axes et le titre:

title="Les 10 compteurs avec la moyenne horaire la plus élevée"
xaxis="Nom du compteur"
yaxis="Moyenne horaire"

2 Premières productions graphiques avec l’API Matplotlib de Pandas

Chercher à produire une visualisation parfaite du premier coup est illusoire. Il est beaucoup plus réaliste d’améliorer graduellement une représentation graphique afin, petit à petit, de mettre en avant les effets de structure dans un jeu de données.

Nous allons donc commencer par nous représenter la distribution des passages aux principales stations de mesure. Pour cela nous allons produire rapidement un barplot puis l’améliorer graduellement.

Dans cette partie, nous allons ainsi reproduire les deux premiers graphiques de la page d’analyse des données : Les 10 compteurs avec la moyenne horaire la plus élevée et Les 10 compteurs ayant comptabilisé le plus de vélos. Les valeurs chiffrées des graphiques peuvent être différentes de celles de la page en ligne, c’est normal, car nous ne travaillons pas systématiquement sur les données ayant la même fraîcheur que celles en ligne.

2.1 Comprendre, en quelques mots, le principe de matplotlib

matplotlib date du début des années 2000 et a émergé pour proposer une alternative en Python à la création de graphiques sous Matlab, un logiciel propriétaire de calcul numérique. matplotlib est donc une librairie assez ancienne, antérieure à l’émergence de Python dans l’écosystème du traitement de données. Cela s’en ressent sur la logique de construction de matplotlib qui n’est pas toujours intuitive lorsqu’on est familier de l’écosystème moderne de la data science. Heureusement, il existe de nombreuses librairies qui s’appuient sur matplotlib mais qui visent à fournir une syntaxe plus familière aux data scientists.

matplotlib propose principalement deux niveaux d’abstraction: la figure et les axes. La figure est, en quelque sorte, la “toile” globale qui contient un ou plusieurs axes dans lesquels s’inséreront des graphiques. Selon les cas, il faudra jouer avec les paramètres de figure ou d’axe, ce qui rend très flexible la construction d’un graphique mais peut également être déroutant car on ne sait jamais trop quel niveau d’abstraction il faut modifier pour mettre à jour sa figure2. Comme le montre la Figure 2.1, tous les éléments d’une figure sont paramétrables.

Figure 2.1: Comprendre l’architecture d’une figure matplotlib (Source: documentation officielle)

En pratique, il existe deux manières de créer et mettre à jour sa figure selon qu’on préfère passer par:

  • l’approche explicite, héritière d’une logique de programmation orientée objet, où on crée des objets Figure et Axes et met à jour ceux-ci.
  • l’approche implicite, basée sur l’interface pyplot qui utilise une succession de fonctions pour mettre à jour les objets créés implicitement.
import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(0, 2, 100)  # Sample data.

# Note that even in the OO-style, we use `.pyplot.figure` to create the Figure.
fig, ax = plt.subplots(figsize=(5, 2.7), layout='constrained')
ax.plot(x, x, label='linear')  # Plot some data on the Axes.
ax.plot(x, x**2, label='quadratic')  # Plot more data on the Axes...
ax.plot(x, x**3, label='cubic')  # ... and some more.
ax.set_xlabel('x label')  # Add an x-label to the Axes.
ax.set_ylabel('y label')  # Add a y-label to the Axes.
ax.set_title("Simple Plot")  # Add a title to the Axes.
ax.legend()  # Add a legend.

Source: Documentation officielle de matplotlib

import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(0, 2, 100)  # Sample data.

plt.figure(figsize=(5, 2.7), layout='constrained')
plt.plot(x, x, label='linear')  # Plot some data on the (implicit) Axes.
plt.plot(x, x**2, label='quadratic')  # etc.
plt.plot(x, x**3, label='cubic')
plt.xlabel('x label')
plt.ylabel('y label')
plt.title("Simple Plot")
plt.legend()

Source: Documentation officielle de matplotlib

Ces éléments constituent le minimum pour comprendre la logique de matplotlib. Pour être plus à l’aise avec ces concepts, la pratique répétée est indispensable.

2.2 Découvrir matplotlib par l’intermédiaire de Pandas

Il est souvent pratique de produire un graphique rapidement, sans forcément trop se préoccuper du style mais pour avoir une idée, rapide, de la distribution statistique de ses données. Pour cela, l’intégration de fonctionnalités graphiques basiques dans Pandas est pratique: on peut directement appliquer quelques instructions sur un DataFrame et cela produira une figure matplotlib.

L’exercice 1 a pour objet de découvrir ces instructions et la manière dont le résultat peut rapidement être retravaillé pour avoir des statistiques descriptives visuelles.

Exercice 1 : Produire un premier graphique

Les données comportent plusieurs dimensions pouvant faire l’objet d’une analyse statistique. Nous allons commencer par nous focaliser sur le volume de passage à tel ou tel compteur.

Puisque nous avons comme objectif de synthétiser l’information présente dans notre jeu de données, nous devons d’abord mettre en œuvre quelques agrégations ad hoc pour produire un graphique lisible.

  1. Garder les dix bornes à la moyenne la plus élevée. Comme pour obtenir un graphique ordonné du plus grand au plus petit avec les méthodes plot de Pandas, il faut avoir les données ordonnées du plus petit au plus grand (oui c’est bizarre mais c’est comme ça…), réordonner les données.

  2. En premier lieu, sans se préoccuper des éléments de style ni de la beauté du graphique, créer la structure du barplot (diagramme en bâtons) de la page d’analyse des données.

  3. Pour préparer le travail sur la deuxième figure, ne conserver que les 10 compteurs ayant comptabilisé le plus de vélos.

  4. Comme pour la question 2, créer un barplot pour reproduire la figure 2 de l’open data parisien.

Les 10 principales stations à l’issue de la question 1 représentent celles ayant la moyenne la plus élevée pour le volume de passages de vélos. Ces données réordonnées permettent de créer un graphique lisible et de mettre en avant les stations les plus fréquentées.

sum_counts
nom_compteur
72 boulevard Voltaire NO-SE 159.539148
27 quai de la Tournelle SE-NO 166.927660
Quai d'Orsay E-O 178.842743
35 boulevard de Ménilmontant NO-SE 180.364565
Totem 64 Rue de Rivoli Totem 64 Rue de Rivoli Vélos E-O 190.852164

La Figure 2.2 présente les données sous forme de barplot basique. Bien qu’elle montre les informations essentielles, elle manque de mise en page esthétique, de couleurs harmonieuses et d’annotations claires, nécessaires pour améliorer la lisibilité et l’impact visuel.

Figure 1 (cliquer ici pour rétracter)
Figure 2.2: Première ébauche de la figure “Les 10 compteurs avec la moyenne horaire la plus élevée”
Figure 2 sans travail sur le style (cliquer ici pour rétracter):
Figure 2.3: Première ébauche de la figure “Les 10 compteurs ayant comptabilisés le plus de vélos”

On commence à avoir quelque chose qui transmet un message synthétique sur la nature des données. En l’occurrence, le message qu’on désire faire passer dans cette visualisation est la hiérarchie relative d’usage des stations.

On peut néanmoins remarquer plusieurs éléments problématiques (par exemple les labels) mais aussi des éléments ne correspondant pas (les titres des axes, etc.) ou manquants (le nom du graphique…). La figure est encore brute de décoffrage.

Comme les graphiques produits par Pandas suivent la logique très flexible de matplotlib, il est possible de les customiser. Cependant, cela demande généralement beaucoup de travail et la grammaire matplotlib n’est pas aussi normalisée et intuitive que celle de ggplot en R. Si on désire rester dans l’écosystème matplotlib, il est préférable de directement utiliser seaborn , qui offre quelques arguments prêts à l’emploi. Sinon on peut basculer, comme nous le ferons, sur l’écosystème plotnine qui offrira la syntaxe normalisée de ggplot pour modifier les différents éléments de notre figure.

3 Utiliser directement seaborn

3.1 Comprendre seaborn en quelques lignes

seaborn est une interface haut-niveau au dessus de matplotlib. Ce package offre un ensemble de fonctionnalités pour créer des figures ou des axes matplotlib directement depuis une fonction admettant de nombreux arguments et, si besoin d’aller plus loin dans la customisation, d’utiliser les fonctionnalités de matplotlib pour mettre à jour la figure, que ce soit par le biais de l’approche implicite ou explicite décrites précédemment.

Comme pour matplotlib, seaborn permet de faire la même figure de multiples manières. seaborn hérite de la dualité axes-figures de matplotlib et il faudra souvent jouer avec un niveau ou l’autre. La principale caractéristique de seaborn est d’offrir quelques points d’entrée standardisés, par exemple seaborn.relplot ou seaborn.catplot, et une logique d’inputs basée sur le DataFrame là où matplotlib est structurée autour du array Numpy. Il faut néanmoins être conscient que seaborn souffre globalement des mêmes limites que matplotlib, notamment du caractère peu intuitif des éléments de customisation qui, dès lors qu’on ne les trouvent pas dans les arguments, peuvent devenir un casse-tête à mettre en oeuvre.

La figure comporte maintenant un message mais il est encore peu lisible. Il y a plusieurs manières de faire un barplot en seaborn. Les deux principales sont :

  • sns.catplot ;
  • sns.barplot.

On propose d’utiliser sns.catplot pour cet exercice. Il s’agit d’un point d’entrée assez fréquent pour faire des graphiques d’une variable discrétisée.

3.2 Reproduction de l’exemple précédent avec seaborn

Nous allons nous contenter de reproduire la Figure 2.2 avec seaborn. Pour cela, voici le code nécessaire afin d’avoir un DataFrame prêt à l’emploi:

Code
df1 = (
    df
    .groupby('nom_compteur')
    .agg({'sum_counts': "mean"})
    .sort_values('sum_counts', ascending = False)
    .head(10)
    .sort_values('sum_counts')
)


df1 = df1.reset_index().sort_values("sum_counts", ascending = False)

df1.head()
nom_compteur sum_counts
9 Totem 73 boulevard de Sébastopol S-N 327.091037
8 Totem 73 boulevard de Sébastopol N-S 232.560270
7 Totem 64 Rue de Rivoli Totem 64 Rue de Rivoli ... 230.304195
6 102 boulevard de Magenta SE-NO 219.405306
5 89 boulevard de Magenta NO-SE 217.406990
Exercice 2: reproduire la première figure avec seaborn
  1. Refaire le graphique précédent avec la fonction catplot de seaborn. Pour contrôler la taille du graphique vous pouvez utiliser les arguments height et aspect.

  2. Ajouter les titres des axes et le titre du graphique

  3. Même si cela n’apporte rien en termes d’information, essayez de colorer en rouge, comme sur la figure du portail open data, l’axe des x. Vous pouvez pré-définir un style avec sns.set_style("ticks", {"xtick.color": "red"})

À l’issue de la question 1, c’est-à-dire en utilisant seaborn pour reproduire de manière minimale un barplot, on obtient la Figure 3.1. C’est déjà un peu plus propre que la version précédente (Figure 2.2) et cela peut déjà suffire pour un travail exploratoire.

Figure 3.1

A la fin de l’exercice, on obtient une figure proche de celle qu’on essaie de reproduire. La principale différence tient à l’absence, sur la nôtre, des valeurs numériques.

Figure 3.2

On comprend ainsi que le boulevard de Sébastopol est le plus emprunté, ce qui ne vous surprendra pas si vous faites du vélo à Paris. Néanmoins, si vous n’êtes pas familiers avec la géographie parisienne, cela sera peu informatif pour vous, vous allez avoir besoin d’une représentation graphique supplémentaire : une carte ! Nous verrons ceci lors d’un prochain chapitre.

4 Découvrir Plotnine, la grammaire des graphiques en Python

plotnine est le nouveau venu dans l’écosystème de la visualisation en Python. Cette librairie est développée par Posit, l’entreprise à l’origine de l’éditeur RStudio et de l’écosystème du tidyverse si central dans le langage R. Cette librairie vise à importer la logique de ggplot en Python, c’est-à-dire une grammaire des graphiques normalisée, lisible et flexible héritée de Wilkinson (2011).

Dans cette approche, un graphique est vu comme une succession de couches qui, une fois superposées, donneront la figure suivante. En soi, ce principe n’est pas différent de celui de matplotlib. Néanmoins, la grammaire utilisée par plotnine est beaucoup plus intuitive et normalisée, ce qui offrira beaucoup plus d’autonomie pour modifier sa figure.

La logique de ggplot (et plotnine) image empruntée à Andrew Heiss

La logique de ggplot (et plotnine) image empruntée à Andrew Heiss

Avec plotnine, il n’y a plus de point d’entrée dual figure-axe. Comme l’illustrent les slides ci-dessous :

  1. On initialise une figure
  2. On met à jour les couches (layers), un niveau d’abstraction très général concernant aussi bien les données représentées que les échelles des axes ou la couleur
  3. À la fin, on peut jouer sur l’esthétique en modifiant les labels des axes, de la légende, les titres, etc.

Nous allons avoir besoin de données hiérarchisées pour avoir des barres ordonnées de manière cohérente:

df1["nom_compteur"] = pd.Categorical(
    df1["nom_compteur"],
    categories = df1["nom_compteur"][::-1],
    ordered=True
)
Exercice 4: reproduire la première figure avec plotnine

Ceci est le même exercice que l’exercice 2. L’objectif est de faire cette figure avec plotnine

Pour cet exercice, nous proposons une correction guidée pas à pas permettant d’illustrer la logique de la grammaire des graphiques.

4.1 La trame de la figure: ggplot()

La première étape de toute figure consiste à définir l’objet du graphique, c’est-à-dire les données qui seront visuellement représentées. Cela passe par la déclaration ggplot avec les paramètres suivants:

  • Le DataFrame, premier paramètre de tout appel à ggplot.
  • Les principaux paramètres esthétiques variables - qui s’insèrent dans aes (aesthetics) - qui seront communs aux différentes couches. En l’occurrence nous n’avons que les axes à déclarer mais nous pourrions, selon la nature du graphique, avoir d’autres eshtétiques dont le comportement serait contrôlé par une variable de notre jeu de données : couleur, taille des points, largeur de la courbe, transparence, etc.
ggplot(df1, aes(x="nom_compteur", y="sum_counts"))
Figure 4.1: La trame de base

Cela nous donne la structure du graphique dans laquelle vont s’insérer tous les éléments ultérieurs. Concernant les \(x\) et \(y\) choisis, cette déclaration permettra de définir un diagramme en barre (barplot) vertical. Nous verrons ensuite que nous allons renverser les axes pour le rendre plus lisible mais cela viendra plus tard.

4.2 Ajouter les géométries: geom_*

Les couches graphiques sont définies par la famille des fonctions geom_ selon une logique additive (d’où le +). Le contrôle de celles-ci se fait à deux niveaux:

  • Dans les paramètres définis par aes soit au niveau global (ggplot) soit au niveau spécifique à la géométrie en question (dans l’appel à geom_)
  • Dans les paramètres constants qui s’appliquent uniformément à la couche, définis comme des paramètres constants
(
    ggplot(df1, aes(x="nom_compteur", y="sum_counts")) +
    geom_bar(stat="identity", fill="red")
)
Figure 4.2: La première couche de géométrie (bar)

On peut ajouter plusieurs couches successives. Par exemple, les valeurs numériques affichées pour donner du contexte peuvent être créées à partir de geom_text dont le positionnement sur la figure est géré par les mêmes paramètres que les autres couches:

df1["text"] = df1["sum_counts"].round().astype(int).astype(str)

(
    ggplot(df1, aes(x="nom_compteur", y="sum_counts")) +
    geom_bar(stat="identity", fill="red") +
1    geom_text(aes(label = "text"), position=position_nudge(y=30))
)
1
Ce paramètre de position est inutile, voire gênant, pour le moment. Mais il nous servira ultérieurement à décaler le label (cf. documentation de plotnine ) quand nous aurons inversé les axes.
Figure 4.3: La seconde couche de géométrie (texte)

4.3 Modifier les échelles: scale_*

L’harmonisation des déclarations d’éléments visuels permise par la grammaire des graphiques se fait avec les géométries geom_*. Il est donc logique que le contrôle de leur comportement se fasse aussi de manière uniformisée, par le biais d’une autre famille de fonctions: les scale_ (scale_x_discrete, scale_x_continuous, scale_color_discrete…).

Ainsi, chaque esthétique (x, y, color, fill, size, etc.) peut être finement paramétrée de manière systématique via sa propre échelle (scale_*). Ceci offre un contrôle quasi total sur la traduction visuelle des données.

On peut aussi ranger dans cette catégorie les fonctions de la famille coord_* qui modifient le système de coordonnées. En l’occurrence, on va se servir de coord_flip pour avoir un diagramme en barre verticales.

(
    ggplot(df1, aes(x="nom_compteur", y="sum_counts")) +
    geom_bar(stat="identity", fill="red") +
    geom_text(aes(label = "text"), position=position_nudge(y=30)) +
    scale_y_continuous(expand=(0, 40)) +
    coord_flip()
)
Figure 4.4: L’échelle de notre graphique

Ici on a peu de paramètres à modifier puisque nos échelles nous conviennent déjà bien (on a pas à utiliser le log pour compresser l’échelle, à appliquer une palette de couleurs…). On va juste aggrandir un peu l’axe des \(x\) pour pouvoir rentrer nos valeurs numériques. Comme avant d’échanger les coordonnées avec coord_flip l’axe en question est \(y\), on va donc jouer avec scale_y_continuous.

4.4 Les labels et les thèmes

La fin de la déclaration de notre figure se fait à travers les éléments de forme que sont les labels (axes, titres, notes de lecture…) et le thème (préconfiguré à travers la famille theme_ ou personnalisé avec les paramètres de la fonction theme). Avant cela, réduisons la taille de nos labels de \(y\)

import textwrap

def wrap_label(s, width=30):
    return '\n'.join(textwrap.wrap(s, width=width))

df1["nom_compteur"] = df1["nom_compteur"].apply(wrap_label)

On peut maintenant personnaliser notre figure:

p = ( 
    ggplot(df1, aes(x="nom_compteur", y="sum_counts")) +
    geom_bar(stat="identity", fill="red") +
    geom_text(aes(label = "text"), position=position_nudge(y=30)) +
    scale_y_continuous(expand=(0, 40)) +
    coord_flip() +
    labs(
        title=title,
        x=xaxis,
        y=yaxis
    ) +
    theme(
        panel_background=element_rect(fill="white"),
        line=element_line(color="white"),
        axis_text_x=element_text(angle=45, hjust=1, color="red"),
        axis_title_x=element_text(color="red"),
    )
)

p
Figure 4.5

Les compteurs parisiens où le volume de cycliste est le plus important.

Figure 4.6

Bien que rapide, cette plongée dans l’univers de la grammaire des graphiques de ggplot montre à quel point celle-ci est intuitive - lorsqu’on comprend sa logique - et puissante.

Caution

Pour contextualiser les données temporelles, on utilise généralement des dates sur l’axe des abscisses (x). Pour éviter de rendre celles-ci illisibles, il faut éviter de donner trop de détail (trop de valeurs affichées, affichage des jours quand les mois suffisent…).

Tourner le texte verticalement pour faire rentrer plus de texte sur l’axe horizontal n’est pas une bonne idée: l’effet principal de cela est de donner un torticolis au lecteur de votre graphique. Il vaut mieux afficher moins de labels, quitte à faire une note de lecture pour les dates vraiment spéciales.

5 Visualisations alternatives

Jusqu’à présent, nous avons consciencieusement reproduit les visualisations proposées sur le tableau de bord de l’open data parisien. Mais nous pourrions vouloir faire passer la même information avec des visualisations différentes:

  • Les lollipop chart ressemblent beaucoup aux diagrammes en barre mais l’information visuelle est un peu plus efficiente: on n’a pas une grosse barre pour représenter les grandeurs mais une ligne plus fine ce qui peut aider à vraiment percevoir les échelles de grandeur dans les données.
  • Puisque on a besoin de contextualiser la figure avec les valeurs exactes - en attendant de découvrir le monde de l’interactivité - pourquoi ne pas utiliser un tableau et y insérer des graphiques ? Les tableaux ne sont pas un mauvais medium de communication, au contraire, s’ils proposent une information visuelle hiérarchisée ils peuvent mettre être très utiles !

5.1 Les lollipop chart

Les diagrammes en bâtons (barplot) sont extrêmement communs, sans doute à cause de l’héritage d’Excel où ces graphiques sont faisables en deux clics. Néanmoins, en ce qui concerne le message à transmettre, ils sont loin d’être parfaits. Par exemple, les barres prennent beaucoup d’espace visuel, ce qui peut brouiller le message à transmettre sur le rapport entre les observations.

Sur le plan sémiologique, c’est-à-dire sur le plan de l’efficacité du message à transmettre, les lollipop charts sont préférables : ils transmettent la même information mais avec moins de signes visuels pouvant brouiller sa compréhension.

Les lollipop charts ne sont pas parfaits non plus mais sont un peu plus efficaces pour transmettre le message. Pour en savoir plus sur les alternatives au barplot, la conférence d’Eric Mauvière pour le réseau des data scientists de la statistique publique, dont le message principal est “Désempilez vos figures”, mérite le détour (disponible sur le site ssphub.netlify.app/ ).

Avec plotnine, il n’est pas trop complexe de créer un lollipop chart. Il suffit d’avoir deux géométries:

  1. Le manche de la sucette est créé avec un geom_segment ;
  2. Le bout de la sucette est créé avec un geom_point
p = (
    ggplot(df1, aes(x="nom_compteur", y="sum_counts")) +
    geom_segment(aes(x="nom_compteur", xend="nom_compteur", y=0, yend="sum_counts"), size=1) +
    geom_point(color="white", fill="red", size=6, stroke=1, shape="o") +
    coord_flip() +
    labs(
        title=title,
        x=xaxis,
        y=yaxis
    ) +
    theme_minimal()
)

p
Figure 5.1
p = (
    ggplot(df1, aes(x="nom_compteur", y="sum_counts")) +
    geom_segment(
        aes(x="nom_compteur", xend="nom_compteur", y=0, yend="sum_counts"), size=1, color = "white"
    ) +
    geom_point(color="white", fill="red", size=6, stroke=1, shape="o") +
    coord_flip() +
    labs(
        title=title,
        x=xaxis,
        y=yaxis
    ) +
    theme_minimal() +
    theme(
        plot_background=element_rect(fill="black"),
        panel_background=element_rect(fill="black"),
        line=element_line(color="black"),
        axis_text_x=element_text(color="white"),
        axis_title_x=element_text(color="white"),
        text=element_text(color="white"),
        plot_title=element_text(ha="left")
    )
)

p
Figure 5.2

Grâce à cette représentation alternative, on se représente ici mieux la différence entre le compteur le plus utilisé et les autres.

Le lollipop chart est une représentation assez classique en biostatistiques ou économie pour représenter les odds ratio issus d’une modélisation logistique. En l’occurrence, les lignes servent généralement à représenter la taille de l’intervalle de confiance dans cette littérature.

Une variante des lollipop charts pour représenter les odds ratio (Galiana et al. 2022)

Une variante des lollipop charts pour représenter les odds ratio (Galiana et al. 2022)

Une variante des lollipop chart, popularisée notamment par datawrapper permet aussi de représenter des intervalles: le range plot. Il permet à la fois de se représenter la hiérarchie entre des observations et l’amplitude d’un phénomène.

Exemple de range plot par Eric Mauvière (ssphub.netlify.app/ ).

Exemple de range plot par Eric Mauvière (ssphub.netlify.app/ ).

5.2 Un tableau stylisé

Les tableaux sont de bons medium pour communiquer des valeurs précises. Mais sans l’ajout d’éléments contextuels, comme des intensités de couleurs ou des figures, ils aident peu à percevoir visuellement les écrats ou les ordres de grandeur.

Grâce à la richesse du format HTML - qui autorise l’insertion de graphismes légers directement dans les cellules - on peut combiner précision numérique et lisibilité visuelle. On a donc la possibilité d’avoir le meilleur des deux mondes.

Nous avons déjà utilisé précédemment le package great_tables pour représenter des statistiques agrégées. Nous allons ici l’utiliser pour intégrer un lollipop chart dans un tableau, permettant une lecture immédiate des valeurs tout en conservant leur exactitude.

Nous allons en profiter pour nettoyer un peu le texte à afficher en retirant les labels doublonnés et en isolant la direction.

Code
df1["direction"] = df1["nom_compteur"].str.extract(
    r"([A-Z]{1,3}-[A-Z]{1,3})$"
)
df1["nom_compteur"] = df1["nom_compteur"].str.replace(
    r"([A-Z]{1,3}-[A-Z]{1,3})$", "", regex=True
)

def deduplicate_label(label):
    parts = label.split()
    mid = len(parts) // 2
    for i in range(1, mid + 1):
        if parts[:i] == parts[i:2*i]:
            return ' '.join(parts[i:])
    return label

df1["nom_compteur"] = df1["nom_compteur"].apply(deduplicate_label)
df1["nom_compteur"] = df1['nom_compteur'].str.replace("(Vélos|Totem)", "", regex=True)

df1.head()
nom_compteur sum_counts text direction
9 73 boulevard de\nSébastopol 327.091037 327 S-N
8 73 boulevard de\nSébastopol 232.560270 233 N-S
7 64 Rue de Rivoli 230.304195 230 O-E
6 102 boulevard de Magenta 219.405306 219 SE-NO
5 89 boulevard de Magenta 217.406990 217 NO-SE

Nous allons aussi créer une colonne intermédiaire pour créer un visuel synthétique de couleur permettant de voir les compteurs sur plusieurs lignes.

Code
import matplotlib.pyplot as plt

df1["nom_compteur_temp"] = df1["nom_compteur"]

# Discrete colormap
categories = df1["nom_compteur_temp"].unique()
cmap = plt.get_cmap("Dark2")

# Create mapping from label to color hex
colors = {cat: cmap(i / max(len(categories) - 1, 1)) for i, cat in enumerate(categories)}
colors = {k: plt.matplotlib.colors.to_hex(v) for k, v in colors.items()}

# Function to return colored cell
def create_color_cell(label: str) -> str:
    color = colors.get(label, "#ccc")
    return f"""
    <div style="
        width: 20px;
        height: 20px;
        background-color: {color};
        border-radius: 3px;
        margin: auto;
    "></div>
    """

Nous allons créer un dictionnaire pour renommer nos colonnes sous une forme plus intelligible que le nom des variables ainsi qu’une variable pour le référencement de la source:

columns_mapping = {
    "nom_compteur": "Localisation",
    "direction": "",
    "text": "",
    "sum_counts": "",
    "nom_compteur_temp": ""
}
source_note = "**Source**: Compteurs vélib sur la page de l'[open data de la ville de Paris](https://opendata.paris.fr/explore/dataset/comptage-velo-donnees-compteurs/dataviz/?disjunctive.id_compteur&disjunctive.nom_compteur&disjunctive.id&disjunctive.name)"

Le fonctionnement de great_tables est assez similaire à celui de plotnine : on construit la table par ajout successif de couches, en combinant les fonctions (avec l’opérateur . cette fois, et non avec +à).

Chaque couche permet d’ajuster un aspect particulier de la table (format des cellules, titres, colonnes, styles, etc.), selon une logique déclarative et cohérente, fidèle à la philosophie de la grammaire des graphiques.

The way great_tables works is quite similar to plotnine: the table is constructed by successively adding layers, combining functions (using the . operator this time, rather than +).

Each layer allows you to adjust a particular aspect of the table (cell format, titles, columns, styles, etc.), according to a declarative and consistent logic, faithful to the philosophy of graphic grammar.

Code
from great_tables import *

df_table = df1.loc[:, ["nom_compteur_temp","nom_compteur", "direction", "text", "sum_counts"]]

gt = (
    GT(
        df_table
    )
    .fmt(fns=create_color_cell, columns="nom_compteur_temp")
    .fmt_nanoplot(columns="sum_counts")
    .tab_spanner(
        label="Compteur",
        columns=["nom_compteur_temp", "nom_compteur", "direction"]
    )
    .tab_spanner(
        label="Moyenne horaire",
        columns=["text", "sum_counts"]
    )
    .cols_label(
        **columns_mapping
    )
    .cols_width(
        cases={
            "nom_compteur_temp": "10%",
            "nom_compteur": "40%",
            "direction": "10%",
            "text": "10%"
        }
    )
    .tab_style(
        style=[
            style.text(size = "small")
        ],
        locations = loc.body(df_table.columns.tolist())
    )
    .tab_style(
        style=[
            style.text(weight="bold")
        ],
        locations=loc.body(columns="text")
    )
    .tab_source_note(
        md(source_note)
    )
)

Pour rendre mieux sur un fond noir, on peut ajouter quelques paramètres spécifiques à ce besoin.

gt_dark = gt.tab_options(
    table_background_color="black",
    heading_background_color="black"
)
gt_dark
Table 5.1: Visualisation alternative sous forme de table
Compteur Moyenne horaire
Localisation
73 boulevard de Sébastopol S-N 327
327
73 boulevard de Sébastopol N-S 233
233
64 Rue de Rivoli O-E 230
230
102 boulevard de Magenta SE-NO 219
219
89 boulevard de Magenta NO-SE 217
217
64 Rue de Rivoli E-O 191
191
35 boulevard de Ménilmontant NO-SE 180
180
Quai d'Orsay E-O 179
179
27 quai de la Tournelle SE-NO 167
167
72 boulevard Voltaire NO-SE 160
160
Source: Vélib counters on the Paris open data page

6 Des graphiques réactifs grâce aux interfaces avec Javascript

Important

On appelle tooltip le texte qui s’affiche en surbrillance lorsqu’on passe la souris sur un élément de la figure (ordinateur) ou lorsqu’on clique sur la figure (smartphone). Il s’agit donc d’un niveau d’information supplémentaire permis par l’interactivité, qui peut être pratique pour alléger le message principal d’une figure.

Néanmoins, comme toute information sur une figure, du travail pour rendre celle-ci utile est nécessaire. On peut rarement se contenter du tooltip par défaut d’une librairie: il faut réfléchir au message que celui-ci doit passer, complément textuel à l’information visuelle représentée sur la figure.

Encore une fois, nous n’allons pas creuser ce sujet - c’est l’objet, en soi, d’un cours de data visualisation - mais avoir en tête cet enjeu est important pour la réception d’une visualisation.

Par ailleurs, un autre élément que nous mettons totalement de côté ici mais qui est important lorsqu’on rentre dans le monde de la visualisation interactive est celui de la responsiveness, c’est-à-dire de la capacité d’une visualisation, et plus généralement d’un site web, à être lisible et fonctionnel sur des tailles d’écran différentes. Gérer des tailles d’écran différentes est compliqué mais est indispensable si on vise un public large dont une partie croissante utilise des smartphones pour consulter un site web.

A ceci s’ajoute des questions d’adaptabilité du site à différentes formes de handicaps visuels. Par exemple, ce serait environ 8% des hommes qui seraient atteints d’une forme de daltonisme, avec des formes différentes (6% ont des difficultés à percevoir les nuances de vert, 2% de rouge).

Bref, vous qui entrez dans le monde de la visualisation, laissez toute espérance de simplicité. Les outils sont simples d’usage mais les besoins sont complexes.

6.1 L’écosystème disponible depuis Python

Les figures construites avec matplotlib ou plotnine sont figées et présentent ainsi l’inconvénient de ne pas permettre d’interaction avec le lecteur. Toute l’information doit donc être contenue dans la figure, ce qui peut la rendre difficile à lire. Si la figure est bien faite, avec différents niveaux d’information, cela peut bien fonctionner.

Il est néanmoins plus simple, grâce aux technologies web, de proposer des visualisations à plusieurs niveaux. Un premier niveau d’information, celui du coup d’œil, peut suffire à assimiler les principaux messages de la visualisation. Ensuite, un comportement plus volontaire de recherche d’information secondaire peut permettre d’en savoir plus. Les visualisations réactives, qui sont maintenant la norme dans le monde de la dataviz, permettent ce type d’approche : le lecteur d’une visualisation peut passer sa souris à la recherche d’informations complémentaires (par exemple, les valeurs exactes) ou cliquer pour faire apparaître des informations complémentaires sur la visualisation ou autour.

Ces visualisations reposent sur le même triptyque que l’ensemble de l’écosystème web : HTML, CSS et JavaScript. Les utilisateurs de Python ne vont jamais manipuler directement ces langages, qui demandent une certaine expertise, mais vont utiliser des librairies au niveau de R qui génèreront automatiquement tout le code HTML, CSS et JavaScript permettant de créer la figure.

Il existe plusieurs écosystèmes Javascript mis à disposition des développeurs.euses par le biais de Python. Les deux principales librairies sont Plotly, associée à l’écosystème Javascript du même nom, et Altair, associée à l’écosystème Vega et Altair en Javascript3. Pour permettre aux pythonistes de découvrir la librairie Javascript émergente Observable Plot, l’ingénieur de recherche français Julien Barnier a développé pyobsplot une librairie Python permettant d’utiliser cet écosystème depuis Python.

L’interactivité ne doit pas juste être un gadget n’apportant pas de lisibilité supplémentaire, voire la détériorant. Il est rare de pouvoir se contenter de la figure produite sans avoir à fournir un travail supplémentaire pour la rendre efficace.

6.2 La librairie Plotly

Le package Plotly est une surcouche à la librairie Javascript Plotly.js qui permet de créer et manipuler des objets graphiques de manière très flexible afin de produire des objets réactifs sans avoir à recourir à Javascript.

Le point d’entrée recommandé est le module plotly.express (documentation ici) qui offre une approche intuitive pour construire des graphiques pouvant être modifiés a posteriori si besoin (par exemple pour customiser les axes).

Visualiser les figures produites par Plotly

Dans un notebook Jupyter classique, les lignes suivantes de code permettent d’afficher le résultat d’une commande Plotly sous un bloc de code :

Pour JupyterLab, l’extension jupyterlab-plotly s’avère nécessaire:

!jupyter labextension install jupyterlab-plotly

6.3 Réplication de l’exemple précédent avec Plotly

Les modules suivants seront nécessaires pour construire des graphiques avec plotly:

import plotly
import plotly.express as px
Exercice 7: un barplot avec Plotly

L’objectif est de reconstruire le premier diagramme en barre rouge avec Plotly.

  1. Réalisez le graphique en utilisant la fonction adéquate avec plotly.express et…

    • Ne pas prendre le thème par défaut mais un à fond blanc, pour avoir un résultat ressemblant à celui proposé sur le site de l’open-data.
    • Pour la couleur rouge, vous pouvez utiliser l’argument color_discrete_sequence.
    • Ne pas oublier de nommer les axes.
  2. Modifier le texte apparaissant en surbrillance (hover text).

  3. Choisir un fond blanc ou noir pour avoir une couleur de fond uniforme.

(a)
(b)
Figure 6.1

6.4 La librairie altair

Pour cet exemple, nous allons reconstruire notre figure précédente.

Comme ggplot/plotnine, Vega est un écosystème graphique visant à proposer une implémentation de la grammaire des graphiques de Wilkinson (2011). La syntaxe de Vega est donc basée sur un principe déclaratif : on déclare une construction par couches et transformations de données progressives.

À l’origine, Vega est basée sur une syntaxe JSON, d’où son lien fort avec Javascript. Néanmoins, il existe une API Python qui permet de faire ce type de figures interactives nativement en Python. Pour comprendre la logique de construction d’un code altair, voici comment répliquer la figure précédente avec :

Voir l’architecture d’une figure altair
import altair as alt

fig_altair = (
    alt.Chart(df1.reset_index())
    .mark_bar(color='steelblue') 
    .encode(
        x=alt.X('sum_counts:Q', title=xaxis),
        y=alt.Y('nom_compteur:N', sort='-x', title=yaxis),
        tooltip=[
            alt.Tooltip('nom_compteur:N', title=xaxis),
            alt.Tooltip('sum_counts:Q', title=yaxis)
        ]
    ).properties(
        title=title
    ).configure_view(
        strokeOpacity=0
    )
)

fig_altair.interactive()

Références

Galiana, Lino, Olivier Meslin, Noémie Courtejoie, et Simon Delage. 2022. « Caractéristiques socio-économiques des individus aux formes sévères de Covid-19 au fil des vagues épidémiques ».
Wilkinson, Leland. 2011. « The grammar of graphics ». In Handbook of computational statistics: Concepts and methods, 375‑414. Springer.

Informations additionnelles

Ce site a été construit automatiquement par le biais d’une action Github utilisant le logiciel de publication reproductible Quarto (version 1.7.33).

L’environnement utilisé pour obtenir les résultats est reproductible par le biais d’uv. Le fichier pyproject.toml utilisé pour construire cet environnement est disponible sur le dépôt linogaliana/python-datascientist

pyproject.toml
[project]
name = "python-datascientist"
version = "0.1.0"
description = "Source code for Lino Galiana's Python for data science course"
readme = "README.md"
requires-python = ">=3.12,<3.13"
dependencies = [
    "altair==5.4.1",
    "black==24.8.0",
    "cartiflette",
    "contextily==1.6.2",
    "duckdb>=0.10.1",
    "folium>=0.19.6",
    "geoplot==0.5.1",
    "graphviz==0.20.3",
    "great-tables==0.12.0",
    "ipykernel>=6.29.5",
    "jupyter>=1.1.1",
    "jupyter-cache==1.0.0",
    "kaleido==0.2.1",
    "langchain-community==0.3.9",
    "loguru==0.7.3",
    "markdown>=3.8",
    "nbclient==0.10.0",
    "nbformat==5.10.4",
    "nltk>=3.9.1",
    "pip>=25.1.1",
    "plotly>=6.1.2",
    "plotnine>=0.15",
    "polars==1.8.2",
    "pyarrow==17.0.0",
    "pynsee==0.1.8",
    "python-dotenv==1.0.1",
    "pywaffle==1.1.1",
    "requests>=2.32.3",
    "scikit-image==0.24.0",
    "scipy==1.13.0",
    "spacy==3.8.4",
    "webdriver-manager==4.0.2",
    "wordcloud==1.9.3",
    "xlrd==2.0.1",
    "yellowbrick==1.5",
]

[tool.uv.sources]
cartiflette = { git = "https://github.com/inseefrlab/cartiflette" }

[dependency-groups]
dev = [
    "nb-clean>=4.0.1",
]

Pour utiliser exactement le même environnement (version de Python et packages), se reporter à la documentation d’uv.

SHA Date Author Description
8dafcf38 2025-08-24 22:04:46 Lino Galiana Deuxième vague de correctifs (#646)
9e03c9b2 2025-08-23 18:57:11 Lino Galiana Premiers correctifs du chapitre de visualisation (#645)
131ccd4c 2025-08-22 19:42:29 Lino Galiana Chapitre de visualisation: plus de place pour la grammaire des graphiques (#643)
73043ee7 2025-08-20 14:50:30 Lino Galiana retire l’historique inutile des données velib (#638)
ff22c636 2025-08-12 09:33:59 lgaliana Improving data visualisation chapter
2326ae94 2025-08-11 15:00:21 lgaliana eval: true dans le chapitre des graphiques pour résoudre #626
7006f605 2025-07-28 14:20:47 Lino Galiana Une première PR qui gère plein de bugs détectés par Nicolas (#630)
99ab48b0 2025-07-25 18:50:15 Lino Galiana Utilisation des callout classiques pour les box notes and co (#629)
94648290 2025-07-22 18:57:48 Lino Galiana Fix boxes now that it is better supported by jupyter (#628)
91431fa2 2025-06-09 17:08:00 Lino Galiana Improve homepage hero banner (#612)
2f96f636 2025-01-29 19:49:36 Lino Galiana Tweak callout for colab engine (#591)
1b184cba 2025-01-24 18:07:32 lgaliana Traduction 🇬🇧 du chapitre 1 de la partie dataviz
e66fee04 2024-12-23 15:12:18 Lino Galiana Fix errors in generated notebooks (#583)
cbe6459f 2024-11-12 07:24:15 lgaliana Revoir quelques abstracts
9cf2bde5 2024-10-18 15:49:47 lgaliana Reconstruction complète du chapitre de cartographie
c9a3f963 2024-09-24 15:18:59 Lino Galiana Finir la reprise du chapitre matplotlib (#555)
46f038a4 2024-09-23 15:28:36 Lino Galiana Mise à jour du premier chapitre sur les figures (#553)
59f5803d 2024-09-22 16:41:46 Lino Galiana Update bike count source data for visualisation tutorial (#552)
06d003a1 2024-04-23 10:09:22 Lino Galiana Continue la restructuration des sous-parties (#492)
005d89b8 2023-12-20 17:23:04 Lino Galiana Finalise l’affichage des statistiques Git (#478)
3fba6124 2023-12-17 18:16:42 Lino Galiana Remove some badges from python (#476)
cf91965e 2023-12-02 13:15:18 linogaliana href in dataviz chapter
1f23de28 2023-12-01 17:25:36 Lino Galiana Stockage des images sur S3 (#466)
09654c71 2023-11-14 15:16:44 Antoine Palazzolo Suggestions Git & Visualisation (#449)
889a71ba 2023-11-10 11:40:51 Antoine Palazzolo Modification TP 3 (#443)
df01f019 2023-10-10 15:55:04 Lino Galiana Menus automatisés (#432)
a7711832 2023-10-09 11:27:45 Antoine Palazzolo Relecture TD2 par Antoine (#418)
154f09e4 2023-09-26 14:59:11 Antoine Palazzolo Des typos corrigées par Antoine (#411)
057dae1b 2023-09-20 16:28:46 Lino Galiana Chapitre visualisation (#406)
1d0780ca 2023-09-18 14:49:59 Lino Galiana Problème rendu chapitre matplotlib (#405)
a8f90c2f 2023-08-28 09:26:12 Lino Galiana Update featured paths (#396)
3bdf3b06 2023-08-25 11:23:02 Lino Galiana Simplification de la structure 🤓 (#393)
78ea2cbd 2023-07-20 20:27:31 Lino Galiana Change titles levels (#381)
8df7cb22 2023-07-20 17:16:03 linogaliana Change link
f0c583c0 2023-07-07 14:12:22 Lino Galiana Images viz (#371)
f21a24d3 2023-07-02 10:58:15 Lino Galiana Pipeline Quarto & Pages 🚀 (#365)
f2e89224 2023-06-12 14:54:20 Lino Galiana Remove spoiler shortcode (#364)
2dc82e7b 2022-10-18 22:46:47 Lino Galiana Relec Kim (visualisation + API) (#302)
03babc6c 2022-10-03 16:53:47 Lino Galiana Parler des règles de la dataviz (#291)
89c10c32 2022-08-25 08:30:22 Lino Galiana Adaptation du shortcode spoiler en notebook (#257)
494a85ae 2022-08-05 14:49:56 Lino Galiana Images featured ✨ (#252)
d201e3cd 2022-08-03 15:50:34 Lino Galiana Pimp la homepage ✨ (#249)
2812ef40 2022-07-07 15:58:58 Lino Galiana Petite viz sympa des prenoms (#242)
a4e24263 2022-06-16 19:34:18 Lino Galiana Improve style (#238)
02ed1e25 2022-06-09 19:06:05 Lino Galiana Règle problème plotly (#235)
299cff3d 2022-06-08 13:19:03 Lino Galiana Problème code JS suite (#233)
5698e303 2022-06-03 18:28:37 Lino Galiana Finalise widget (#232)
7b9f27be 2022-06-03 17:05:15 Lino Galiana Essaie régler les problèmes widgets JS (#231)
12965bac 2022-05-25 15:53:27 Lino Galiana :launch: Bascule vers quarto (#226)
9c71d6e7 2022-03-08 10:34:26 Lino Galiana Plus d’éléments sur S3 (#218)
4f675284 2021-12-12 08:37:21 Lino Galiana Improve website appareance (#194)
66a52761 2021-11-23 16:13:20 Lino Galiana Relecture partie visualisation (#181)
2a8809fb 2021-10-27 12:05:34 Lino Galiana Simplification des hooks pour gagner en flexibilité et clarté (#166)
2f4d3905 2021-09-02 15:12:29 Lino Galiana Utilise un shortcode github (#131)
2e4d5862 2021-09-02 12:03:39 Lino Galiana Simplify badges generation (#130)
80877d20 2021-06-28 11:34:24 Lino Galiana Ajout d’un exercice de NLP à partir openfood database (#98)
6729a724 2021-06-22 18:07:05 Lino Galiana Mise à jour badge onyxia (#115)
4cdb759c 2021-05-12 10:37:23 Lino Galiana :sparkles: :star2: Nouveau thème hugo :snake: :fire: (#105)
7f9f97bc 2021-04-30 21:44:04 Lino Galiana 🐳 + 🐍 New workflow (docker 🐳) and new dataset for modelization (2020 🇺🇸 elections) (#99)
0a0d0348 2021-03-26 20:16:22 Lino Galiana Ajout d’une section sur S3 (#97)
a5b7c990 2020-10-05 15:07:09 Lino Galiana Donne lien vers données compteurs
18be8f43 2020-10-01 17:08:53 Lino Galiana Intégration de box inspirées du thème pydata sphinx (#58)
5ac3cbee 2020-09-28 18:59:24 Lino Galiana Continue la partie graphiques (#54)
94f39ecc 2020-09-24 21:25:32 Lino Galiana quelques mots sur vizu
Retour au sommet

Notes de bas de page

  1. Ce chapitre sera construit autour de l’écosystème Quarto . En attendant ce chapitre, vous pouvez consulter la documentation exemplaire de cet écosystème et pratiquer, ce sera le meilleur moyen de découvrir.↩︎

  2. Heureusement, comme il existe un énorme corpus de code en ligne utilisant matplotlib, les assistants de code comme ChatGPT ou Github Copilot sont précieux pour construire un graphique à partir d’instructions.↩︎

  3. Le nom de ces librairies est inspiré de la constellation du triangle d’été dont Véga et Altair sont deux membres.↩︎

Citation

BibTeX
@book{galiana2023,
  author = {Galiana, Lino},
  title = {Python pour la data science},
  date = {2023},
  url = {https://pythonds.linogaliana.fr/},
  doi = {10.5281/zenodo.8229676},
  langid = {fr}
}
Veuillez citer ce travail comme suit :
Galiana, Lino. 2023. Python pour la data science. https://doi.org/10.5281/zenodo.8229676.