La semaine dernière, je suis tombée sur un article sur R-bloggers qui présentaient 10 fonctions de tidyverse qui “pourraient sauver ma journée” (10 Tidyverse functions that might save your day)
Je les ai toutes testées une à une, en changeant parfois les exemples pour qu’ils correspondent davantage à mes besoins, en termes de manipulation de données. C’est ce que je partage avec vous dans cet article.
Cet exercice m’a permis de faire trois belles découvertes, et de revoir certaines fonctions que j’avais un peu oublié…..
J’espère que cet article vous sera également utile !
La fonction crossing()
(qui appartient au package tidyr)
permet de créer un data frame avec en ligne toutes les combinaisons des éléments de deux vecteurs fournis en entrée. Autrement dit, la fonction crossing()
permet de construire facilement une grille.
library (tidyverse)
# vecteur de différences
delta <- c(1, 2, 4, 10, 20)
# vecteur d'crt types
sd <- c(5,10,15)
# creation du data frame contenant toutes les combinaisons
mydf <- crossing(delta, sd)
#affichage du data frame contenant les combinaisons de delta et sd
mydf
## # A tibble: 15 × 2
## delta sd
## <dbl> <dbl>
## 1 1 5
## 2 1 10
## 3 1 15
## 4 2 5
## 5 2 10
## 6 2 15
## 7 4 5
## 8 4 10
## 9 4 15
## 10 10 5
## 11 10 10
## 12 10 15
## 13 20 5
## 14 20 10
## 15 20 15
En pratique, cela peut servir, par exemple, à créer plusieurs scénarios pour des calculs de puissance ou nombre de sujets nécessaires :
# calcul des puissances pour chaque scenério de différence et d'écart type
mydf <- mydf %>%
mutate(power = power.t.test(n=50, delta=delta, sd=sd, sig.level=0.05, type="two.sample", alternative="two.sided")$power)
# creation d'une variable contenant en format chaine de caractère la valeur des écart types, pour l'utiliser dans les facets
mydf <- mydf %>%
mutate(sd_char=str_glue('sd={mydf$sd}'))
# plot
ggplot(mydf, aes(x=delta, y=power))+
geom_point()+
geom_line()+
facet_wrap(~sd_char)
Si vous avez trois variables, vous pouvez enchainer les fonctions crossing()
:
delta <- c(1, 2, 4, 10, 20)
sd <- c(5,10,15)
# data frame des combinaison de delta et sd
tmp1 <- crossing(delta, sd)
# data frame avec une variable n (nombre de sujets)
tmp2 <- tibble(n <- seq(10, 100, by=10))
# ceration du data frame avec toutes les combinaisons de delta, sd et n
mydf <- crossing(tmp1, tmp2)
head(mydf, n=15)
## # A tibble: 15 × 3
## delta sd `n <- seq(10, 100, by = 10)`
## <dbl> <dbl> <dbl>
## 1 1 5 10
## 2 1 5 20
## 3 1 5 30
## 4 1 5 40
## 5 1 5 50
## 6 1 5 60
## 7 1 5 70
## 8 1 5 80
## 9 1 5 90
## 10 1 5 100
## 11 1 10 10
## 12 1 10 20
## 13 1 10 30
## 14 1 10 40
## 15 1 10 50
La fonction rowwise()
(du package dplyr
) peut être employée lorsque vous souhaitez créer une nouvelle variable (dans un data frame) en utilisant des valeurs d’une même ligne, et en employant une fonction mathématique.
Cela peut être très utile si vous avez plusieurs variables indicatrices (d’une maladie par exemple, avec les valeurs 0 si la maladie est absente, et 1 si la maladie est présente), et que vous souhaitez créer une variable qui compte le nombre de maladies présente :
# creation de 10 variables de 5 valeurs 0 ou 1 dans uen matrice
mymat <- matrix(sample(c(0,1), 5*10, replace = TRUE), nrow = 5, ncol = 10)
# Conversion de la matrice en data frame
mydf <- as.data.frame(mymat)
# Création des noms des variables
names(mydf) <- paste0("M", 1:10)
mydf
## M1 M2 M3 M4 M5 M6 M7 M8 M9 M10
## 1 1 0 1 0 0 0 1 0 0 0
## 2 0 1 1 1 0 0 0 0 0 1
## 3 1 1 1 1 0 0 0 0 1 0
## 4 1 0 1 0 0 0 1 0 0 1
## 5 0 0 1 1 0 0 1 1 1 1
# création d'une variable M_all contenant la somme des variables M1 à M10 - sans employer rowwise()
tmp <- mydf %>%
mutate(M_all = M1+M2+M3+M4+M5+M6+M7+M8+M9+M10)
tmp
## M1 M2 M3 M4 M5 M6 M7 M8 M9 M10 M_all
## 1 1 1 1 0 1 0 0 0 0 0 4
## 2 1 1 0 0 1 0 1 0 1 0 5
## 3 1 1 0 1 1 1 0 0 0 1 6
## 4 1 1 0 1 0 0 1 1 1 1 7
## 5 0 0 1 1 0 1 0 0 0 0 3
Dans ce premier bout de code, j’ai utilisé l’opérateur +
, et je n’ai pas eu besoin d’employer la fonction rowwise()
.
Mais, si vous avez de nombreuses variables à additionner, il est beaucoup plus simple d’employer la fonction sum()
. Dans cette situation, vous devrez d’abord employer la fonction rowwise()
pour indiquer que la somme doit être réalisée pour chaque ligne.
# création d'une variable Ma_all2 contenant la somme en utilisant une fonction rowwise() avec la fonction mathématique sum()
tmp2 <- mydf %>%
rowwise() %>%
mutate(M_all2 = sum(c_across(M1:M10)))
tmp2
## # A tibble: 5 × 11
## # Rowwise:
## M1 M2 M3 M4 M5 M6 M7 M8 M9 M10 M_all2
## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 0 1 0 1 0 1 0 0 1 1 5
## 2 0 1 1 1 1 1 1 1 1 1 9
## 3 1 1 0 1 0 0 1 0 1 0 5
## 4 1 0 1 0 1 0 0 0 1 1 5
## 5 1 1 1 0 1 0 0 0 1 0 5
Remarque : La fonction c-across
imbriquée dans la fonction sum()
, permet de spécifier la première et la dernière colonne à considérer.
Pour plus d’informations sur cette fonction, vous pouvez consulter l’article “Opérations en ligne avec dplyr”
La fonction pluck()
(du package purrr
) permet d’extraire des valeurs contenues dans des listes emboitées en spécifiant le chemin jusqu’à la valeur, en utilisant une chaîne de caractères plutôt que des indices dans des crochets :
library(purrr)
# création d'une liste de 2 éléments A et B
mylist <- list(A = c(1,2,3), B=c("Luc", "Louis"))
# extraire les éléments A de la liste
pluck(mylist,"A")
## [1] 1 2 3
# extraire l'élément 2 de l'élément B de la liste
pluck(mylist,"B",2)
## [1] "Louis"
Cela est aussi très pratique pour extraire les valeurs d’une liste emboîtée dans une (ou plusieurs) listes :
mylist2 <- list(A = list(B = list(C = 123)))
mylist2
## $A
## $A$B
## $A$B$C
## [1] 123
pluck(mylist2, "A", "B", "C") # renvoie la valeur 123
## [1] 123
La fonction rownames_to_column()
(du package tibble
) permet de transformer des rownames (noms de ligne) en variable, et de la placer en première position.
Pour illustrer cela, nous pouvons employer le jeu de données mtcars qui contient des rownames :
head(mtcars)
## mpg cyl disp hp drat wt qsec vs am gear carb
## Mazda RX4 21.0 6 160 110 3.90 2.620 16.46 0 1 4 4
## Mazda RX4 Wag 21.0 6 160 110 3.90 2.875 17.02 0 1 4 4
## Datsun 710 22.8 4 108 93 3.85 2.320 18.61 1 1 4 1
## Hornet 4 Drive 21.4 6 258 110 3.08 3.215 19.44 1 0 3 1
## Hornet Sportabout 18.7 8 360 175 3.15 3.440 17.02 0 0 3 2
## Valiant 18.1 6 225 105 2.76 3.460 20.22 1 0 3 1
mtcars <- mtcars %>%
rownames_to_column(var="modele")
head(mtcars)
## modele mpg cyl disp hp drat wt qsec vs am gear carb
## 1 Mazda RX4 21.0 6 160 110 3.90 2.620 16.46 0 1 4 4
## 2 Mazda RX4 Wag 21.0 6 160 110 3.90 2.875 17.02 0 1 4 4
## 3 Datsun 710 22.8 4 108 93 3.85 2.320 18.61 1 1 4 1
## 4 Hornet 4 Drive 21.4 6 258 110 3.08 3.215 19.44 1 0 3 1
## 5 Hornet Sportabout 18.7 8 360 175 3.15 3.440 17.02 0 0 3 2
## 6 Valiant 18.1 6 225 105 2.76 3.460 20.22 1 0 3 1
La fonction rowid_to_column()
permet, quand à elle, de créer, en première position, une variable contenant le numéro de la ligne (ou index)
mtcars <- mtcars %>%
rowid_to_column(var="index")
head(mtcars)
## index modele mpg cyl disp hp drat wt qsec vs am gear carb
## 1 1 Mazda RX4 21.0 6 160 110 3.90 2.620 16.46 0 1 4 4
## 2 2 Mazda RX4 Wag 21.0 6 160 110 3.90 2.875 17.02 0 1 4 4
## 3 3 Datsun 710 22.8 4 108 93 3.85 2.320 18.61 1 1 4 1
## 4 4 Hornet 4 Drive 21.4 6 258 110 3.08 3.215 19.44 1 0 3 1
## 5 5 Hornet Sportabout 18.7 8 360 175 3.15 3.440 17.02 0 0 3 2
## 6 6 Valiant 18.1 6 225 105 2.76 3.460 20.22 1 0 3 1
Pour plus d’information sur la gestion des rownames, vous pouvez consulter mon article Comment gérer les rownames dans R ?
Il s’agit d’une fonction de nettoyage (qui appartient au package readr
) qui identifie le premier nombre rencontré dans une chaîne de caractères, supprime tous les caractères non numériques avant ce premier nombre, et tous les caractères après celui-ci :
# simulation de données
id <- 1:3
p_choco <- c("p=80%", "p=75%", "p=89%")
mydf <- data.frame(id, p_choco )
mydf
## id p_choco
## 1 1 p=80%
## 2 2 p=75%
## 3 3 p=89%
# nettoyage de la variable p_choco
mydf <- mydf %>%
mutate(p_choco_clean =parse_number(p_choco) )
mydf
## id p_choco p_choco_clean
## 1 1 p=80% 80
## 2 2 p=75% 75
## 3 3 p=89% 89
Cette fonction est particulièrement efficace lorsqu’on ne retrouve pas toujours le même motif :
# simulation de données
id <- 1:3
p_choco <- c(">10%", "faible : 5%", " attention 1 pourcent = trop peu")
mydf <- data.frame(id, p_choco)
mydf
## id p_choco
## 1 1 >10%
## 2 2 faible : 5%
## 3 3 attention 1 pourcent = trop peu
# nettoyage de la variable p_choco
mydf <- mydf %>%
mutate(p_choco_clean =parse_number(p_choco) )
mydf
## id p_choco p_choco_clean
## 1 1 >10% 10
## 2 2 faible : 5% 5
## 3 3 attention 1 pourcent = trop peu 1
A noter que la marque de regroupement spécifiée par les paramètres régionaux est ignorée à l’intérieur du numéro :
parse_number("1.234,56",
locale = locale(decimal_mark = ",",
grouping_mark = "."))
## [1] 1234.56
Vous retrouverez d’autres fonctions de nettoyage dans cet article “Nettoyer les données sous R : 7 situations courantes”
Il s’agit d’une série de fonctions du package forcats
qui permettent de regrouper les niveaux d’un facteur en un niveau commun, de manière automatique, en se basant sur :
fct_lump_min()
fct_lump_prop()
fct_lump_n()
Voici quelques exemples :
library(gt)
# structure des données pizzaplace
str(pizzaplace)
## tibble [49,574 × 7] (S3: tbl_df/tbl/data.frame)
## $ id : chr [1:49574] "2015-000001" "2015-000002" "2015-000002" "2015-000002" ...
## $ date : chr [1:49574] "2015-01-01" "2015-01-01" "2015-01-01" "2015-01-01" ...
## $ time : chr [1:49574] "11:38:36" "11:57:40" "11:57:40" "11:57:40" ...
## $ name : chr [1:49574] "hawaiian" "classic_dlx" "mexicana" "thai_ckn" ...
## $ size : chr [1:49574] "M" "M" "M" "L" ...
## $ type : chr [1:49574] "classic" "classic" "veggie" "chicken" ...
## $ price: num [1:49574] 13.2 16 16 20.8 18.5 ...
# passage en facteur de la variable size
pizzaplace$size <- as.factor(pizzaplace$size)
# création d'un tableau de contingence de la variable size
table(pizzaplace$size)
##
## L M S XL XXL
## 18956 15635 14403 552 28
# regrouper les catégories de la colonne size qui ont moins de 700 observations
# en une nouvelle catégorie appelée "autres tailles".
pizzaplace$size2 <-fct_lump_min(pizzaplace$size,min=700, other_level="autres tailles")
table(pizzaplace$size2)
##
## L M S autres tailles
## 18956 15635 14403 580
# regrouper les catégories de la colonne size qui ont moins de 30% observations
# en une nouvelle catégorie appelée "autres tailles".
pizzaplace$size3 <-fct_lump_prop(pizzaplace$size,p=0.3, other_level="autres tailles")
table(pizzaplace$size3)
##
## L M autres tailles
## 18956 15635 14983
# regrouper les catégories de la colonne size en gardant les 4 catégories les plus fréquentes
# et en regroupant les autres catégories en une nouvelle catégorie appelée "autres tailles"
pizzaplace$size4 <-fct_lump_n(pizzaplace$size,n=4, other_level="autres tailles")
table(pizzaplace$size4)
##
## L M S XL autres tailles
## 18956 15635 14403 552 28
# regrouper les catégories de la colonne size en gardant les 2 catégories les moins fréquentes
# et en regroupant les autres catégories en une nouvelle catégorie appelée "autres tailles"
pizzaplace$size5 <-fct_lump_n(pizzaplace$size,n=-2, other_level="autres tailles")
table(pizzaplace$size5)
##
## XL XXL autres tailles
## 552 28 48994
Vous retrouverez plus de détails et plus d’exemples dans l’article “5 fonctions pour regrouper des modalités”
Cette combinaison de fonctions (qui appartiennent au package forcats
pour fct_reorder
et ggplot2
pour geom_col
) permet de réordonner les modalités des variables catégorielles sur un graphique afin d’obtenir un meilleur effet visuel.
# création du jeu de données de comptage
pizza_type_count <- pizzaplace %>%
select(type) %>%
group_by(type) %>%
count()
pizza_type_count
# A tibble: 4 × 2
# Groups: type [4]
type n
<fct> <int>
1 chicken 11050
2 classic 14888
3 supreme 11987
4 veggie 11649
# plot des comptages
pizza_type_count %>%
ggplot(aes(x=type, y=n)) +
geom_col(fill="orange") +
ggtitle("Distribution du nombre de pizzas vendues, par type")
Ici l’ordre des modalités (chicken, classic, supreme et veggie) est celui des levels, par défaut, c’est-à-dire l’ordre alphabétique.
Si vous souhaitez ordonner les modalités de la pizza la moins vendue à la plus vendue, vous pouvez utiliser la fonction fct_reorder()
avant la fonction geom_col()
, comme ceci :
# creation d'un data frame avec le nombre de pizzas vendus par type
pizzaplace %>%
select(type) %>%
group_by(type) %>%
count() %>%
ungroup() %>% # necessité de dégrouper avant de modifier l'ordre
mutate(type=fct_reorder(type,n)) %>% # reordonne la variable type de la plus fréquente à la moins fréquente
ggplot(aes(x=type, y=n)) +
geom_col(fill="orange") +
ggtitle("Distribution du nombre de pizzas vendues, par type")
Pour obtenir le graphique dans l’autre sens, il suffit d’ajouter la fonction fct_rev()
, devant la fonction fct_reorder():
# creation d'un data frame avec le nombre de pizza vendu par type
pizzaplace %>%
select(type) %>%
group_by(type) %>%
count() %>%
ungroup() %>% # necessité de dégrouper avant de modifier l'ordre
mutate(type=fct_rev(fct_reorder(type,n))) %>% # reordonne la variable type de la moins fréquente à la plus fréquente
ggplot(aes(x=type, y=n)) + # plot
geom_col(fill="orange") +
ggtitle("Distribution du nombre de pizzas vendues, par type")
Si vous avez besoin de réordonner des modalités d’une variable catégorielle, vous pouvez utiliser l’interface graphique du package questionr
, comme montré dans cette vidéo :
Vous pouvez également consulter l’article “Comment modifier l’ordre d’affichage d’un plot ?”
La fonction separate()
(du package tidyr
) permet de séparer une colonne contenant des éléments séparés par des délimiteurs (par exemple, des virgules, des points-virgules, des espaces) en plusieurs colonnes.
Voici l’exemple de la page d’aide :
df <- tibble(x = c(NA, "x.y", "x.z", "y.z"))
df
## # A tibble: 4 × 1
## x
## <chr>
## 1 <NA>
## 2 x.y
## 3 x.z
## 4 y.z
# sépare la colonne x, en 2 colonnes nommées A et B
df2 <- df %>%
separate(x, c("A", "B"))
df2
## # A tibble: 4 × 2
## A B
## <chr> <chr>
## 1 <NA> <NA>
## 2 x y
## 3 x z
## 4 y z
# si on souhaite seulement la deuxième variable
df3 <- df %>%
separate(x, c(NA, "B"))
df3
## # A tibble: 4 × 1
## B
## <chr>
## 1 <NA>
## 2 y
## 3 z
## 4 z
À noter que cette fonction separate()
a été remplacé par les fonctions separate_wider_delim()
et separate_wider_position()
. Vous pouvez toujours employer separate()
, mais les deux autres fonctions sont recommandées. Je les trouve effectivement plus intuitives :
df4 <- df %>%
separate_wider_delim(x, delim= ".", names = c("A", "B"))
df4
## # A tibble: 4 × 2
## A B
## <chr> <chr>
## 1 <NA> <NA>
## 2 x y
## 3 x z
## 4 y z
# si on souhaite conserver seulement la deuxième variable
df5 <- df %>%
separate_wider_delim(x, delim= ".", names = c(NA, "B"))
df5
## # A tibble: 4 × 1
## B
## <chr>
## 1 <NA>
## 2 y
## 3 z
## 4 z
Voici un exemple avec la fonction separate_wider_position() :
df <- tibble(id = 1:3, x = c("m-123", "f-455", "f-123"))
df
## # A tibble: 3 × 2
## id x
## <int> <chr>
## 1 1 m-123
## 2 2 f-455
## 3 3 f-123
df6 <- df %>%
separate_wider_position(x, c(gender = 1, 1, unit = 3))
df6
## # A tibble: 3 × 3
## id gender unit
## <int> <chr> <chr>
## 1 1 m 123
## 2 2 f 455
## 3 3 f 123
L’argument x dans cette ligne de code est le nom de la colonne à séparer, et l’argument c(gender = 1, 1, unit = 3) indique la position des éléments à séparer. Plus précisément, cette ligne de code sépare la colonne x en trois colonnes nommées gender, NA (non nommée) et unit.
La première colonne (gender) est extraite à partir du premier caractère (position 1). La deuxième colonne est vide car il n’y a pas d’indication de position pour le deuxième élément à séparer (cela est indiqué par 1 dans les positions). La troisième colonne (unit) est extraite à partir du troisième caractère (position 3). Ainsi, cette ligne de code divise la colonne x en trois nouvelles colonnes et les stocke dans le data frame df. Le nom de la colonne d’origine (x) est remplacé par les trois nouvelles colonnes créées.
Il est important de noter que la fonction separate_wider_position()
peut être utilisée avec un grand nombre d’options pour traiter les données en fonction de la position des éléments à séparer, et que les arguments names_sep
et widths
peuvent être utilisés pour spécifier des positions et des largeurs personnalisées pour chaque nouvelle colonne.
Remarque: vous retrouverez un autre exemple d’utilisation de la fonction separate()
, dans les articles “Nettoyer les données sous R : 7 situations courantes” et “Nettoyer et valider les données avec R“.
La fonction separate_rows()
est une fonction du package tidyr
qui permet de séparer une colonne contenant des éléments séparés par des délimiteurs (par exemple, des virgules, des points-virgules, des espaces) en plusieurs lignes. Voici deux exemples d’utilisation de cette fonction :
library(tidyr)
# Création d'un data frame
df <- data.frame(id = c(1, 2), tags = c("r, programming, data science", "statistics, machine learning"))
df
## id tags
## 1 1 r, programming, data science
## 2 2 statistics, machine learning
# Séparation de la colonne tags en plusieurs lignes
df1 <- df %>% separate_rows(tags, sep = ", ")
df1
## # A tibble: 5 × 2
## id tags
## <dbl> <chr>
## 1 1 r
## 2 1 programming
## 3 1 data science
## 4 2 statistics
## 5 2 machine learning
La fonction str_flatten_comma()
est une fonction du package stringr
qui permet de concaténer les éléments d’un vecteur en une chaîne de caractères en les séparant par une virgule.
Supposons que vous avez un vecteur de chaînes de caractères contenant des noms de villes, et vous souhaitez concaténer ces noms en une seule chaîne séparée par des virgules. Vous pouvez utiliser la fonction str_flatten_comma()
de la manière suivante :
library(stringr)
# Création d'un vecteur de chaînes de caractères
cities <- c("New York", "Paris", "Tokyo", "London")
cities
## [1] "New York" "Paris" "Tokyo" "London"
# Concaténation des noms de villes avec une virgule
cities2 <- str_flatten_comma(cities)
cities2
## [1] "New York, Paris, Tokyo, London"
La fonction arrange()
du package dplyr
permet de trier les lignes d’un data frame selon une ou plusieurs variables.
La fonction distinct()
permet, quant à elle, de sélectionner les lignes uniques d’un data frame, en éliminant les doublons.
Supposons que vous avez un jeu de données contenant des informations sur des fruits, y compris leur nom, leur couleur et leur prix, et que vous voulez obtenir une liste de tous les fruits uniques par couleur, triés par ordre croissant de prix.
Nous pouvons utiliser distinct()
pour sélectionner les lignes uniques en fonction du fruit et de la couleur et arrange()
pour trier les résultats en fonction du prix :
library(dplyr)
# simulation de données
fruits <- data.frame(
nom = c("pomme", "banane", "raisin", "pomme", "fraise", "banane", "pomme"),
couleur = c("rouge", "jaune", "vert", "rouge", "rouge", "jaune", "verte"),
prix = c(0.5, 0.4, 0.3, 0.6, 0.7, 0.5, 0.6)
)
# affichage du data frame
fruits
## nom couleur prix
## 1 pomme rouge 0.5
## 2 banane jaune 0.4
## 3 raisin vert 0.3
## 4 pomme rouge 0.6
## 5 fraise rouge 0.7
## 6 banane jaune 0.5
## 7 pomme verte 0.6
# selection des lignes uniques et arrangement
resultats <- fruits %>%
distinct(couleur, nom, .keep_all = TRUE) %>% # sélectionner les lignes uniques en fonction du nom et de la couleur
arrange(couleur, prix) # trier les résultats en fonction du prix
# affichage du data frame resultats
resultats
## nom couleur prix
## 1 banane jaune 0.4
## 2 pomme rouge 0.5
## 3 fraise rouge 0.7
## 4 raisin vert 0.3
## 5 pomme verte 0.6
Les 3 fonctions que j’ai découvertes 🤩 sont pluck()
, parse_numbers()
, et str_flatten_comma()
.
Et les piqures de rappels 💉💉💉 sur les fonctions crossing
et separate()
m’ont fait beaucoup de bien !
Les autres fonctions, quant à elles, je les utilise couramment.
Et vous, est-ce que cet article vous a permis de découvrir des fonctions qui vont vous “sauver la vie” ?
👇👇👇 Dites le moi en commentaire 🙏
C’est possible en faisant un don sur la page Tipeee du blog
Enregistrez vous pour recevoir gratuitement mes fiches “aide mémoire” (ou cheat sheets) qui vous permettront de réaliser facilement les principales analyses biostatistiques avec le logiciel R et pour être informés des mises à jour du site.
3 réponses
Merci beaucoup.
C’est vraiment très intéressant. Toutefois, j’aimerais avoir ce document en PDF pour faciliter la lecture.
Bonjour,
les articles sont seulement consultables en ligne, il n’existe pas de pdf.
Bonsoir Claire
Merci pour ces éclairages et rappels.