Une version actualisée de ce chapitre est disponible sur guide-R : Vecteurs

Ce chapitre est évoqué dans le webin-R #02 (les bases du langage R) sur YouTube.

Nous allons reprendre plusieurs éléments de base du langage R que nous avons déjà abordés mais de manière plus formelle. Une bonne compréhension des bases du langage, bien qu’un peu ardue de prime abord, permet de comprendre le sens des commandes qu’on utilise et de pleinement exploiter la puissance que R offre en matière de manipulation de données.

Dans ce chapitre, nous reviendrons sur les vecteurs, tandis que les listes et les tableaux de données seront abordés dans un chapitre dédié.

Présentation des vecteurs

Les vecteurs sont l’un des objets de base de R et correspondent à une liste de valeurs. Leurs propriétés fondamentales sont :

  • les vecteurs sont unidimensionnels (i.e. ce sont des objets à une seule dimension, à la différence d’une matrice par exemple) ;
  • toutes les valeurs d’un vecteur sont d’un seul et même type ;
  • les vecteurs ont une longueur qui correspond au nombre de valeurs contenues dans le vecteur.

Les principaux types de vecteurs

Dans R, il existe quatre types fondamentaux de vecteurs :

  • les nombres réels (c’est-à-dire les nombres décimaux que nous utilisons au quotidien),
  • les nombres entiers,
  • les chaînes de caractères (qui correspondent à du texte) et
  • les valeurs logiques ou valeurs booléennes, à savoir vrai ou faux.

Pour connaître la nature d’un objet, le plus simple est d’utiliser la fonction class. Par exemple :

class(12.5)
[1] "numeric"

La réponse "numeric" nous indique qu’il s’agit d’un nombre réel. Parfois, vous pourrez rencontrer le terme "double" qui désigne également les nombres réels. Notez que R étant anglophone, la décimale est indiquée avec un point (.) et non avec une virgule comme c’est l’usage en français.

Essayons avec un nombre entier :

class(3)
[1] "numeric"

Sous R, lorsqu’on tape un nombre sans autre précision, il est considéré par défaut comme un nombre réel. Pour indiquer spécifiquement qu’on veut un nombre entier, il faut rajouter le suffixe L :

class(3L)
[1] "integer"

Au quotidien, il arrive rarement d’avoir à utiliser ce suffixe, mais il est toujours bon de le connaître au cas où vous le rencontriez dans des manuels ou des exemples de code.

Pour saisir une chaîne de caractères, on aura recours aux doubles guillemets droits (") :

class("abc")
[1] "character"

Il est également possible d’utiliser des guillemets simples ('), dès lors qu’on utilise bien le même type de guillemets pour indiquer le début et la fin de la chaîne de caractères (par exemple 'abc').

Enfin, les valeurs logiques s’indiquent avec TRUE pour vrai et FALSE pour faux. Il est aussi possible d’utiliser les raccourcis T et F. Attention à bien utiliser les majuscules, R étant sensible à la casse.

class(TRUE)
[1] "logical"

En résumé, les classes R des quatre types fondamentaux de vecteur sont :

Exemple Classe R Type
5L integer nombre entier
3.14 numeric nombre réel
"abcd" character chaîne de caractères
TRUE logical booléenne

En plus des types de base, il existe de nombreuses autres classes de vecteurs dans R que nous aborderons ultérieurement dans d’autres chapitres. Les plus courantes sont :

Classe R Type
factor facteur
labelled vecteur labellisé
Date date
POSIXct date et heure

Création

La fonction c

Pour créer un vecteur, on utilisera la fonction c, la lettre c étant un raccourci du mot anglais combine puisque cette fonction permet de combiner des valeurs individuelles dans un vecteur unique. Il suffit de lui passer la liste des valeurs à combiner :

taille <- c(1.88, 1.65, 1.92, 1.76)
taille
[1] 1.88 1.65 1.92 1.76
class(taille)
[1] "numeric"
sexe <- c("h", "f", "h", "f")
sexe
[1] "h" "f" "h" "f"
class(sexe)
[1] "character"
urbain <- c(TRUE, TRUE, FALSE, FALSE)
urbain
[1]  TRUE  TRUE FALSE FALSE
class(urbain)
[1] "logical"

Nous l’avons vu, toutes les valeurs d’un vecteur doivent obligatoirement être du même type. Dès lors, si on essaie de combiner des valeurs de différents types, R essaiera de les convertir au mieux. Par exemple :

x <- c(2L, 3.14, "a")
x
[1] "2"    "3.14" "a"   
class(x)
[1] "character"

Dans le cas présent, toutes les valeurs ont été converties en chaînes de caractères.

La fonction rep

Dans certaines situations, on peut avoir besoin de créer un vecteur d’une certaine longueur mais dont toutes les valeurs sont identiques. Cela se réalise facilement avec rep à qui on indiquera la valeur à répéter puis le nombre de répétitions :

rep(2, 10)
 [1] 2 2 2 2 2 2 2 2 2 2

On peut aussi lui indiquer plusieurs valeurs qui seront alors répétées en boucle :

rep(c("a", "b"), 3)
[1] "a" "b" "a" "b" "a" "b"

La fonction seq

Dans d’autres situations, on peut avoir besoin de créer un vecteur contenant une suite de valeurs, ce qui se réalise aisément avec seq à qui on précisera les arguments from (point de départ), to (point d’arrivée) et by (pas). Quelques exemples valent mieux qu’un long discours :

seq(1, 10)
 [1]  1  2  3  4  5  6  7  8  9 10
seq(5, 17, by = 2)
[1]  5  7  9 11 13 15 17
seq(10, 0)
 [1] 10  9  8  7  6  5  4  3  2  1  0
seq(100, 10, by = -10)
 [1] 100  90  80  70  60  50  40  30  20  10
seq(1.23, 5.67, by = 0.33)
 [1] 1.23 1.56 1.89 2.22 2.55 2.88 3.21 3.54 3.87 4.20 4.53
[12] 4.86 5.19 5.52

L’opérateur :

L’opérateur : est un raccourci de la fonction seq pour créer une suite de nombres entiers. Il s’utilise ainsi :

1:5
[1] 1 2 3 4 5
24:32
[1] 24 25 26 27 28 29 30 31 32
55:43
 [1] 55 54 53 52 51 50 49 48 47 46 45 44 43

Nous verrons un peu plus loin que ce raccourci est fort pratique.

Longueur d’un vecteur

Un vecteur dispose donc d’une longueur qui correspond au nombre de valeurs qui le composent. Elle s’obtient avec length :

length(taille)
[1] 4
length(c("a", "b"))
[1] 2

Il est possible de faire un vecteur de longueur nulle avec c(). Bien évidemment sa longueur est zéro.

length(c())
[1] 0

Quelques vecteurs remarquables

R fournit quelques vecteurs particuliers qui sont directement accessibles :

  • LETTERS : les 26 lettres de l’alphabet en majuscules
  • letters : les 26 lettres de l’alphabet en minuscules
  • month.name : les noms des 12 mois de l’année en anglais
  • month.abb : la version abrégée des 12 mois en anglais
  • pi : la constante mathématique π
LETTERS
 [1] "A" "B" "C" "D" "E" "F" "G" "H" "I" "J" "K" "L" "M" "N"
[15] "O" "P" "Q" "R" "S" "T" "U" "V" "W" "X" "Y" "Z"
letters
 [1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n"
[15] "o" "p" "q" "r" "s" "t" "u" "v" "w" "x" "y" "z"
length(letters)
[1] 26
month.name
 [1] "January"   "February"  "March"     "April"    
 [5] "May"       "June"      "July"      "August"   
 [9] "September" "October"   "November"  "December" 
month.abb
 [1] "Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug" "Sep"
[10] "Oct" "Nov" "Dec"
length(month.abb)
[1] 12
pi
[1] 3.141593
length(pi)
[1] 1

Combiner des vecteurs

Pour combiner des vecteurs, rien de plus simple. Il suffit d’utiliser c ! Les valeurs des différents vecteurs seront mises bout à bout pour créer un unique vecteur.

x <- c(2, 1, 3, 4)
length(x)
[1] 4
y <- c(9, 1, 2, 6, 3, 0)
length(y)
[1] 6
z <- c(x, y)
z
 [1] 2 1 3 4 9 1 2 6 3 0
length(z)
[1] 10
min_maj <- c(letters, LETTERS)
min_maj
 [1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n"
[15] "o" "p" "q" "r" "s" "t" "u" "v" "w" "x" "y" "z" "A" "B"
[29] "C" "D" "E" "F" "G" "H" "I" "J" "K" "L" "M" "N" "O" "P"
[43] "Q" "R" "S" "T" "U" "V" "W" "X" "Y" "Z"
length(min_maj)
[1] 52

Valeurs manquantes

Lorsqu’on travaille avec des données d’enquête, il est fréquent que certaines données soient manquantes, en raison d’un refus du participant de répondre à une question donnée ou d’un oubli ou d’un dysfonctionnement du matériel de mesure, etc.

Une valeur manquante s’indique sous R avec NA (pour not available). Cette valeur peut s’appliquer à n’importe quel type de vecteur, qu’il soit numérique, textuel ou logique.

taille <- c(1.88, NA, 1.65, 1.92, 1.76, NA)
sexe <- c("h", "f", NA, "h", NA, "f")

Les valeurs manquantes sont prises en compte dans le calcul de la longueur du vecteur.

length(taille)
[1] 6

Il ne faut pas confondre NA avec un autre objet qu’on rencontre sous R qui s’appelle NULL et qui représente l’objet vide. NULL ne contient absolument rien. La différence se comprend mieux lorsqu’on essaie de combiner ces objets :

c(NULL, NULL, NULL)
NULL
length(c(NULL, NULL, NULL))
[1] 0

On peut combiner NULL avec NULL, du vide plus du vide renverra toujours du vide dont la dimension est égale à zéro.

c(NA, NA, NA)
[1] NA NA NA
length(c(NA, NA, NA))
[1] 3

Par contre, un vecteur composé de trois valeurs manquantes a une longueur de 3, même si toutes ses valeurs sont manquantes.

Indexation par position

L’indexation est l’une des fonctionnalités les plus puissantes mais aussi les plus difficiles à maîtriser de R. Il s’agit d’opérations permettant de sélectionner des sous-ensembles de valeurs en fonction de différents critères. Il existe trois types d’indexation : (i) l’indexation par position, (ii) l’indexation par nom et (iii) l’indexation par condition. Le principe est toujours le même : on indique entre crochets ([]) ce qu’on souhaite garder ou non.

Pour rappel, les crochets s’obtiennent sur un clavier français de type PC en appuyant sur la touche Alt Gr et la touche ( ou ).

Commençons par l’indexation par position encore appelée indexation directe. Ce mode le plus simple d’indexation consiste à indiquer la position des éléments à conserver.

Reprenons notre vecteur taille :

taille
[1] 1.88   NA 1.65 1.92 1.76   NA

Si on souhaite le premier élément du vecteur, on peut faire :

taille[1]
[1] 1.88

Si on souhaite les trois premiers éléments ou les éléments 2, 5 et 6 :

taille[1:3]
[1] 1.88   NA 1.65
taille[c(2, 5, 6)]
[1]   NA 1.76   NA

Si on veut le dernier élément :

taille[length(taille)]
[1] NA

Il est tout à fait possible de sélectionner les valeurs dans le désordre :

taille[c(5, 1, 4, 3)]
[1] 1.76 1.88 1.92 1.65

Dans le cadre de l’indexation par position, il est également possible de spécifier des nombres négatifs, auquel cas cela signifiera toutes les valeurs sauf celles-là. Par exemple :

taille[c(-1, -5)]
[1]   NA 1.65 1.92   NA

À noter, si on indique une position au-delà de la longueur du vecteur, R renverra NA. Par exemple :

taille[23:25]
[1] NA NA NA

Des vecteurs nommés

Les différentes valeurs d’un vecteur peuvent être nommées. Une première manière de nommer les éléments d’un vecteur est de le faire à sa création :

sexe <- c(Michel = "h", Anne = "f", Dominique = NA, Jean = "h", Claude = NA, Marie = "f")

Lorsqu’on affiche le vecteur, la présentation change quelque peu.

sexe
   Michel      Anne Dominique      Jean    Claude     Marie 
      "h"       "f"        NA       "h"        NA       "f" 

La liste des noms s’obtient avec names.

names(sexe)
[1] "Michel"    "Anne"      "Dominique" "Jean"     
[5] "Claude"    "Marie"    

Pour ajouter ou modifier les noms d’un vecteur, on doit attribuer un nouveau vecteur de noms :

names(sexe) <- c("Michael", "Anna", "Dom", "John", "Alex", "Mary")
sexe
Michael    Anna     Dom    John    Alex    Mary 
    "h"     "f"      NA     "h"      NA     "f" 

Pour supprimer tous les noms, il y a la fonction unname :

anonyme <- unname(sexe)
anonyme
[1] "h" "f" NA  "h" NA  "f"

Indexation par nom

Lorsqu’un vecteur est nommé, il est dès lors possible d’accéder à ses valeurs à partir de leur nom. Il s’agit de l’indexation par nom.

sexe["Anna"]
Anna 
 "f" 
sexe[c("Mary", "Michael", "John")]
   Mary Michael    John 
    "f"     "h"     "h" 

Par contre il n’est pas possible d’utiliser l’opérateur - comme pour l’indexation directe. Pour exclure un élément en fonction de son nom, on doit utiliser une autre forme d’indexation, l’indexation par condition, expliquée dans la section suivante. On peut ainsi faire…

sexe[names(sexe) != "Dom"]

… pour sélectionner tous les éléments sauf celui qui s’appelle Dom.

Indexation par condition

L’indexation par condition consiste à fournir un vecteur logique indiquant si chaque élément doit être inclus (si TRUE) ou exclu (si FALSE). Par exemple :

sexe
Michael    Anna     Dom    John    Alex    Mary 
    "h"     "f"      NA     "h"      NA     "f" 
sexe[c(TRUE, FALSE, FALSE, TRUE, FALSE, FALSE)]
Michael    John 
    "h"     "h" 

Écrire manuellement une telle condition n’est pas très pratique à l’usage. Mais supposons que nous ayons également à notre disposition les deux vecteurs suivants, également de longueur 6.

urbain <- c(TRUE, FALSE, FALSE, FALSE, TRUE, TRUE)
poids <- c(80, 63, 75, 87, 82, 67)

Le vecteur urbain est un vecteur logique. On peut directement l’utiliser pour avoir le sexe des enquêtés habitant en milieu urbain :

sexe[urbain]
Michael    Alex    Mary 
    "h"      NA     "f" 

Supposons qu’on souhaite maintenant avoir la taille des individus pesant 80 kilogrammes ou plus. Nous pouvons effectuer une comparaison à l’aide des opérateurs de comparaison suivants :

Opérateur de comparaison Signification
== égal à
!= différent de
> strictement supérieur à
< strictement inférieur à
>= supérieur ou égal à
<= inférieur ou égal à

Voyons tout de suite un exemple :

poids >= 80
[1]  TRUE FALSE FALSE  TRUE  TRUE FALSE

Que s’est-il passé ? Nous avons fourni à R une condition et il nous a renvoyé un vecteur logique avec autant d’éléments qu’il y a d’observations et dont la valeur est TRUE si la condition est remplie et FALSE dans les autres cas. Nous pouvons alors utiliser ce vecteur logique pour obtenir la taille des participants pesant 80 kilogrammes ou plus :

taille[poids >= 80]
[1] 1.88 1.92 1.76

On peut combiner ou modifier des conditions à l’aide des opérateurs logiques habituels :

Opérateur logique Signification
& et logique
| ou logique
! négation logique

Comment les utilise-t-on ? Voyons tout de suite un exemple. Supposons que je veuille identifier les personnes pesant 80 kilogrammes ou plus et vivant en milieu urbain :

poids >= 80 & urbain
[1]  TRUE FALSE FALSE FALSE  TRUE FALSE

Les résultats sont différents si je souhaite isoler les personnes pesant 80 kilogrammes ou plus ou vivant milieu urbain :

poids >= 80 | urbain
[1]  TRUE FALSE FALSE  TRUE  TRUE  TRUE

Une remarque importante : quand l’un des termes d’une condition comporte une valeur manquante (NA), le résultat de cette condition n’est pas toujours TRUE ou FALSE, il peut aussi être à son tour une valeur manquante.

taille
[1] 1.88   NA 1.65 1.92 1.76   NA
taille > 1.8
[1]  TRUE    NA FALSE  TRUE FALSE    NA

On voit que le test NA > 1.8 ne renvoie ni vrai ni faux, mais NA.

Une autre conséquence importante de ce comportement est qu’on ne peut pas utiliser l’opérateur l’expression == NA pour tester la présence de valeurs manquantes. On utilisera à la place la fonction ad hoc is.na :

is.na(taille > 1.8)
[1] FALSE  TRUE FALSE FALSE FALSE  TRUE

Pour compliquer encore un peu le tout, lorsqu’on utilise une condition pour l’indexation, si la condition renvoie NA, R ne sélectionne pas l’élément mais retourne quand même la valeur NA. Ceci a donc des conséquences sur le résultat d’une indexation par comparaison.

Par exemple si je cherche à connaître le poids des personnes mesurant 1,80 mètre ou plus :

taille
[1] 1.88   NA 1.65 1.92 1.76   NA
poids
[1] 80 63 75 87 82 67
poids[taille > 1.8]
[1] 80 NA 87 NA

Les éléments pour lesquels la taille n’est pas connue ont été transformés en NA, ce qui n’influera pas le calcul d’une moyenne. Par contre, lorsqu’on utilisera assignation et indexation ensemble, cela peut créer des problèmes. Il est donc préférable lorsqu’on a des valeurs manquantes de les exclure ainsi :

poids[taille > 1.8 & !is.na(taille)]
[1] 80 87

Pour plus de détails sur les conditions et le calcul logique dans R, on pourra se référer au chapitre dédié.

Assignation par indexation

Dans tous les exemples précédents, on a utilisé l’indexation pour extraire une partie d’un vecteur, en plaçant l’opération d’indexation à droite de l’opérateur <-.

Mais l’indexation peut également être placée à gauche de cet opérateur d’assignation. Dans ce cas, les éléments sélectionnés par l’indexation sont alors remplacés par les valeurs indiquées à droite de l’opérateur <-.

Prenons donc un exemple simple :

v <- 1:5
v
[1] 1 2 3 4 5
v[1] <- 3
v
[1] 3 2 3 4 5

Cette fois, au lieu d’utiliser quelque chose comme x <- v[1], qui aurait placé la valeur du premier élément de v dans x, on a utilisé v[1] <- 3, ce qui a mis à jour le premier élément de v avec la valeur 3. Ceci fonctionne également pour les différents types d’indexation évoqués précédemment :

sexe["Alex"] <- "f"

Enfin on peut modifier plusieurs éléments d’un seul coup soit en fournissant un vecteur, soit en profitant du mécanisme de recyclage. Les deux commandes suivantes sont ainsi rigoureusement équivalentes :

sexe[c(1, 3, 4)] <- c("Homme", "Homme", "Homme")
sexe[c(1, 3, 4)] <- "Homme"

L’assignation par indexation peut aussi être utilisée pour ajouter une ou plusieurs valeurs à un vecteur :

length(sexe)
[1] 6
sexe[7] <- "f"
sexe
Michael    Anna     Dom    John    Alex    Mary         
"Homme"     "f" "Homme" "Homme"     "f"     "f"     "f" 
length(sexe)
[1] 7

On commence à voir comment l’utilisation de l’indexation par conditions et de l’assignation va nous permettre de faire des recodages (que nous aborderons plus en détail dans un chapitre dédié).

En résumé

  • Un vecteur est un objet unidimensionnel contenant une liste de valeurs qui sont toutes du même type (entières, numériques, textuelles ou logiques).
  • La fonction class permet de connaître le type du vecteur et la fonction length sa longueur, c’est-à-dire son nombre d’éléments.
  • La fonction c sert à créer et à combiner des vecteurs.
  • Les valeurs manquantes sont représentées avec NA.
  • Un vecteur peut être nommé, c’est-à-dire qu’un nom textuel a été associé à chaque élément. Cela peut se faire lors de sa création ou avec la fonction names.
  • L’indexation consiste à extraire certains éléments d’un vecteur. Pour cela, on indique ce qu’on souhaite extraire entre crochets ([]) juste après le nom du vecteur. Le type d’indexation dépend du type d’information transmise.
  • S’il s’agit de nombres entiers, c’est l’indexation par position : les nombres représentent la position dans le vecteur des éléments qu’on souhaite extraire. Un nombre négatif s’interprète comme tous les éléments sauf celui-là.
  • Si on indique des chaînes de caractères, c’est l’indexation par nom : on indique le nom des éléments qu’on souhaite extraire. Cette forme d’indexation ne fonctionne que si le vecteur est nommé.
  • Si on transmet des valeurs logiques, le plus souvent sous la forme d’une condition, c’est l’indexation par condition : TRUE indique les éléments à extraire et FALSE les éléments à exclure. Il faut être vigilant aux valeurs manquantes (NA) dans ce cas précis.
  • Enfin, il est possible de ne modifier que certains éléments d’un vecteur en ayant recours à la fois à l’indexation ([]) et à l’assignation (<-).