x <- "bleu"
if (x %in% c("bleu", "rouge", "jaune")) {
cat(x, "est une couleur primaire\n")
} else {
cat(x, "n'est pas une couleur primaire\n")
}bleu est une couleur primaire
Les alternatives permettent d’effectuer une opération uniquement si une condition logique est remplie. L’alternative la plus couramment utilisée dans les langages de haut-niveau est le if-else.
SI Test
Instruction 1
SINON
Instruction 2
FIN SI
En R, on utilise la syntaxe suivante :
x <- "bleu"
if (x %in% c("bleu", "rouge", "jaune")) {
cat(x, "est une couleur primaire\n")
} else {
cat(x, "n'est pas une couleur primaire\n")
}bleu est une couleur primaire
Le if-else peut être étendu à plusieurs tests.
SI Test 1
Instruction 1
SINONSI Test 2
Instruction 2
SINON
Instruction 3
FIN SI
Ce qui en R donne :
x <- "violet"
if (x %in% c("bleu", "rouge", "jaune")) {
cat(x, "est une couleur primaire\n")
} else if (x %in% c("violet", "vert", "orange")) {
cat(x, "est une couleur secondaire\n")
} else {
cat(x, "n'est ni une couleur primaire, ni une couleur secondaire\n")
}violet est une couleur secondaire
En R, il existe une fonction pour le if-else :
x <- 1
res <- ifelse(
x > 0, # La condition
"positif", # Si vrai
"négatif" # Sinon
)
print(res)[1] "positif"
Les boucles permettent d’effectuer des opérations plusieurs de fois de suite de manière controlée. Il y en a plusieurs sortes. Dans le cas de traitements de données sous R, vous serez surtout intéressé par la boucle de parcours qui est éxécutée sur chacun des éléments d’une liste.
POURCHAQUE valeur DANS collection
Instruction 1
FIN POURCHAQUE
En R, cela se traduit par :
couleur_primaire <- c("bleu", "rouge", "jaune")
for (couleur in couleur_primaire) {
cat(couleur, "\n")
}bleu
rouge
jaune
On peut réaliser des boucles imbriquées :
sequence <- 2:4
for (i in sequence) {
for (j in sequence) {
cat(i, "/", j, "= ")
cat(i / j, "\n")
}
}2 / 2 = 1
2 / 3 = 0.6666667
2 / 4 = 0.5
3 / 2 = 1.5
3 / 3 = 1
3 / 4 = 0.75
4 / 2 = 2
4 / 3 = 1.333333
4 / 4 = 1
Il arrive que l’on veuille stopper la boucle au cours de son exécution ou tout simplement passer à l’élément suivant de la liste. Pour cela, on utilise les mots-clés break et next.
couleur_primaire <- c("bleu", "rouge", "jaune")
for (couleur in couleur_primaire) {
if (couleur == "bleu") {
cat("J'aime pas le", couleur, "je passe...\n")
next # passer à l'élément suivant
} else if (couleur == "rouge") {
cat("Du", couleur, "je panique !!! j'me tire...\n")
break # briser la boucle
}
cat(couleur, "\n")
}J'aime pas le bleu je passe...
Du rouge je panique !!! j'me tire...
On voit que jaune n’est jamais affiché car la boucle est brisée avant que le jaune puisse être affiché.
purrr et la programmation fonctionnellepacman::p_load(purrr, dplyr)Les boucles for sont :
Dans ce cadre, le package purrr a été conçu pour optimiser l’écriture de boucle dans un paradigme de programmation fonctionnelle. Pour faire simple, l’idée est que chaque étape du code est réalisée par l’appele d’une fonction qui ne retourne qu’un seul résultat.
Pour illustrer le principe, prenant un example de boucle qui, pour chaque valeur d’un vecteur numérique, calcul son carré et ajoute le résultat dans un vecteur préalablement instantié :
# Définition de la donnée
seq <- 1:5
# Instantiation d'un vecteur pour stocker les résultat
res <- vector()
# La boucle
for (i in seq) {
# Calcul du carré
square <- i ^ 2
# Stockage du résultat
res <- c(res, square)
}
print(res)[1] 1 4 9 16 25
Avec purrr, l’opération est considérée comme l’application d’une fonction qui retourne une liste de valeurs. Cette liste est ensuite convertie en vecteur via list_c().
# Définition de la donnée
seq <- 1:5
# Application de la fonction
res <- map(seq, \(i) i ^ 2) |>
list_c() # conversion de la liste en vecteur
print(res)[1] 1 4 9 16 25
Pour rendre la chose encore plus claire, on pourrait écrire la fonction du carré et la passer directement à la fonction map() :
# Fonction du carré
square <- function(x) {
return(x ^ 2)
}
# Définition de la donnée
seq <- 1:5
# Application de la fonction
res <- map(seq, square) |>
list_c() # conversion de la liste en vecteur
print(res)[1] 1 4 9 16 25
Au final, le code est plus structuré, lisible et cohérent.
Prenons un exemple plus parlant pour du traitement des données. Imaginons que l’on dispose de plusieurs data.frame dans une liste et que l’on souhaite réaliser une régression linéaire sur ces différentes sources de données et récupérer le \(R^2\) pour chacune.
# Données d'entraînement de dplyr, séparée par CYL (4, 6 et 8)
data_list <- split(mtcars, mtcars$cyl)
r_squared <-
# Application de la régression linéaire
map(data_list, \(df) lm(mpg ~ wt, data = df)) |>
# Application du résumé sur les objets lm
map(summary) |>
# Récupérer les R2 sous forme de vecteur numérique nommée par valeur du CYL
map_dbl("r.squared")
print(r_squared) 4 6 8
0.5086326 0.4645102 0.4229655
Vous me direz qu’une fonction peut prendre plusieurs arguments en entrée. Effectivement, c’est pourquoi purrr dispose d’une fonction map2() qui itère sur 2 listes d’arguments simultanément. À plus de 2 listes d’arguments, on utilise la fonction pmap().
# Ajusté des modèles linéaires
mods <- map(data_list, \(df) lm(mpg ~ wt, data = df))
# Faire une prédiction (predict()) avec les modèles préalablement ajustés (mods)
# sur les données séparées (data_list)
map2(mods, data_list, predict)$`4`
Datsun 710 Merc 240D Merc 230 Fiat 128 Honda Civic
26.47010 21.55719 21.78307 27.14774 30.45125
Toyota Corolla Toyota Corona Fiat X1-9 Porsche 914-2 Lotus Europa
29.20890 25.65128 28.64420 27.48656 31.02725
Volvo 142E
23.87247
$`6`
Mazda RX4 Mazda RX4 Wag Hornet 4 Drive Valiant Merc 280
21.12497 20.41604 19.47080 18.78968 18.84528
Merc 280C Ferrari Dino
18.84528 20.70795
$`8`
Hornet Sportabout Duster 360 Merc 450SE Merc 450SL
16.32604 16.04103 14.94481 15.69024
Merc 450SLC Cadillac Fleetwood Lincoln Continental Chrysler Imperial
15.58061 12.35773 11.97625 12.14945
Dodge Challenger AMC Javelin Camaro Z28 Pontiac Firebird
16.15065 16.33700 15.44907 15.43811
Ford Pantera L Maserati Bora
16.91800 16.04103
Et avec pmap() :
x <- list(1, 1, 1)
y <- list(10, 20, 30)
z <- list(100, 200, 300)
pmap(list(x, y, z), \(first, second, third) (first + third) * second)[[1]]
[1] 1010
[[2]]
[1] 4020
[[3]]
[1] 9030
Même si la programmation fonctionnelle proposée par purrr est très attrayante, il demeure indispensable de savoir écrire une boucle classique. Il s’agit d’une des briques de base en programmation.
lapply(), sapply(), etc.
Quand vous ferez vos recherches sur internet, vous verrez souvent passer les fonctions lapply, sapply(), tapply() et autres apply(). Ces fonctions fonctionnent de manière assez similaires aux fonctions proposées par purrr mais viennent par défaut sous R.
Je vous encourage à utiliser purrr car ses fonctions sont plus cohérentes entre elles. De plus, le package vient avec de nombreux “petits plus” très agréables au quotidien.
Si vous êtes intéressés par la question, vous pouvez lire ce biais sur stackoverflow.
Ressources :
purrrpurrr