Boucles, tests et fonctions
Informatique — R, chapitre 6
Ce chapitre clôt la première partie du cours. Il présente les trois constructions qui font d’une suite de commandes un véritable programme : le test, qui fait dépendre le code d’une condition ; la boucle, qui répète ; et la fonction, qui nomme un calcul pour le réutiliser.
Un avertissement d’emblée : en R, la boucle est moins nécessaire qu’ailleurs. La vectorisation du chapitre 1 la remplace le plus souvent avantageusement.
6.1 Le test
6.1.1 if et else
La condition entre parenthèses doit produire une seule valeur logique. Les accolades délimitent le bloc à exécuter.
6.1.2 Enchaîner plusieurs cas
else doit être sur la ligne de l’accolade fermante
if (x > 10) {
print("grand")
}
else { # ERREUR
print("petit")
}R lit ligne par ligne. Quand il rencontre l’accolade fermante seule, il considère l’instruction terminée et ne s’attend plus à un else.
Écrivez } else { sur la même ligne. C’est une contrainte du langage, pas une question de style.
6.1.3 ifelse : la version vectorisée
if ne traite qu’une valeur. Pour un vecteur entier, R fournit ifelse.
Trois arguments : la condition, la valeur si vrai, la valeur si faux. C’est la construction à privilégier pour recoder une variable — avec le motif par affectation conditionnelle vu au chapitre 3.
6.2 La boucle
6.2.1 for : un nombre connu de répétitions
La variable i prend successivement chaque valeur de la suite. Elle peut parcourir n’importe quel vecteur, pas seulement des nombres.
6.2.2 Stocker les résultats
Une boucle qui se contente d’afficher ne sert à rien. Il faut conserver ce qu’elle produit.
resultat <- c()
for (i in 1:n) {
resultat <- c(resultat, i^2) # a eviter
}À chaque tour, R recopie tout le vecteur pour en créer un plus grand. Sur mille itérations, c’est mille recopies de taille croissante.
Déclarez la taille d’avance — numeric(n), character(n), vector("list", n) — puis remplissez par indice. Sur des données volumineuses, l’écart de temps se compte en minutes.
6.2.3 while : un nombre inconnu de répétitions
while convient quand le nombre de tours dépend du calcul lui-même. Assurez-vous que la condition finit par devenir fausse, sans quoi la boucle ne s’arrête jamais.
6.2.4 Le piège de 1:n
Quand v est vide, length(v) vaut 0 et 1:0 produit la suite 1, 0 — deux itérations sur un vecteur qui n’a aucun élément.
Écrivez seq_along(v) plutôt que 1:length(v). La différence ne se voit qu’au moment où elle casse le programme, souvent bien après.
6.3 Les fonctions
6.3.1 Écrire la sienne
R renvoie automatiquement la dernière expression évaluée. return existe mais n’est utile que pour sortir avant la fin.
6.3.2 Les arguments par défaut
Les arguments sans défaut d’abord, ceux avec défaut ensuite. À l’appel, on peut nommer les arguments et les donner dans n’importe quel ordre.
6.3.3 Ce qui se passe à l’intérieur reste à l’intérieur
Une fonction travaille sur une copie. C’est une garantie précieuse : appeler une fonction ne peut pas altérer vos données à votre insu.
seuil <- 10
f <- function(x) x > seuil # a eviterCette fonction marche… tant que seuil existe dans votre session. Déplacée dans un autre script, elle échoue ou, pire, utilise un autre seuil sans le dire.
Passez tout ce dont la fonction a besoin en argument : f <- function(x, seuil = 10) x > seuil.
6.4 La famille apply : boucler sans boucle
6.4.1 sapply sur un vecteur
Une fonction sans nom, écrite directement dans l’appel, s’appelle une fonction anonyme. Depuis R 4.1, elle peut aussi s’écrire \(x) x^2 + 1.
6.4.2 sapply sur les colonnes d’un tableau
C’est l’usage le plus fréquent, et le plus utile.
6.4.3 apply sur les lignes ou les colonnes
Retenez : 1 comme ligne, 2 comme colonne, dans l’ordre où on les écrit dans d[lignes, colonnes].
| Fonction | Sur quoi | Renvoie |
|---|---|---|
sapply |
vecteur, liste, colonnes d’un tableau | un vecteur si possible |
lapply |
idem | toujours une liste |
apply |
matrice ou tableau | selon la dimension choisie |
tapply |
un vecteur, découpé par un facteur | une valeur par groupe |
apply n’est pas systématiquement plus rapide : sur un vecteur, sqrt(v) bat largement sapply(v, sqrt), parce que la vectorisation opère en une seule opération interne.
L’intérêt de sapply est la lisibilité — une ligne au lieu de cinq — et l’absence du piège de l’objet qui grandit. Gardez la boucle explicite quand chaque tour dépend du précédent : une simulation, une suite récurrente.
À vous
Écrivez une fonction note_mention qui prend une note et renvoie sa mention, puis appliquez-la à un vecteur de notes.
note_mention <- function(note) {
if (note >= 16) {
"tres bien"
} else if (note >= 14) {
"bien"
} else if (note >= 10) {
"passable"
} else {
"insuffisant"
}
}
sapply(notes, note_mention)sapply est indispensable ici : note_mention(notes) échouerait, parce que if n’accepte qu’une seule valeur. C’est précisément la situation où la vectorisation ne suffit plus.
Ce qu’il faut retenir
| Écriture | Effet |
|---|---|
if (cond) { } else { } |
exécuter selon une condition — } else { sur la même ligne |
ifelse(cond, oui, non) |
la version vectorisée, pour un vecteur entier |
for (i in 1:n) { } |
répéter un nombre connu de fois |
seq_along(v) |
plus sûr que 1:length(v) |
while (cond) { } |
répéter tant que la condition tient |
resultat <- numeric(n) |
préparer le contenant avant la boucle |
function(x, y = 2) { } |
écrire sa propre fonction |
sapply, lapply |
appliquer une fonction à chaque élément |
apply(m, 1, f) / apply(m, 2, f) |
par ligne / par colonne |
La première partie du cours s’achève ici : vous connaissez le langage. La seconde partie l’emploie à ce pour quoi il a été conçu, en commençant par les fonctions statistiques.