2025.42 changes

This commit is contained in:
2025-10-24 22:29:15 +02:00
parent d0bcfcf8f1
commit e4579e4887
24 changed files with 699 additions and 352 deletions

View File

@@ -41,13 +41,21 @@ toute la variété de graphiques générables avec Plotly, via des fonctions pr
### Installer Plotly Express
```bash= {.numberLines}
```bash {.numberLines}
pip install plotly
```
Plotly contient un package `express` proposant des fonctions simples de rendu.
----
### Barres
### Graphiques Plotly Express
Nous allons passer en revue quelques graphiques réalisables avec Express.
----
#### `plotly.express.bar`
Générer un graphique en barres est assez simple avec Plotly, et fonctionne
de manière relativement satisfaisante par défaut (ex. texte en blanc sur fond sombre)
@@ -56,15 +64,14 @@ de manière relativement satisfaisante par défaut (ex. texte en blanc sur fond
import pandas as pd
from plotly import express
data = pd.DataFrame(data={
"product": ["pomme", "poire", "banane", "pêche"],
"price": [1.99, 2.49, 2.99, 3.49],
"wpu": [200, 180, 140, 200]
})
# Générer un graphique
# Générer un graphique avec deux colonnes par valeur de "product"
figure = express.bar(data, x="product", y=["price", "wpu"], barmode="group", title="Prix et poids unitaires")
figure.show()
figure.show(renderer="browser") # Le renderer peut être obligatoire, ex. avec Spyder
```
[Documentation de `bar`](https://plotly.com/python-api-reference/generated/plotly.express.bar.html)
@@ -75,17 +82,16 @@ figure.show()
----
### Barres (sur un objet `Series`)
#### `plotly.express.bar` (`Series`)
Vous pouvez également générer un graphique en partant d'un objet de type `Series`, même si l'objet ne contient
Vous pouvez également générer un graphique en partant d'un objet de type `Series`{.python}, même si l'objet ne contient
en lui-même qu'une seule colonne. Les valeurs d'index peuvent également servir sur un axe; il suffit d'utiliser
la valeur `None`{.python} :
la valeur `None`{.python} pour indiquer que les valeurs d'index doivent être utilisées sur l'axe des X.
```python {.numberLines}
import pandas as pd
from plotly import express
values = pd.Series(
data=[1.99, 2.49, 2.99, 3.49],
index=["pomme", "poire", "banane", "peche"],
@@ -93,15 +99,17 @@ values = pd.Series(
)
# Générer un graphique
figure = express.bar(values, x=None, y="price")
figure.show()
figure.show(renderer="browser")
```
L'axe des Y pourra utiliser le nom associé à l'objet `Series` (`values.name`{.python})
----
#### Personnalisation
Quelques personnalisations sont possibles, notamment via la notion de `template` (thème),
et de `layout` (organisation du contenu).
que nous reverrons avec Plotly.
```python {.numberLines}
import pandas as pd
@@ -115,25 +123,8 @@ data = pd.DataFrame(data={
# Générer un graphique. Plotly express ne prend en charge qu'un seul graphique par figure
figure = express.bar(data, x="product", y="price", title="Prix", template="seaborn",
labels={"price": "Prix", "product": "Produit"})
figure.layout.update({"font": {"family": "Cabin", "size": 13}})
figure.show()
figure.show(renderer="browser")
```
----
Voici une liste des valeurs possibles fournies par Plotly pour l'argument `template`{.python}:
- `ggplot2` : Thème par défaut de GGPlot2 pour [R]{.naming}
- `seaborn` : Thème par défaut de Seaborn pour Python
- `plotly` : Thème par défaut de Plotly
- `plotly_white`
- `plotly_dark`
- `presentation`
- `xgridoff`
- `ygridoff`
- `gridon`
- `none`
----
![Rendu personnalisé en barres](assets/images/eda-plotly-express-bar-themed.png)
@@ -144,7 +135,7 @@ Voici une liste des valeurs possibles fournies par Plotly pour l'argument `templ
----
### Secteurs concentriques
#### `plotly.express.sunburst` (anneaux concentriques)
Le graphique [sunburst]{.naming} permet d'afficher des secteurs, et de les découper en sous-catégories
dans des anneaux concentriques. Ici, nous avons un `DataFrame`{.python} avec des ventes par ville,
@@ -175,7 +166,7 @@ if __name__ == '__main__':
----
### Nuages de points
#### `plotly.express.scatter` (nuages de points)
Les nuages de points ([scatter plot]{.naming}) sont un moyen pratique de mettre en regard deux valeurs
d'une même observation afin d'essayer de déduire une tendance ou une corrélation. Ils se présentent en
@@ -186,14 +177,14 @@ graphique.
import pandas as pd
from plotly import express as px
# Charger le dataset Iris
data = pd.read_csv("https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data", header=None,
names=["sepal-length", "sepal-width", "petal-length", "petal-width", "class"])
plot = px.scatter(data, x="sepal-length", y="sepal-width", size="petal-width", color="class", template="seaborn",
title="Iris flowers dataset",
labels={"sepal-length": "Sepal length", "sepal-width": "Sepal width", "petal-width": "Petal width", "class": "Class"})
plot.layout.update({"font": {"family": "Cabin", "size": 13}})
plot.show()
plot.show(renderer="browser")
```
[Documentation de `scatter`](https://plotly.com/python-api-reference/generated/plotly.express.scatter.html)
@@ -204,76 +195,341 @@ plot.show()
----
## Construction
## Plotly
Cette section regroupe plusieurs solutions pour parvenir à certains résultats.
Cette section permet de creuser plus loin en utilisant les
outils fondamentaux proposés par Plotly, eux aussi relativement simples
mais riches.
----
### Deux axes Y dans un graphique en barres
### Notions
Par défaut, Plotly Express ne génère pas deux axes distincts lorsque vous affichez un graphique
avec deux colonnes pour l'axe Y. Si leurs ordres de grandeur sont différents, les barres partageront toutefois
la même échelle.
Plotly propose nombre d'outils classés dans certains thèmes pour vous permettre de
personnaliser vos graphiques. Les thèmes que nous allons aborder sont les suivants :
Pour obtenir deux axes distincts en Y, nous devons utiliser Plotly manuellement. Rien d'anormalement
complexe toutefois :
| Thème | Description en français |
|:----------------------------------|-----------------------------------------------------|
| `templates` | Thèmes pour personnaliser les graphiques |
| `layout` et `figure` | Mises en page pour les graphiques |
| `axes` | Axes pour les graphiques |
| `texttemplates` | Textes pour les éléments de graphiques |
| `palettes de couleurs` | Sélections de couleurs pour les graphiques |
| `séquences et cartes de couleurs` | Séquences et cartes de couleurs pour les graphiques |
| `traces` | Tracés pour les graphiques |
| `sous-graphiques` | Sous-graphiques pour les rendus |
----
### Templates
Les [templates]{.naming} dans Plotly sont des objets contenant un ensemble de configurations
pour le rendu d'éléments de graphiques. Les options individuelles personnalisables
sont extrêmement nombreuses (des milliers), ainsi Plotly fournit des thèmes prêts
à l'emploi pour vous permettre de personnaliser l'apparence de vos graphiques.
Voici une liste des noms possibles fournis par Plotly pour appliquer un `template`{.python} à un rendu :
| Template | Description | Template | Description |
|----------------|--------------------------------------|----------------|---------------------------------------------------|
| `plotly` | Thème par défaut de **Plotly** | `ggplot2` | Thème par défaut de **GGPlot2** pour [R]{.naming} |
| `seaborn` | Thème par défaut de **Seaborn** | `plotly_white` | Thème blanc avec des grilles |
| `plotly_dark` | Thème sombre | `presentation` | Style optimisé pour les présentations |
| `xgridoff` | Désactive la grille en X | `ygridoff` | Désactive la grille en Y |
| `gridon` | Active les grilles sur les deux axes | `none` | Désactive tous les styles par défaut |
----
#### Associer un template
```python {.numberLines}
from plotly.express import bar
import pandas as pd
data = pd.DataFrame({
"product": ["stylo", "clavier", "souris", "écran"],
"price": [1.99, 15.99, 8.99, 129.99],
})
figure = bar(data, x="product", y="price", title="Prix", template="seaborn")
figure.show(renderer="browser")
```
Ce code génère un graphique en barres, en utilisant le template `seaborn`{.python}.
[Documentation sur les templates](https://plotly.com/python/templates/)
----
![Barres rendues avec le template seaborn](assets/images/eda-plotly-template-select-base.png)
----
#### Définir un template
Vous pouvez définir vos propres templates dans Plotly en définissant des objets héritant
de la classe `plotly.graph_objs.layout.Template`{.python}.
Ces objets sont créés avec un dictionnaire contenant les configurations des éléments de graphiques
que vous souhaitez personnaliser.
```python {.numberLines}
from plotly.graph_objs.layout import Template
my_template = Template(layout={
"hoverlabel": {"bgcolor": "#333333", "bordercolor": "black", "font": {
"family": "Roboto", "size": 12, "color": "white", "weight": "bold"
}},
"font": {"family": "Roboto", "size": 15},
"margin": {"pad": 20},
"title": {"font": {"size": 36, "weight": "bold"}}
})
```
----
#### Enregistrer le template
Pour enregistrer votre template afin de le désigner simplement par un nom, vous devez l'ajouter au
dictionnaire des templates disponibles dans Plotly.
```python {.numberLines}
from plotly.graph_objs.layout import Template
from plotly.io import templates
my_template = Template(layout={
"hoverlabel": {
"bgcolor": "#333333",
"bordercolor": "black",
"font": {"family": "Roboto", "size": 12, "color": "white", "weight": "bold"}
},
"font": {"family": "Roboto", "size": 15},
"margin": {"pad": 20},
"title": {"font": {"size": 36, "weight": "bold"}}
})
# Enregistrer le template
templates["my_template"] = my_template
```
Vous pourrez ensuite le sélectionner en précisant son nom seul, ou via une addition `template="other_template+my_template"`{.python} afin de superposer
les configurations des deux templates.
----
### Layout et Figure
Les objets de type `plotly.graph_objs.Layout`{.python} peuvent être renseignés sous forme de dictionnaires et comprennent
plusieurs options pour personnaliser le rendu des graphiques. Les options disponibles sont nombreuses et varient en fonction
du type de graphique que vous souhaitez afficher. Elles sont exactement les mêmes que celles des templates.
[Documentation sur les options de layout](https://plotly.com/python/reference/layout/)
----
#### Appliquer un layout (Express)
```python {.numberLines}
import pandas as pd
import plotly
from plotly.graph_objs import Figure
from plotly.express import sunburst
df = pd.DataFrame(data={
"country": ["France", "France", "Spain", "Spain", "England", "England", "England"],
"city": ["Montpellier", "Bordeaux", "Madrid", "Valencia", "London", "Manchester", "Bristol"],
"sales": [150_000, 127_000, 97_200, 137_250, 200_000, 180_000, 150_000]
})
plot: Figure = sunburst(df, path=["country", "city"], values="sales", title="Sales by country and city", template="ggplot2")
plot.layout.update({"font": {"family": "Cabin", "size": 13}, "showlegend": True})
plot.show(renderer="browser")
```
Les objets de type `plotly.graph_objs.Figure`{.python} possèdent un attribut `layout`{.python} de type `plotly.graph_objs.Layout`{.python},
qui fonctionne comme un dictionnaire et permet de personnaliser le rendu des graphiques. Ici, on a personnalisé la police et l'affichage des légendes.
----
#### Appliquer un layout (Plotly)
```python {.numberLines}
import pandas as pd
from plotly.graph_objs import Figure, Bar
df = pd.DataFrame(data={"label": ["Citron", "Pomme", "Mangue"], "price": [1.99, 3.97, 6.8]})
figure = Figure(
data=[Bar(x=df["label"], y=df["price"])],
layout={
"font": {"family": "Cabin", "size": 20},
"title": {"text": "Prix au kilo"},
}
)
figure.show(renderer="browser")
```
Ici, on crée un objet `Figure`{.python} manuellement, en lui associant une ou plusieurs [traces]{.naming}.
L'initialisation accepte un argument `layout: dict`{.python} pour personnaliser le rendu des graphiques.
----
#### Exemples d'options de layout
| Option | Type / valeur possible | Description / notes |
| ------------------------- | -------------------------------------------------- | ------------------------------------------------------------------------ |
| `title.automargin` | booléen | Ajuste automatiquement les marges pour éviter le chevauchement du titre. |
| `title.font` | dict | Définit la police du texte du titre. |
| `title.font.color` | color | Couleur du texte du titre. |
| `title.font.family` | string | Famille de police (ex. `"Arial, sans-serif"`). |
| `title.font.lineposition` | flaglist / string | Décoration du texte : `"under"`, `"over"`, `"through"`, ou `"none"`. |
| `title.font.shadow` | string | Définit lombre du texte, `"auto"` ou une valeur personnalisée. |
| `title.font.size` | nombre | Taille de la police (>= 1). |
| `title.font.style` | `"normal"` / `"italic"` | Style du texte (normal ou italique). |
1/4
----
| Option | Type / valeur possible | Description / notes |
| ------------------------- | -------------------------------------------------- | ------------------------------------------------------------------------ |
| `title.font.textcase` | `"normal"` / `"word caps"` / `"upper"` / `"lower"` | Définit la casse du texte. |
| `title.font.variant` | `"normal"` / `"small-caps"` / `"all-small-caps"` | Variante typographique. |
| `title.font.weight` | nombre ou `"normal"` / `"bold"` | Épaisseur de la police (11000). |
| `title.pad` | dict | Définit les marges internes autour du titre. |
| `title.pad.b` | nombre | Marge interne inférieure (px). |
| `title.pad.l` | nombre | Marge interne gauche (px). |
| `title.pad.r` | nombre | Marge interne droite (px). |
| `title.pad.t` | nombre | Marge interne supérieure (px). |
2/4
----
| Option | Type / valeur possible | Description / notes |
| ------------------------- | -------------------------------------------------- | ------------------------------------------------------------------------ |
| `title.subtitle` | dict | Définit le sous-titre du graphique. |
| `title.subtitle.text` | string | Texte du sous-titre. |
| `title.subtitle.font` | dict | Police du sous-titre (mêmes propriétés que `title.font`). |
| `title.text` | string | Texte du titre principal. |
| `title.x` | nombre ∈ [0, 1] | Position horizontale du titre (relative à `title.xref`). |
| `title.xanchor` | `"auto"` / `"left"` / `"center"` / `"right"` | Alignement horizontal du texte selon `x`. |
| `title.xref` | `"container"` / `"paper"` | Référence horizontale du titre. |
| `title.y` | nombre ∈ [0, 1] ou `"auto"` | Position verticale du titre (relative à `title.yref`). |
3/4
----
| Option | Type / valeur possible | Description / notes |
| ------------------------- | -------------------------------------------------- | ------------------------------------------------------------------------ |
| `title.yanchor` | `"auto"` / `"top"` / `"middle"` / `"bottom"` | Alignement vertical du texte selon `y`. |
| `title.yref` | `"container"` / `"paper"` | Référence verticale du titre. |
4/4
Toutes les options sont disponibles dans la [documentation officielle](https://plotly.com/python/reference/layout/) de Plotly.
----
#### Layouts à axes multiples
Par défaut, Plotly Express ne génère pas deux axes Y distincts lorsque vous affichez un graphique
avec deux colonnes pour l'axe Y. Si leurs plages de valeurs sont différentes, les barres partageront toutefois
la même échelle.
Pour définir deux axes distincts en Y (ou 3), nous devons utiliser Plotly manuellement.
```python {.numberLines}
from plotly.graph_objs import Figure, Bar
import pandas as pd
data = pd.DataFrame(data={
"product": ["pomme", "poire", "banane", "pêche"],
"price": [1.99, 2.49, 2.99, 3.49], "wpu": [200, 180, 140, 200]
})
figure = Figure([
Bar(name="Prix", x=data["product"], y=data["price"], yaxis="y", offsetgroup=1),
Bar(name="Poids", x=data["product"], y=data["wpu"], yaxis="y2", offsetgroup=2),
])
# Afficher le dernier graphique généré
figure.layout.update({
"template": "seaborn",
"title": "Prix et poids unitaires", "font": {"family": "Cabin", "size": 13},
"yaxis": {"title": "Prix (€)"}, "yaxis2": {"title": "Poids (g)", "overlaying": "y", "side": "right"}
})
figure.show()
figure = Figure(
data=[
Bar(name="Prix", x=data["product"], y=data["price"], yaxis="y1", offsetgroup=1),
Bar(name="Poids", x=data["product"], y=data["wpu"], yaxis="y2", offsetgroup=2),
],
layout={
"template": "seaborn",
"title": "Prix et poids unitaires",
"font": {"family": "Cabin", "size": 13},
"yaxis1": {"title": "Prix (€)", "color": "red", "side": "left"},
"yaxis2": {"title": "Poids (g)", "overlaying": "y1", "side": "right"}
}
)
figure.show(renderer="browser")
```
----
#### Nomenclature des axes
Plotly gère dynamiquement la définition de plusieurs axes dans un graphique. Lorsque vous définissez
par exemple un axe Y pour un tracé orthogonal 2D, vous le définissez via un argument nommé `yaxis`{.python}.
La valeur de cet attribut est généralement un nom tel que `y`, `y1`, `y2`, etc.
Notez que le nom `y`{.python} est un raccourci pour `y0`{.python}.
Les informations de mise en page pour cet axe sont ensuite définies dans le layout avec un nom similaire,
par exemple `yaxis1`{.python} pour `y1`{.python}.
----
![Rendu avec deux axes Y](assets/images/eda-plotly-compose-2axes.png)
----
### Texte personnalisé dans un camembert
### Texte personnalisé dans les éléments
Par défaut, les secteurs de graphique camembert ([pie]{.naming}) affichent la taille
d'un secteur en pourcents du total. Vous pouvez y formater d'autres infos :
d'un secteur en pourcents du total. De façon moins évidente, le texte des barres est
masqué par défaut. L'affichage ou non d'une étiquette, et la personnalisation du texte
des étiquettes est possible, via un ensemble d'arguments définis dans le tableau suivant.
| Option | Type / valeur possible | Description / notes |
| ------------------------- |-------------------------------------------------| ------------------------------------------------------------------------ |
| `textinfo` | `"label"` / `"value"` / `"percent"` ou addition | Définit le texte à afficher dans chaque secteur. |
| `texttemplate` | string | Format du texte à afficher dans chaque secteur. |
| `textposition` | `"inside"` / `"outside"` / `"auto"` / `"middle"` | Position du texte par rapport au secteur. |
----
#### Texte de secteurs de camembert
```python {.numberLines}
import pandas as pd
from plotly import express as px
from plotly.graph_objs import Figure, Pie
from plotly.colors import qualitative
data = pd.DataFrame(data={
"product": ["pomme", "poire", "banane", "pêche"],
"price": [1.99, 2.49, 2.99, 3.49], "wpu": [200, 180, 140, 200]
"product": ["oignon", "carotte", "pomme", "poire", "radis", "tomate"],
"category": ["légume", "légume", "fruit", "fruit", "légume", "fruit"],
"origin": ["Espagne", "France", "France", "France", "France", "Espagne"],
"price": [1.69, 2.49, 2.99, 1.79, 1.29, 2.99]
})
figure = px.pie(data, values="price", names="product", title="Prix")
# Afficher le dernier graphique généré
figure.layout.update({
"template": "seaborn",
"title": "Prix au kilo", "font": {"family": "Cabin", "size": 13},
})
figure.update_traces(**{"textinfo": "label+value", "texttemplate": "%{label}<br>%{value:.2f}€"})
figure.show()
figure = Figure(
data=[Pie(
values=data["price"],
labels=data["product"].str.title(),
customdata=data[["category", "origin"]], # 2 colonnes
title="Prix",
textinfo="label+value",
texttemplate="<b>%{label}</b><br>%{value:.2f}€<br><em>%{customdata[0]}</em><br>%{customdata[1]}",
insidetextorientation="tangential")
],
layout={
"template": "seaborn",
"title": "Prix au kilo", "font": {"family": "Cabin", "size": 13},
"piecolorway": qualitative.Prism
})
figure.show(renderer="browser")
```
[Personnalisation des camemberts](https://plotly.com/python/pie-charts/)
----
![Rendu avec deux lignes de texte par secteur](assets/images/eda-plotly-compose-pie-label.png)
![Personnalisation du texte des secteurs](assets/images/eda-plotly-texttemplate-pie.png)
----
@@ -332,3 +588,42 @@ colorier les barres.
----
![Rendu avec gradient de couleurs](assets/images/eda-plotly-express-bar-gradient.png)
----
### Tracer une ligne ou une courbe de régression
Pour analyser la tendance générale de la disposition des points d'un jeu de données, il est
souvent intéressant de tracer des courbes (polynômes) ou des lignes.
Numpy propose une fonction de régression pour des points 2D, capable de générer les coefficients
d'une fonction polynômiale du `n`ième degré : `np.polyfit()`{.python}
----
```python {.numberLines}
import pandas as pd
import numpy as np
from plotly.graph_objs import Figure, Scatter
from scipy.ndimage import gaussian_filter
# Créer une courbe aléatoire lissée
df = pd.DataFrame(data={"col1": gaussian_filter(np.random.random(size=200) * 79.0 + 1.0, sigma=1.5)})
# Calculer les coefficients d'une régression de degré 2 pour col1
c2, c1, c0 = np.polyfit(df.index, df["col1"], 2)
df["reg1"] = df.index ** 2 * c2 + df.index * c1 + c0
# Créer une image avec deux lignes
figure = Figure([
Scatter(name="values", x=df.index, y=df["col1"], line={"color": "black", "width": 2}, marker={"symbol": "circle"}, mode="lines+markers"),
Scatter(name="regression", x=df.index, y=df["reg1"], line={"color": "red", "width": 4, "dash": "dot"}, mode="lines")
])
figure.layout.update({"template": "seaborn", "xaxis1": {"title": "time"}, "title": "Test de régression", "font": {"family": "Cabin", "size": 13}, "yaxis1": {"title": "value"}})
figure.show(renderer="browser")
```
Calcul et tracé d'une courbe sur un graphique avec `scipy` et `Plotly`
----
![Rendu courbe de régression polynômiale du second degré](assets/images/eda-plotly-regression-curve.png)