Un vector (techniquement un atomic vector) en R est une collection de valeurs de même type : le plus souvent integer, numeric, character, logical ; plus rarement complex ou raw.
Ils sont stockés de façon contiguë en mémoire, un peu comme un tableau en C.
Si on lui donne des entrées de types mélangés, R les convertira silencieusement vers un type commun, le plus souvent character, ce qui peut donner des résultats inattendus.
v <- c(2, 5.4, "txt")
v
#> [1] "2" "5.4" "txt"
typeof(v)
#> [1] "character"
Si tu as déjà toutes les valeurs, utilise c() (abrégé de « concatenate ») :
x <- 10
v <- c(4, 7, x)
La fonction c() est très souple, et ses entrées peuvent contenir d'autres vecteurs ou des intervalles.
c() accepte n'importe quel nombre et n'importe quelle variété d'entrées, et les aplatit en un seul vecteur.
Si le vecteur part de peu et grossit beaucoup pendant l'exécution (par exemple si on ajoute des valeurs dans une boucle), il peut être nettement plus rapide de préallouer un long vecteur qu'on remplira ensuite avec les vraies données :
v <- vector(length = 100)
Cela réduit au minimum le besoin de copier l'objet : une opération lente et gourmande en mémoire pour les longs vecteurs.
Pour un intervalle de valeurs consécutives (croissantes ou décroissantes), on peut utiliser la notation :
v <- 1:5 # equivalent to c(1, 2, 3, 4, 5)
w <- 10:7 # c(10, 9, 8, 7)
En R, les intervalles ne sont pas un type à part : ce n'est qu'une façon abrégée de construire un vecteur.
La fonction seq() offre un contrôle plus fin :
# specify step size
seq(2, 11, by = 3) # c(2, 5, 8, 11)
# specify number of values, evenly-spaced
seq(2.5, 4, length.out = 5) # c(2.500, 2.875, 3.250, 3.625, 4.000)
La fonction rep() permet de créer facilement des motifs répétitifs :
count <- 3
rep(7, count)
#> [1] 7 7 7
rep(1:3, count)
#> [1] 1 2 3 1 2 3 1 2 3
rep(1:3, each = 2) # the "each" keyword is required in this case
#> [1] 1 1 2 2 3 3
Quelques intervalles sont prédéfinis sous forme de constantes intégrées :
letters # LETTERS is the UPPERCASE equivalent
#> [1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s" "t" "u" "v" "w" "x" "y" "z"
month.name
#> [1] "January" "February" "March" "April" "May" "June" "July" "August"
#> [9] "September" "October" "November" "December"
month.abb
#> [1] "Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug" "Sep" "Oct" "Nov" "Dec"
Les opérations s'appliquent automatiquement élément par élément sur le vecteur. Les boucles, les compréhensions de listes et les récursions courantes dans d'autres langages ne sont pas nécessaires et, pour des raisons de performance, il vaut mieux les éviter quand c'est possible.
2 + 1:3 # c(3, 4, 5)
2 * 1:3 # c(2, 4, 6)
12 / 1:3 # c(12 6 4)
2 ^ (1:3) # c(2, 4, 8) : exponentiation, operator precedence order here needs ()
Les paires de vecteurs fonctionnent aussi, les éléments étant traités deux à deux :
v <- 1:3
w <- 5:7
v + w
#> [1] 6 8 10
v * w
#> [1] 5 12 21
v ^ w
#> [1] 1 64 2187
C'est plus puissant qu'il n'y paraît au premier abord, et il y aura d'autres choses à explorer dans les concepts suivants.
Comme dans beaucoup de langages, on peut utiliser l'indice entre crochets :
v <- 4:7
v[2]
#> [1] 5
v[2:4]
#> [1] 5 6 7
Mais attention : R utilise par défaut une indexation qui commence à 1. C'est courant dans les langages scientifiques (Fortran, Matlab, Julia), mais cela diffère de l'indexation à partir de zéro du C et de ses nombreux successeurs.
On peut utiliser des intervalles, comme dans l'exemple ci-dessus, mais aussi dans la fonction c(), très souple :
v <- c(4, 7, 10)
w <- c(2, v[2:3], 11) # c(2, 7, 10, 11)
Les vectors (comme la plupart des choses en R) peuvent être traités comme modifiables, avec une syntaxe tout aussi souple pour écrire dans un seul élément ou dans plusieurs :
v <- 1:10
v[2] <- 42
v[4:6] <- c(11, 12, 13)
v
#> [1] 1 42 3 11 12 13 7 8 9 10
Les programmeurs habitués à d'autres langages pourraient croire que v[-1] permet d'accéder au dernier élément d'un vecteur.
Faux !
Pour cela, utilise tail().
v <- c(2, 4, 6) # defaults to last 6 entries
tail(v)
#> [1] 2 4 6
tail(v, n=1) # just the last entry
#> [1] 6
Les indices négatifs sont en fait un moyen de supprimer un élément :
v <- c(2, 4, 6)
v[-2]
#> [1] 2 6 # second element gone
Cela renvoie une copie plus courte du vecteur. v lui-même reste inchangé.
Appliquer des comparaisons à un vecteur renvoie un vecteur de booléens de même longueur.
C'est très pratique dans des fonctions comme 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
Pour des expressions logiques plus compliquées, il est important d'utiliser les opérateurs & et |, qui fonctionnent sur les vecteurs.
Les opérateurs à deux caractères && et || ne servent qu'à comparer des valeurs uniques, comme expliqué dans le [concept Booléens][concept-boolean].
v <- c(4, 7, 10)
v %% 2 == 0 & v > 5
#> [1] FALSE FALSE TRUE
C'est un sujet sur lequel on reviendra dans les prochains chapitres. Pour l'instant, remarque simplement le format de la sortie dans la console :
1:20
#> [1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
#> [18] 18 19 20
Les lignes commencent par des nombres entre [ ] pour indiquer l'indice du premier élément (utile pour les longs vecteurs qui s'étalent sur plusieurs lignes).
Mais pourquoi les valeurs uniques sont-elles précédées de [1] ?
Il n'existe pas vraiment de valeurs scalaires en R !
Ce qui ressemble à un nombre ou à une lettre unique est en réalité un vecteur de longueur 1.
On a déjà eu un aperçu de cela dans les concepts Bases et Booléens, avec les types numériques et logiques.
Les vecteurs décrits ci-dessus sont plus formellement appelés atomic vectors. C'est parce qu'ils ne peuvent contenir que des valeurs simples et indivisibles (d'où le terme « atomiques ») comme numeric, character, boolean. Des structures plus complexes peuvent former des recursive vectors, qui seront présentés quand on abordera les lists.