Skip to content

Commit 64c12dc

Browse files
committed
English version
1 parent e945ff4 commit 64c12dc

File tree

3 files changed

+92
-153
lines changed

3 files changed

+92
-153
lines changed

_quarto-en.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ project:
1818
- content/manipulation/04b_regex_TP.qmd
1919
- content/visualisation/index.qmd
2020
- content/modelisation/index.qmd
21+
- content/modelisation/0_preprocessing.qmd
2122

2223

2324
website:

content/modelisation/01_preprocessing/_exo2.qmd

Lines changed: 2 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,7 @@ __This exercise is OPTIONAL__
2828
`Percent of adults with less than a high school diploma, 2015-19`,
2929
`Percent of adults with a bachelor's degree or higher, 2015-19`.
3030
2. Use a graph to represent the correlation matrix. You can use the `seaborn` package and its `heatmap` function.
31-
3. Display a scatter plot matrix of the variables in `df2` using `pd.plotting.scatter_matrix`.
32-
4. (Optional) Recreate these figures using `Plotly`, which also offers the option to create a correlation matrix.
31+
3332

3433
:::
3534

@@ -82,7 +81,7 @@ sns.heatmap(
8281
corr,
8382
mask=mask, # Mask upper triangular matrix
8483
cmap=cmap,
85-
annot=True
84+
annot=True,
8685
vmax=.3,
8786
vmin=-.3,
8887
center=0, # The center value of the legend. With divergent cmap, where white is
@@ -95,29 +94,6 @@ plt.show(fig)
9594
```
9695

9796

98-
::: {.content-visible when-profile="fr"}
99-
Alors que celle construite directement avec `corr` de `Pandas`
100-
ressemblera plutôt à ce tableau :
101-
:::
102-
103-
::: {.content-visible when-profile="en"}
104-
105-
Whereas the one constructed directly with `corr` from `Pandas` will look more like this table:
106-
:::
107-
108-
109-
110-
```{python}
111-
#| output: false
112-
#| echo: true
113-
# Construction directement avec pandas également possible
114-
g2 = df2.drop("winner", axis = 1).corr().style.background_gradient(cmap='coolwarm').format('{:.2f}')
115-
```
116-
117-
```{python}
118-
g2
119-
```
120-
12197
::: {.content-visible when-profile="fr"}
12298
Le nuage de point obtenu à l'issue de la question 3 ressemblera à :
12399
:::
@@ -143,12 +119,3 @@ The result of question 4, on the other hand, should look like the following char
143119
:::
144120

145121

146-
```{python}
147-
#| echo: true
148-
# 4. Matrice de corrélation avec plotly
149-
import plotly
150-
import plotly.express as px
151-
htmlsnip2 = px.scatter_matrix(df2)
152-
htmlsnip2.update_traces(diagonal_visible=False)
153-
htmlsnip2.show()
154-
```

content/modelisation/0_preprocessing.qmd

Lines changed: 89 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ description: |
1616
description-en: |
1717
In order to obtain data that is consistent with modeling assumptions, it is essential to take the time to prepare the data to be supplied to a model. The quality of the prediction depends heavily on this preliminary work, known as _preprocessing_. This chapter presents the issues involved and illustrates them using the `Scikit Learn` library, which makes this work less tedious and more reliable.
1818
bibliography: ../../reference.bib
19-
image: featured_preprocessing.png
19+
image: https://minio.lab.sspcloud.fr/lgaliana/generative-art/pythonds/artisan.jfif
2020
echo: false
2121
---
2222

@@ -99,7 +99,7 @@ The `Scikit` user guide is a valuable reference to consult regularly. The sectio
9999

100100

101101

102-
:::: {.content-visible when-profile="en"}
102+
:::: {.content-visible when-profile="fr"}
103103

104104
::: {.note}
105105
## `Scikit Learn`, un succès français ! 🐓🥖🥐
@@ -551,6 +551,7 @@ Detecting data drift is crucial to adjust or retrain the model, ensuring its rel
551551
::::
552552

553553

554+
::: {.content-visible when-profile="fr"}
554555
### Normalisation
555556

556557
La **normalisation** est l'action de transformer les données de manière
@@ -560,87 +561,26 @@ Par défaut, la norme utilisée par `Scikit` est une norme $\mathcal{l}_2$.
560561
Cette transformation est particulièrement utilisée en classification de texte ou pour effectuer du *clustering*.
561562

562563
Au passage, ceci est l'occasion de découvrir comment découper ses données en plusieurs échantillons grâce à la fonction [`train_test_split`](https://scikit-learn.org/dev/modules/generated/sklearn.model_selection.train_test_split.html) de `Scikit`. Nous allons faire un échantillon de 70% des données pour estimer les paramètres de normalisation (phase d'apprentissage) et extrapoler aux 30% de données restantes. Cette répartition est assez classique mais est bien-sûr adaptable selon les projets. L'avantage d'utiliser [`train_test_split`](https://scikit-learn.org/dev/modules/generated/sklearn.model_selection.train_test_split.html) plutôt que de faire soi-même les échantillonnages avec la méthode `sample` de `Pandas` est que la fonction de `Scikit` permettra d'aller beaucoup plus loin dans le paramétrage de l'échantillonnage, notamment si on désire de la stratification, tout en étant fiable. Faire ceci de manière manuelle est fastidieux et risqué car potentiellement complexe à mettre en oeuvre sans erreur.
563-
564-
565-
::: {.exercise}
566-
## Exercice 4 : Normalisation
567-
568-
1. A l'aide de la documentation de la fonction [`train_test_split`](https://scikit-learn.org/dev/modules/generated/sklearn.model_selection.train_test_split.html) de `Scikit`, créer deux échantillons (respectivement 70% et 30% des données).
569-
1. Normaliser la variable `Median_Household_Income_2019` (ne pas écraser les valeurs !) et regarder l'histogramme avant/après normalisation.
570-
3. Vérifier que la norme $\mathcal{l}_2$ est bien égale à 1 (grâce à la fonction `np.linalg.norm` et l'argument `axis=1` pour les 10 premières observations, sur l'ensemble d'entraînement puis sur les autres observations.
571-
572564
:::
573565

566+
::: {.content-visible when-profile="en"}
574567

575-
```{python}
576-
from sklearn.model_selection import train_test_split
577-
578-
X_train, X_test, y_train, y_test = train_test_split(
579-
df2.drop(columns = "winner"), df2['winner'], test_size=0.3)
580-
```
581-
582-
```{python}
583-
#| eval: false
584-
# 1. Normalisation des variables et vérification sur Median_Household_Income_2019
585-
scaler = preprocessing.Normalizer().fit(X_train)
586-
X1 = pd.DataFrame(
587-
scaler.transform(X_train),
588-
columns = X_train.columns
589-
)
590-
X2 = pd.DataFrame(
591-
scaler.transform(X_test),
592-
columns = X_test.columns
593-
)
594-
```
595-
596-
```{python}
597-
p1 = (
598-
ggplot(X_train, aes(x = "Median_Household_Income_2019")) +
599-
geom_histogram() +
600-
labs(x = "2019 Median household income ($)")
601-
)
602-
p2 = (
603-
ggplot(X1, aes(x = "Median_Household_Income_2019")) +
604-
geom_histogram() +
605-
labs(x = "2019 Median household income (normalized, training sample)")
606-
)
607-
p3 = (
608-
ggplot(X2, aes(x = "Median_Household_Income_2019")) +
609-
geom_histogram() +
610-
labs(x = "2019 Median household income (normalized, extrapolated sample)")
611-
)
612-
```
613-
614-
615-
```{python}
616-
#| fig-cap: "Question 2, avant normalisation"
617-
p1
618-
```
568+
### Normalization
619569

620-
```{python}
621-
#| fig-cap: "Question 2, variable transformée, sur l'échantillon de normalisation"
622-
p2
623-
```
570+
**Normalization** is the process of transforming data to achieve a unit norm ($\mathcal{l}_1$ or $\mathcal{l}_2$).
571+
In other words, with the appropriate norm, the sum of elements equals 1.
572+
By default, `Scikit` uses an $\mathcal{l}_2$ norm.
573+
This transformation is especially useful in text classification or clustering.
624574

625-
```{python}
626-
#| fig-cap: "Question 2, variable transformée, à partir des paramètres entraînés"
627-
p3
628-
```
575+
This is also an opportunity to explore how to split data into multiple samples using the [`train_test_split`](https://scikit-learn.org/dev/modules/generated/sklearn.model_selection.train_test_split.html) function in `Scikit`. We will create a 70% sample of the data to estimate normalization parameters (training phase) and extrapolate to the remaining 30%. This split is fairly standard but, of course, adaptable depending on the project. The advantage of using [`train_test_split`](https://scikit-learn.org/dev/modules/generated/sklearn.model_selection.train_test_split.html) instead of manually sampling with `Pandas``sample` method is that `Scikit`’s function allows for much more control over sampling, particularly if stratification is desired, while being reliable. Doing this manually can be tedious and risky, as it is potentially complex to implement without errors.
576+
:::
629577

630-
Enfin, si on calcule la norme, on obtient bien le résultat attendu à la fois sur l'échantillon _train_ et sur l'échantillon extrapolé.
631578

632-
```{python}
633-
# 3. Vérification de la norme L2
634-
pd.DataFrame(
635-
{
636-
"X_train_norm2": np.linalg.norm(X1.head(10), axis=1),
637-
"X_test_norm2": np.linalg.norm(X2.head(10), axis=1)
638-
}
639-
).head(5)
640-
```
579+
{{< include "01_preprocessing/_exo4.qmd" >}}
641580

642581

643582

583+
::: {.content-visible when-profile="fr"}
644584
## Encodage des valeurs catégorielles
645585

646586
Les données catégorielles doivent être recodées sous forme de valeurs numériques pour être intégrés aux modèles de *machine learning*.
@@ -701,76 +641,107 @@ puis retirer dans les deux queues de la distribution les valeurs extrêmes corre
701641

702642
Plusieurs packages permettent de faire ce type d'opérations, qui sont parfois plus complexes si on s'intéresse aux outlier sur plusieurs variables.
703643
On pourra notamment citer la fonction `IsolationForest()` du package `sklearn.ensemble`.
644+
:::
645+
::: {.content-visible when-profile="en"}
704646

705-
## Exercice d'application
647+
## Encoding Categorical Values
706648

707-
::: {.caution}
708-
## Attention aux nouvelles modalités !
649+
Categorical data must be recoded into numeric values to be integrated into machine learning models.
709650

710-
Les _transformers_ créent un _mapping_ entre des modélités textuelles et des valeurs numériques. Cela présuppose que les données sur lesquelles a été construit ce _mapping_ intègrent l'ensemble des valeurs possibles pour les modalités textuelles.
651+
This can be done in several ways with `Scikit`:
711652

712-
Néanmoins, si de nouvelles modalités apparaissent, le classifieur ne saura pas comment celles-ci doivent être transformées en valeurs numériques. Cela provoquera une erreur pour `Scikit`. Cette erreur technique est logique puisqu'il faudrait mettre à jour non seulement le _mapping_ mais aussi l'estimation des paramètres sous-jacents.
653+
* `LabelEncoder`: transforms a vector `["a","b","c"]` into a numeric vector `[0,1,2]`. This approach has the drawback of introducing an order to the categories, which is not always desirable.
654+
* `OrdinalEncoder`: a generalized version of `LabelEncoder` designed to apply to matrices ($X$), while `LabelEncoder` applies mainly to a vector ($y$).
713655

714-
:::
656+
For one-hot encoding, several methods are available:
715657

658+
* `pandas.get_dummies` performs a dummy expansion.
659+
A vector of size *n* with *K* categories will be transformed into a matrix of size $n \times K$, where each column represents a dummy variable for category *k*.
660+
There are $K$ categories, resulting in multicollinearity.
661+
In linear regression with a constant,
662+
one category should be removed before estimation.
716663

664+
* `OneHotEncoder` is a generalized (and optimized) version of dummy expansion. This is the recommended method.
717665

718-
::: {.exercise}
719-
## Exercice 5 : Encoder des variables catégorielles
720666

721-
1. Créer `df` qui conserve uniquement les variables `state_name` et `county_name` dans `votes`.
722-
2. Appliquer à `state_name` un `LabelEncoder`
723-
*Note : Le résultat du label encoding est relativement intuitif, notamment quand on le met en relation avec le vecteur initial.*
667+
## Imputation
668+
669+
Data often contains missing values, that is, observations in our _DataFrame_ containing a `NaN`. These gaps can cause bugs or misinterpretations when moving to modeling.
670+
One initial approach could be to remove all observations with a `NaN` in at least one column.
671+
However, if our table contains many `NaN`s, or if these are spread across numerous columns,
672+
we risk removing a significant number of rows, and, with that, losing important information, as missing values are rarely [randomly distributed](https://stefvanbuuren.name/fimd/sec-MCAR.html).
673+
674+
While this solution remains viable in many cases, a more robust approach called *imputation* exists. This method involves replacing missing values with a specified value. For example:
675+
676+
- Mean imputation: replacing all `NaN`s in a column with the column's average;
677+
- Median imputation on the same principle, or using the most frequent column value for categorical variables;
678+
- Regression imputation: using other variables to interpolate an appropriate replacement value.
679+
680+
More complex methods are available, but in many cases, the above approaches can provide much more satisfactory results.
681+
The `Scikit` package makes imputation very straightforward ([documentation here](https://scikit-learn.org/stable/modules/impute.html)).
682+
724683

725-
3. Regarder la *dummy expansion* de `state_name`
726-
4. Appliquer un `OrdinalEncoder` à `df[['state_name', 'county_name']]`
727-
*Note : Le résultat du _ordinal encoding_ est cohérent avec celui du label encoding*
684+
## Handling Outliers
728685

729-
5. Appliquer un `OneHotEncoder` à `df[['state_name', 'county_name']]`
686+
Outliers are observations that significantly deviate from the general trend of other observations in a dataset. In other words, they are data points that stand out unusually from the overall data distribution.
687+
This may be due to data entry errors, respondents who incorrectly answered a survey, or simply extreme values that may bias a model too much.
730688

731-
*Note : `scikit` optimise l'objet nécessaire pour stocker le résultat d'un modèle de transformation. Par exemple, le résultat de l'encoding One Hot est un objet très volumineux. Dans ce cas, `scikit` utilise une matrice Sparse.*
689+
For example, these could be 3 individuals measuring over 4 meters in height within a population or household incomes exceeding 10 million euros per month at a national level.
732690

691+
It is good practice to routinely examine the distribution of available variables
692+
to check if some values deviate too significantly from others.
693+
Sometimes these values will interest us, for instance, if we are focusing solely on very high incomes (top 0.1%) in France. However, often these values will be more of a hindrance, especially if they don’t make sense in the real world.
694+
695+
If we find that the presence of these extreme values or *outliers* in our dataset is more problematic than helpful,
696+
it is reasonable to simply remove them.
697+
Most of the time, we set a percentage of data to remove, such as 0.1%, 1%, or 5%,
698+
then remove the corresponding extreme values in both tails of the distribution.
699+
700+
Several packages can perform these operations, which can become complex if we examine outliers across multiple variables.
701+
The `IsolationForest()` function in the `sklearn.ensemble` package is particularly noteworthy.
733702
:::
734703

735-
```{python}
736-
#1. Création de df
737-
df = votes.loc[
738-
:,["state_name",'county_name']
739-
]
740-
```
741704

742-
Si on regarde les _labels_ et leurs transpositions numériques via `LabelEncoder`
743705

744-
```{python}
745-
#2. Appliquer un LabelEncoder à stat_name
746-
label_enc = preprocessing.LabelEncoder().fit(df['state_name'])
747-
np.column_stack((label_enc.transform(df['state_name']),df['state_name']))
748-
```
706+
::: {.content-visible when-profile="fr"}
707+
## Exercice d'application
749708

709+
::: {.caution}
710+
## Attention aux nouvelles modalités !
750711

751-
```{python}
752-
# 3. dummy expansion de state_name
753-
pd.get_dummies(df['state_name'])
754-
```
712+
Les _transformers_ créent un _mapping_ entre des modalités textuelles et des valeurs numériques. Cela présuppose que les données sur lesquelles a été construit ce _mapping_ intègrent l'ensemble des valeurs possibles pour les modalités textuelles.
755713

756-
Si on regarde l'`OrdinalEncoder`:
714+
Néanmoins, si de nouvelles modalités apparaissent, le classifieur ne saura pas comment celles-ci doivent être transformées en valeurs numériques. Cela provoquera une erreur pour `Scikit`. Cette erreur technique est logique puisqu'il faudrait mettre à jour non seulement le _mapping_ mais aussi l'estimation des paramètres sous-jacents.
757715

758-
```{python}
759-
# 4. OrdinalEncoder
760-
ord_enc = preprocessing.OrdinalEncoder().fit(df)
761-
# ord_enc.transform(df[['state', 'county']])
762-
ord_enc.transform(df)[:,0]
763-
```
716+
:::
764717

718+
::::
719+
720+
:::: {.content-visible when-profile="en"}
721+
722+
## Application exercise
723+
724+
::: {.caution}
725+
## Be careful with new categories!
726+
727+
Transformers create a mapping between text categories and numeric values. This assumes that the data used to build this mapping includes all possible values for the text categories.
728+
729+
However, if new categories appear, the classifier will not know how to transform these into numeric values, which will cause an error in `Scikit`. This technical error makes sense, as it would require updating not only the mapping but also the estimation of underlying parameters.
730+
731+
:::
732+
::::
733+
734+
{{< include "01_preprocessing/_exo5.qmd" >}}
765735

766-
```{python}
767-
# 5. OneHotEncoder
768-
onehot_enc = preprocessing.OneHotEncoder().fit(df)
769-
onehot_enc.transform(df)
770-
```
771736

772737

773-
# Références
738+
::: {.content-visible when-profile="fr"}
739+
# Références {.unnumbered}
740+
:::
741+
742+
::: {.content-visible when-profile="en"}
743+
# Reference {.unnumbered}
744+
:::
774745

775746
::: {#refs}
776747
:::

0 commit comments

Comments
 (0)