Dans le concept Vectors, on a vu qu'un vecteur peut être utilisé dans une expression conditionnelle, ce qui produit un vecteur logique.
Celui-ci peut ensuite servir dans des fonctions telles que all() et any().
v <- c(4, 7, 10)
v >= 6
#> [1] FALSE TRUE TRUE
all(v > 6)
#> [1] FALSE # not all elements match this condition
any(v > 6)
#> [1] TRUE # at least one element matches
Cette technique est bien plus puissante que cela.
On peut extraire certains éléments d'un vecteur à l'aide d'un indice ou d'un vecteur d'indices :
v <- 5:10
v
#> [1] 5 6 7 8 9 10
v[3]
#> [1] 7
v[c(2, 4)]
#> [1] 6 8
Autrement, on peut utiliser un vecteur logique pour filtrer le vecteur d'origine, ce qui renvoie un sous-ensemble des entrées associées à une valeur TRUE :
v <- 1:3
bools <- c(FALSE, TRUE, TRUE)
v[bools]
#> [1] 2 3
Il n'y a qu'un pas de là à générer le vecteur booléen avec une expression conditionnelle :
bools <- v >= 2
bools
#> [1] FALSE TRUE TRUE
v[bools]
#> [1] 2 3
# simpler form
v[v >= 2]
#> [1] 2 3
Ces expressions conditionnelles peuvent être aussi complexes que l'on veut, et faire intervenir plusieurs vecteurs ou fonctions :
v <- 1:10
w <- 10:1
# pairwise comparison
v > w
#> [1] FALSE FALSE FALSE FALSE FALSE TRUE TRUE TRUE TRUE TRUE
# shorter form
v[v > w]
#> [1] 6 7 8 9 10
Nous avons appelé ce concept « Filtrage de vecteurs », en espérant qu'il parle au plus grand nombre de programmeurs.
Au sein de la communauté R, le terme « subsetting » est probablement plus courant.
Par souci de simplicité, tous les exemples ci-dessus comparaient des vecteurs de même longueur. Que se passe-t-il quand les longueurs ne correspondent pas ?
La réponse est un mécanisme puissant lorsqu'on l'emploie à dessein, mais déroutant lorsqu'il est inattendu.
Un exemple :
v <- 1:6
cond <- c(TRUE, FALSE)
v[cond]
#> [1] 1 3 5
Que s'est-il passé ? On a indexé un vecteur de longueur 6 avec un booléen de longueur 2, mais on a obtenu un vecteur de longueur 3.
Si un vecteur est en quelque sorte « trop court » pour l'expression dans laquelle il est utilisé, R le répète jusqu'à atteindre la longueur voulue : c'est ce qu'on appelle le « recyclage ».
Notre vecteur c(TRUE, FALSE) devient ainsi c(TRUE, FALSE, TRUE, FALSE, TRUE, FALSE), et v[cond] renvoie alors les valeurs TRUE de v, ici les nombres impairs.
Alors, que se passe-t-il dans l'exemple suivant ?
v <- 1:3
0.5 * v
#> [1] 0.5 1.0 1.5
2 + v
#> [1] 3 4 5
À première vue, cela ressemble à une multiplication par un scalaire et à une addition par un scalaire (un peu étrange), mais les scalaires n'existent pas en R.
À la place, les nombres sont recyclés pour former un vecteur de la longueur de v, puis l'opération arithmétique est appliquée élément par élément avec c(0.5, 0.5, 0.5) ou c(2, 2, 2).
Le recyclage est extrêmement utile, mais assure-toi que c'est bien ce que tu veux.
Un léger décalage entre les longueurs de vecteurs (par exemple 99 contre 100) peut provoquer des bugs déroutants.
Certains auteurs recommandent de ne l'utiliser que lorsqu'un des vecteurs a une longueur de 1.
La fonction which() prend un argument booléen et renvoie un vecteur d'indices qui valent TRUE.
v
#> [1] 2 7 9
v[v > 5] # returns values
#> [1] 7 9
which(v > 5) # returns indices
#> [1] 2 3
Les sections précédentes portaient sur la recherche d'entrées correspondant à un certain critère.
Si tu veux simplement savoir si le vecteur contient un élément précis, utilise l'opérateur %in%.
3 %in% 1:5
#> [1] TRUE
7 %in% 1:5
#> [1] FALSE
C'est fonctionnellement équivalent au code suivant, mais bien plus pratique :
length(v[v == 3]) > 0
#> [1] TRUE
Il existe aussi un opérateur %notin%, qui donne le not logique de %in%.