La version originale de ce chapitre a été écrite par Ewen Gallic dans le cadre de son support de cours d’Ewen Gallic intitulé Logiciel R et programmation, chapitre 4 Boucles et calculs vectoriels.

Il existe deux sortes de boucles dans R. Celles pour lesquelles les itérations continuent tant qu’une condition n’est pas invalidée (while), et celles pour lesquelles le nombre d’itérations est défini au moment de lancer la boucle (for).

Avant de présenter chacune de ces fonctions, il est nécessaire de préciser que les boucles ne sont pas le point fort de R. Dès que l’on souhaite appliquer une fonction à chaque élément d’un vecteur, et/ou que le résultat de chaque itération ne dépend pas de l’itération précédente, il est préférable de vectoriser les calculs (voir le chapitre sur la vectorisation).

Les boucles avec while

Quand on souhaite répéter un calcul tant qu’une condition est satisfaite, on utilise la fonction while, avec la syntaxte suivante :

while (condition) {
  instruction
}

avec condition une valeur logique (TRUE ou FALSE), et instruction du code, qui peut être entouré d’accolades si on souhaite évaluer plusieurs instructions.

Le code instruction sera répété tant que condition est vrai. Prenons l’exemple d’une plante qui mesure 10 centimètres et qui va grandir de 10 % tous les ans jusqu’à atteindre 2 mètres.

taille <- 0.10
duree <- 0
while (taille < 2) {
  taille <- taille * 1.1
  duree <- duree + 1
}
message(glue::glue("La plante a atteint {round(taille, 1)} mètres en {duree} années."))
La plante a atteint 2.1 mètres en 32 années.

Les boucles avec for

Quand on connaît le nombre d’itérations à l’avance, on peut utiliser la boucle for. La syntaxe est la suivante :

for (variable in vector) {
  instruction
}

avec variable le nom d’une variable locale à la boucle for, vector un vecteur à n éléments définissant les valeurs que prendra variable pour chacun des n tours, et instruction le code à exécuter à chaque itération.

On peut utiliser for pour remplir les éléments d’une liste, ou d’un vecteur.

# Mauvaise manière
resultat <- NULL
for (i in 1:3) {
  resultat[i] <- i
}
resultat
[1] 1 2 3

À chaque itération, R doit trouver le vecteur de destination en mémoire, créer un nouveau vecteur qui permettra de contenir plus de données, copier données depuis l’ancien vecteur pour les insérer dans le nouveau, et enfin supprimer l’ancien vecteur (Ross, 2014). C’est une opération coûteuse en temps. Un moyen de rendre cette allocation plus efficace est de créer a priori le vecteur ou la liste en le remplissant avec des données manquantes. Ainsi, R n’aura pas besoin de ré-allouer la mémoire à chaque itération.

# Manière plus économique
resultat <- rep(NA, 3)
for (i in 1:3) {
  resultat[i] <- i
}
resultat
[1] 1 2 3

Les conditions

On peut soumettre l’exécution de codes en R à conditions que certaines conditions soient honorées.

Les instructions if … else

Les instructions if et else fournissent un moyen d’exécuter du code si une condition est respectée ou non. La syntaxe prend deux formes :

# Première forme (pas de code si condition == FALSE)
if (condition) {instruction si vrai}

# Seconde forme
if (condition) {instruction si vrai} else {instruction si faux}

avec condition un logique, instruction si vrai le code à exécuter si la condition est vérifiée et instruction si faux le code à exécuter si la condition n’est pas remplie. À nouveau, on peut avoir recours aux accolades pour créer des regroupements.

# Simple condition
x <- 2
if (x == 2) print("Hello")
[1] "Hello"
x <- 3
if (x == 2) print("Hello")

# Avec des instructions dans le cas contraire
if (x == 2) print("Hello") else print("x est différent de 2")
[1] "x est différent de 2"
if (x == 2) {
  print("Hello")
} else {
  x <- x - 1
  print(paste0("La nouvelle valeur de x : ", x))
}
[1] "La nouvelle valeur de x : 2"

Attention, lorsque l’on fait des regroupements et qu’on utilise la structure if et else, il est nécessaire d’écrire le mot else sur la même ligne que la parenthèse fermante du groupe d’instructions à réaliser dans le cas où la condition du if est vérifiée.

La fonction switch

Avec la fonction switch, on peut indiquer à R d’exécuter un code en fonction du résultat obtenu lors d’un test. La syntaxe est la suivante :

switch(valeur_test,
  cas_1 = {
    instruction_cas_1
  },
  cas_2 = {
    instruction_cas_2
  },
  ...
)

avec valeur_test un nombre ou une chaîne de caractères. Si valeur_test vaut cas_1, alors uniquement instruction_cas_1 sera évaluée, si valeur_test vaut cas_2, alors ce sera instruction_cas_2 qui le sera, et ainsi de suite. On peut rajouter une valeur par défaut en utilisant la syntaxte suivante :

switch(valeur_test,
  cas_1 = {
    instruction_cas_1
  },
  cas_2 = {
    instruction_cas_2
  },
  ...,
  {
    instruction_defaut
  }
)

Voici un exemple d’utilisation, issu de la page d’aide de la fonction.

centre <- function(x, type) {
  switch(type,
    mean = mean(x),
    median = median(x),
    trimmed = mean(x, trim = .1)
  )
}
x <- rcauchy(10)
centre(x, "mean")
[1] 1.79229
centre(x, "median")
[1] 0.3857596
centre(x, "trimmed")
[1] 0.6393129

L’instruction repeat … break

L’instruction repeat permet de répéter une expression. Il est nécessaire d’ajouter un test d’arrêt, à l’aide de l’instruction break.

i <- 1
repeat {
  i <- i + 1
  if (i == 3) break
}
i
[1] 3

L’instruction next

L’instruction next autorise de passer immédiatement à l’itération suivante d’une boucle for, while ou repeat.

result <- rep(NA, 10)
for (i in 1:10) {
  if (i == 5) next
  result[i] <- i
}
# Le 5e élément de result n'a pas été traité
result
 [1]  1  2  3  4 NA  6  7  8  9 10

Barre de progression

Lorsque l’exécution d’une boucle prend du temps, il peut être intéressant d’avoir une idée de l’état d’avancement des itérations. Pour cela, il est bien sûr possible d’afficher une valeur dans la console à chaque tour, chaque 10 tours, etc.

La fonction txtProgressBar de l’extension utils permet un affichage d’une barre de progression dans la console. Il suffit de lui fournir une valeur minimale et maximale, et de la mettre à jour à chaque itération. Le paramètre style autorise de surcroit à choisir un style pour la barre. Le style numéro 3 affiche un pourcentage de progression, et est utile lorsque d’autres résultats sont affichés dans la console lors de l’exécution de la boucle, dans la mesure où la barre est de nouveau affichée au complet dans la console si nécessaire.

Dans l’exemple qui suit, à chacun des dix tours, une pause de 0.1 seconde est effectuée, puis la barre de progression est mise à jour.

nb_tours <- 10
pb <- txtProgressBar(min = 1, max = nb_tours, style = 3)
for (i in 1:nb_tours) {
  Sys.sleep(0.1)
  setTxtProgressBar(pb, i)
}

  |                                                        
  |                                                  |   0%
  |                                                        
  |======                                            |  11%
  |                                                        
  |===========                                       |  22%
  |                                                        
  |=================                                 |  33%
  |                                                        
  |======================                            |  44%
  |                                                        
  |============================                      |  56%
  |                                                        
  |=================================                 |  67%
  |                                                        
  |=======================================           |  78%
  |                                                        
  |============================================      |  89%
  |                                                        
  |==================================================| 100%

Pour plus d’options, on pourra se référer à la fonction progress_bar de l’extension progress, présentée en détail sur https://r-pkg.org/pkg/progress.

Si l’exécution est vraiment longue, et qu’on est impatient de connaître les résultats, il existe de plus une fonction amusante dans l’extension beepr, qui porte le nom de beep. Plusieurs sons peuvent être utilisés (voir la page d’aide de la fonction).

library(beepr)
beep("mario")

Pour approfondir

On pourra également consulter le chapitre Boucles et exécution conditionnelle de l’excellente Introduction à R et au tidyverse de Julien Barnier (https://juba.github.io/tidyverse).