2025.46 changes

This commit is contained in:
2025-11-15 18:20:01 +01:00
parent c45c6e9f2d
commit cb516edca4
8 changed files with 132 additions and 32 deletions

View File

@@ -438,10 +438,11 @@ print(selection1, selection2, sep="\n")
### Bon à savoir : vues et copies
Lorsque vous extrayez une partie d'un `DataFrame`{.python}, vous obtenez généralement un objet de type `DataFrame`{.python}.
Cependant, il faut savoir que par défaut, le nouveau `DataFrame`{.python} est une **vue** sur le `DataFrame`{.python} initial.
Cela veut dire que si vous modifiez le contenu du `DataFrame`{.python} initial, le contenu du `DataFrame`{.python} extrait sera aussi modifié, et vice versa.
Cependant, il faut savoir que dans Pandas 2.0+, le nouveau `DataFrame`{.python} est une **copie** des données du `DataFrame`{.python} initial.
Cela veut dire que modifier le contenu d'un des deux `DataFrame`{.python} n'a pas d'effet sur l'autre.
Pour obtenir une copie des données du `DataFrame`{.python} extrait, il faut utiliser la méthode `.copy()`{.python}.
Pour obtenir une vue des données du `DataFrame`{.python} extrait et appliquer des modifications partagées, il faut
extraire en utilisant par exemple `.loc[(lignes, colonnes)]` (avec un `tuple`{.python}).
```python {.numberLines}
import pandas as pd
@@ -552,8 +553,13 @@ is_high = data > 3 # F, F, F, T, T, T
high_data = data.loc[is_high & is_even] # 4, 6
```
On peut utiliser les opérateurs binaires `&`{.python} (`and`) et `|`{.python} (`or`) pour combiner les séries de booléens.
Le résultat est une série de booléens de la même longueur.
Pour des questions d'ordre d'évaluation des opérateurs, vous devrez quasi systématiquement utiliser des parenthèses pour discriminer vos conditions.
Les opérateurs à utiliser pour assembler vos propositions sont les opérateurs logiques binaires:
- `&`{.python}: ET binaire
- `|`{.python}: OU binaire
- `~`{.python}: NON binaire
- `^`{.python}: OU exclusif binaire
----
@@ -712,6 +718,24 @@ L'attribut est documenté ici :
[37]: https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.Series.dt.day_name.html
[38]: https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.Series.dt.isocalendar.html
----
#### Exemple d'extraction de la semaine ISO
Dans le standard ISO, le numéro de semaine est déterminé comme suit :
La première semaine de l'année est celle dont la majorité de jours (au moins 4) est contenue
dans l'année en cours.
```python {.numberLines}
import pandas as pd
data = pd.DataFrame(data={
"order": ["A15", "B84", "D25", "B43"],
"date": pd.to_datetime(["2024-06-10", "2021-02-14", "2023-06-25", "2024-11-30"])
})
data["week"] = data["date"].dt.isocalendar()["week"]
```
----
@@ -870,6 +894,14 @@ Dans tous les cas sauf pour `df.at`{.python}, la clé peut être un des objets s
- liste de clés de lignes/colonne ou numéro de ligne (`[a, b, c]`{.python});
- tuple à deux éléments (`(ligne(s), colonne(s))`{.python}).
**Attention** : Passer un tuple contenant un slice ne fonctionne pas syntaxiquement parlant
s'il est exprimé explicitement, ie. avec des parenthèses.
```python {.numberLines}
df.loc[(0, 0:2)] # Ceci est une erreur de syntaxe
df.loc[0, 0:2] # Ceci est du Python valide
```
----
## Valeurs vides
@@ -1020,6 +1052,9 @@ df3 = df.fillna({"siret": "N/A", "siege": "Inconnu"})
df4 = df.ffill() # forward fill
# Remplir les colonnes avec la prochaine valeur non vide trouvée
df5 = df.bfill() # backward fill
# Remplir les valeurs NaN en interpolant
# https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.interpolate.html
df6 = df.interpolate(method="cubic")
```
----
@@ -1157,6 +1192,8 @@ df = pd.DataFrame(data={
df.drop(labels=["prenom"], axis="columns", inplace=True)
# Retirer les lignes aux index 0 et 2, mais retourner un nouveau DataFrame
df2 = df.drop(labels=[0, 2], axis="index")
# Variante : Retirer les lignes aux index 0 et 2
df3 = df.drop(index=[0, 2])
```
----
@@ -1200,7 +1237,7 @@ df = pd.DataFrame(data={
"numero": [8, 31, 23, 14]
})
# Appliquer une fonction sur chaque colonne
# Appliquer une fonction sur chaque ligne
df2 = df.apply(lambda s: s * 2, axis="columns")
```
@@ -1434,7 +1471,7 @@ et organiser les éléments dans chaque groupe.
----
## Astuces inoubliables
## Extra :En vrac
À l'usage, de nombreux cas empiriques déborderont des bases vues dans ce chapitre. Par exemple, certains
comportements sont difficiles à comprendre, ou certains usages nécessitent quelques ajustements pour la performance.
@@ -1446,7 +1483,14 @@ comportements sont difficiles à comprendre, ou certains usages nécessitent que
### Warning : modifications sur une copie de slice
Lorsqu'un utilisateur utilise Pandas dans le cadre d'une recherche ou d'un traitement, il est fréquent de
vouloir filtrer les lignes d'un `DataFrame`, puis d'appliquer des modifications au contenu de ce dernier.
Lorsqu'un utilisateur utilise Pandas dans l'objectif de modifier un `DataFrame`{.python}
extrait d'un `DataFrame`{.python} plus large (après filtrage de lignes par exemple), il
sera généralement exposé à un message d'avertissement tel que le suivant :
```text {.numberLines}
SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_index,col_indexer] = value instead
```
Le message peut être désactivé via les paramètres de Pandas, ou encore en récupérant explicitement
un `DataFrame`{.python} via la méthode `copy()`{.python}.

View File

@@ -18,7 +18,7 @@ propre à la publication.
## Comment fonctionne Plotly ?
Plotly est une bibliothèque Python compatible avec les `DataFrame`{.python} Pandas, qui permet de créer des graphiques
Plotly est une bibliothèque Python qui permet de créer des graphiques
interactifs, et cela grâce aux technologies web (HTML, CSS et JS). Un navigateur suffit à
afficher le contenu d'un graphique généré avec Plotly.
@@ -55,10 +55,10 @@ Nous allons passer en revue quelques graphiques réalisables avec Express.
----
#### `plotly.express.bar`
#### `plotly.express.bar` (`DataFrame`)
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)
Générer un graphique en barres est assez simple avec Plotly Express, et fonctionne
de manière relativement satisfaisante par défaut (ex. gestion du contraste et angle des étiquettes)
```python {.numberLines}
import pandas as pd
@@ -799,6 +799,10 @@ figure.update_layout(
figure.show(renderer="browser")
```
La méthode `add_hrect()`{.python} dessine un rectangle prenant l'intégralité de la largeur du
graphique. Il ne reste qu'à définir les coordonnées `y0` et `y1` du rectangle,
coordonnées qui dépendent d'une référence (taille du graphique, pixels ou axe Y)
----
![Ajout d'un rectangle horizontal](assets/images/eda-plotly-trace-hrect.png)
@@ -835,18 +839,29 @@ 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)})
# Créer une courbe aléatoire lissée de valeurs entre 1 et 80
SIZE: int = 500
np.random.seed(4) # Pour reproduire les résultats
df = pd.DataFrame(data={"col1": gaussian_filter(np.random.random(size=SIZE) * 79.0 + 1.0, sigma=SIZE / 133)})
# 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
df["reg1"] = c2 * df.index**2 + c1 * df.index + 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 = Figure(
data=[
Scatter(name="values", x=df.index, y=df["col1"], line={"color": "gray", "width": 1}, mode="lines"),
Scatter(
name="regression", x=df.index, y=df["reg1"], line={"color": "red", "width": 4, "dash": "dot"}, mode="lines"
),
],
layout={
"template": "seaborn",
"xaxis1": {"title": "time"},
"title": "Test de régression",
"font": {"family": "Cabin", "size": 13},
"yaxis1": {"title": "value"},
},
)
figure.show(renderer="browser")
```

Binary file not shown.

Before

Width:  |  Height:  |  Size: 250 KiB

After

Width:  |  Height:  |  Size: 189 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 89 KiB

After

Width:  |  Height:  |  Size: 88 KiB

View File

@@ -15,6 +15,11 @@ jupyter = "^1.0.0"
xlsxwriter = "^3.1.1"
plotly = "^5.15.0"
[tool.black]
line-length = 120
[tool.ruff]
line-length = 120
[build-system]
requires = ["poetry-core"]

View File

@@ -0,0 +1,30 @@
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 de valeurs entre 1 et 80
SIZE: int = 500
np.random.seed(4) # Pour reproduire les résultats
df = pd.DataFrame(data={"col1": gaussian_filter(np.random.random(size=SIZE) * 79.0 + 1.0, sigma=SIZE / 133)})
# Calculer les coefficients d'une régression de degré 2 pour col1
c2, c1, c0 = np.polyfit(df.index, df["col1"], 2)
df["reg1"] = c2 * df.index**2 + c1 * df.index + c0
# Créer une image avec deux lignes
figure = Figure(
data=[
Scatter(name="values", x=df.index, y=df["col1"], line={"color": "gray", "width": 1}, mode="lines"),
Scatter(
name="regression", x=df.index, y=df["reg1"], line={"color": "red", "width": 4, "dash": "dot"}, mode="lines"
),
],
layout={
"template": "seaborn",
"xaxis1": {"title": "time"},
"title": "Test de régression",
"font": {"family": "Cabin", "size": 13},
"yaxis1": {"title": "value"},
},
)
figure.show(renderer="browser")

View File

@@ -6,14 +6,12 @@ from plotly.graph_objs import Figure, Bar
data = pd.DataFrame(data={
"product": ["tarte", "gâteau", "biscuit", "mille-feuille", "éclair", "brownie"],
"price": [2.99, 3.49, 1.99, 4.99, 5.99, 6.99],
"weight": [250, 300, 200, 400, 500, 600]
"weight": [400, 500, 100, 250, 150, 350]
})
figure: Figure = make_subplots(rows=1, cols=3, subplot_titles=("Prix", "Poids unitaires"))
subplot = figure.get_subplot(row=1, col=2)
subplot.xaxis["domain"] = [0.3555555, 1.0]
print(subplot, dir(subplot))
figure: Figure = make_subplots(rows=1, cols=2, subplot_titles=("Prix", "Poids unitaires"))
# subplot = figure.get_subplot(row=1, col=2)
# subplot.xaxis["domain"] = [0.3555555, 1.0]
figure.add_trace(Bar(name="Prix", x=data["product"], y=data["price"]), row=1, col=1)
figure.add_trace(Bar(name="Poids", x=data["product"], y=data["weight"]), row=1, col=2)
figure.update_layout(template="seaborn", title="Prix et poids unitaires", font={"family": "Cabin", "size": 13})
figure.update_traces(row=1, col=2, specs=2)
figure.show(renderer="browser")

View File

@@ -1,5 +1,5 @@
import pandas as pd
from plotly.graph_objs import Figure, Bar, Scatter
from plotly.graph_objs import Figure, Bar
data = pd.DataFrame(
data={
@@ -8,13 +8,21 @@ data = pd.DataFrame(
"weight": [250, 300, 200, 400, 500, 600],
}
)
figure: Figure = Figure(data=[Bar(name="Prix", x=data["product"], y=data["price"])])
figure.add_hrect(y0=2.75, y1=4.5, fillcolor="gray", opacity=0.25, layer="below")
figure: Figure = Figure(data=[])
figure.add_hrect(
y0=2.75,
y1=4.5,
fillcolor="gray",
opacity=0.25,
layer="below",
)
figure.add_trace(Bar(name="Prix", x=data["product"], y=data["price"]))
figure.add_annotation(text="Zone de prix spéciale", xref="paper", yref="paper", x=0.5, y=0.5, xanchor="center", yanchor="middle", showarrow=False, font={"family": "Cabin", "size": 20})
figure.update_layout(
template="seaborn",
title="Prix et poids unitaires",
font={"family": "Cabin", "size": 13},
xaxis={"title": "Produit", "showgrid": False},
yaxis={"title": "Prix (€)", "showgrid": False}
yaxis={"title": "Prix (€)", "showgrid": False},
)
figure.show(renderer="browser")