2.5 Opérations et manipulations de vecteurs
Dans ce chapitre nous allons allez plus loin avec les vecteurs. Nous allons voir comment les manipuler pour réaliser des opérations mathématiques avec les vecteurs de nombres, ainsi que comment selectionner une partie d'un vecteur, en modifer les valeurs, ajouter ou supprimer des éléments etc.
Opérations mathématiques entre vecteurs
Dans la premiére partie de ce chapitre, nous allons voir comment on peut appliquer les différentes opérations mathématiques suelles avec des vecteurs. Cela a un intêret surtout pour les vecteurs de nombres (integer
,double
et complex
), mais nous regarderons ce qui se passe également avec les types character
et complex
.
Opérations entre vecteurs de nombres
Dans le premier chapitre de ce cours, nous avons vu que R était capable de réaliser les opérations usuelles avec les nombres : addition, soustraction etc. Ces opérations fonctionnement également avec les vecteurs, comme nous allons le voir.
Des opérations membre à membre
Les opérations entre vecteurs dans R se font membre à membre. Regardons un exemple avec l'addition :
vect1 = c(1, 2, 3)
vect2 = c(2, -1, 1)
vect3 = vect1 + vect2
print(vect3) #3 1 4
Comme vous pouvez le constater, le 1er élément de vect1
est ajouté avec le premier élément de vect2
, le second élément de vect1
est ajouté au second élément de vect2
etc. C'est le même principe que pour l'addition de deux vecteurs en mathématiques classiques, les composantes s'ajoutent entre elles deux par deux.
Ce principe est valable pour toutes les opérations mathématiques classique : addition, soustraction, mais aussi multiplication, division, modulo etc. Toutes ces opérations se font membre à membre, comme on peut le voir dans les exemples suivant :
#soustraction
vect1 = c(1, 2, 3)
vect2 = c(2,-1,1)
vect3 = vect1 + vect2
print(vect3) -1 3 2
#multiplication
vect1 = c(1,2,3)
vect2 = c(2,-1,1)
vect3 = vect1 * vect2
print(vect3) #2 -2 3
#division
vect1 = c(1,2,3)
vect2 = c(2,-1,1)
vect3 = vect1 / vect2
print(vect3) #0.5 -2.0 3.0
#modulo (reste aprés la division entiére)
vect1 = c(1,2,3)
vect2 = c(2,-1,1)
vect3 = vect1 %% vect2
print(vect3) #1 0 0
#division avec arrondie à l'entier inférieur
vect1 = c(1,2,3)
vect2 = c(2,-1,1)
vect3 = vect1 %/% vect2
print(vect3) #0 -2 3
Il est important de bien retenir ce principe des opérations membre à membre pour les opérations sur les vecteurs. En particulier pour la multiplication, ce qui peu parfois surprendre car on pourrait s'attendre par exemple à ce que la multiplication de vecteurs corresponde au produit sclaire mathématique ou au produit vectoriel.
Opérations entre vecteurs de longueur différentes
Nous avons vu pour l'instant uniquement des opérations entre vecteurs de même longueur. Que ce passe-il si on veut réaliser une opération entre vecteurs qui n'ont pas le même nombre de composantes ?
Supposons que l'on souhaite ajouter vect1 = c(1,1,1,1)
qui est de longeur n = 4
avec un vecteur plus petit, vect2 = c(1,2)
donc la longueur est k = 2
. R va alors prendre le plus petit vecteur (ici celui de taille k=2
) et il va appliquer l'opération demandée membre à membre avec les k
premiers éléments du vecteur vect1
. Puis il recommance, avec les k
suivant éléments de vect1
, et ainsi de suite jusqu'à la fin des éléments de vect1
. Regardons un exemple :
vect1 = c(1,1,1,1)
vect2 = c(1,2)
vect3 = vect1 + vect2
print(vect3) #2 3 2 3
R ajoute le premier élément de vect2
avec le premier élément de vect1
, puis le second élément de vect2
avec le second de vect1
. Là, il se retrouve à la fin de vect2
, alors qu'il reste des éléments dans vect1
. R recommence alors à parcourir vect2
depuis le début. Il ajouter le premier élément de vect2
avec le troisième élément de vect1
, puis le second élément de vect2
avec le quatriéme élément de vect1
, et il s'arrête car il parcouru intégralement le plus long vecteur.
Un cas d'usage particulier d'opérateur sur deux vecteurs de longueur différente consiste à utiliser un vecteur de taille quelconque avec un nombre (soit un vecteur de dimension 1). Dans cet exemple, on multiplie toutes les composantes de notre vecteur par 2
:
vect1 = c(1,2,3,4)
vect2 = 2*vect1
print(vect2) #2 4 6 8
Chaque composante de vect1
est bien multiplié par 2.
Remarque : Si vous essayez de faire une opération entre deux vecteurs de longueurs différentes, mais dont la longueur du plus grand n'est pas un multiple entier de celle du plus petit (par exemple un vecteur de longeur 3 ajouté un vecteur de longueur 2), ce principe s'appliquera. Simplement R studio vous affichera un avertissement indiquant que vos vecteurs ont des tailles non multiplies. R parcourera toujours le les plus grand vecteur en lui appliquant l'opération demandé élément par élément avec ceux du plus petit, et il reprendra le petit vecteur à son début à chaque fois que cela est nécessaire pour continuer.
Calculs avec les valeurs manquantes et les infinis/NaN
Nous avons vu dans le premier chapitre que R était capable de gérer les cas d'indétermination lors des calculs, ainsi que de donner la valeur NaN
en cas d'indétermination. Cela fonctionne toujours parfaitement avec les vecteurs :
vect1 = c(10, NA, Inf)
vect2 = c(0, 10, -Inf)
vect3 = vect1 / vect2
print(vect3) Inf, NA, NaN
Comme vous pouvez le voir, 10/0
donne bien Inf
, toute opération avec NA
produit un NA
(exactement comme la régle de propagation des NaN
), et Inf / -Inf
donne bien une indétermination représentée par NaN
.
Gardez bien en tête que tous calcul impliquant NA
donnera toujours NA
, exactement comme tout calcul impliquant NaN
donnera toujours NaN
. La seule expection est un calcul implique NA
et NaN
en même temps. Alors NA
l'emportera comme vous pouvez le voir :
NA + NaN #donne NA
Un peu d'explications sur le comportement des NA
Prennons le temps de comprendre pourquoi cela fonctionne ainsi. Quel pourrait bien être le résultat d'un calcul impliquant une observation manquante, qui n'a pas de valeur ? Supposons que l'on souhaite calculer combien des individus payent d'impôt sur le revenu, sachant que le taux d'imposition est le même pour tous, de 20%
. On dispose de 4 individus, mais l'un d'entre eux à mal rempli sa déclaration, et la valeur est manquante. Calculons leurs impôts :
revenus = c(12000, 40000,27000, NA) #le 4éme individu n'a pas rendu sa déclaration
impots = 0.20 * revenus
print(impots)# 2400 8000 5400 NA
Pour le dernier individu, on obtient une valeur NA
. On pourrait trouver cela étrange, et s'attendre à avoir NaN
à la place, qu'en pensez vous ? Aprés tout, la valeur des revenus de cet individu existe, et est un nombre inconnu. 20% de ce nombre peut être considéré comme un nombre indéterminé, ce qu'est exactement ce que représente NaN
. Il serait donc tout aussi logique qu'un calcul impliquant un NA
de type integer
, double
ou complex
donne NaN
au lieu de NA
.
R est un langage pour les statistiques. En décidant d'afficher NA
comme résultat de tout calcul impliquant NA
, cela nous permet de toujours savoir que le probléme en question vient d'une observation manquante, ce qui est important pour l'utilisateur.
Un exemple éclairant
Supposons maintenant que nos 4 individus sont morts dans un accident d'avion, et que l'on souhaite savoir quel combien vont hériter chacun de leurs enfants. On dispose pour cela nombre d'enfants de chaque individu, et on considére que seul le revenu compte, qu'il n'y à pas patrimoine à prendre en compte.
revenus = c(12000, 40000,27000, NA) #le 4éme individu n'a pas rendu sa déclaration
nb_enfants = c(0,2,1,0)
heritage = revenus / nb_enfants
#print(heritage) #Inf 20000 27000 NA
On remarque que le premier individu et le dernier individu n'ont pas d'enfants. On ne connait pas le revenu du dernier individu, mais son nombre d'enfant est bien connu. Quand on regarde le résultat de heritage
on peut voir une différence entre le premier individu et le dernier. Dans le premier cas, on obtient une valeur Inf
, qui indique une valeur indéterminée, car on à tenté de diviser par 0
. La seule interprétation est que notre individu n'a pas d'enfants.
Pour le quatriéme individu, on obtient NA
. Cela signifie que l'une deux des variables (revenu ou nombre d'enfant) est manquante, et qu'on ne peut donc pas déterminer l'héritage de chaque enfant. Ce NA
nous donne une autre information que NaN
, il nous indique que nous n'avons pas toutes les données sur cet individu, alors que NaN
indique que nous avons toutes les données, mais que vous réalisons un calcul qui n'a pas de sens mathématiquement parlant.
Pour cette raison, un calcul impliquant NA
et NaN
donnera toujours NA
: il est important de signaler à l'utilisateur qu'il manque une observation pour réaliser le calcul en cours, même si avec cette observation le résultat aurait été indéterminé. Du point de vue du statisticien, savoir qu'une donnée manque quelque part est le plus important.
Opérations entre vecteurs non numériques
Nous avons vu les opérations entre vecteurs de nombres, soit des vecteurs de type integer
,double
ou complex
. Regardons ce qui se passe quand on essaye de réaliser ces opérations pour les types character
et logical
.
Opérations entres chaines de caractéres
Les opérations mathématiques entre chaines de caractéres sont tous simples impossibles en R. Si d'autres langages permettent d'ajouter deux chaines entre elles à l'aide du signe +
, R lui ne le permet pas. Toute opération mathématique classique impliquant un objet de type character
entrainera une erreur.
prenom = "George"
nom = "Washington"
nom_complet = prenom + nom
#Error in prenom + nom : non-numeric argument to binary operator
R refuse l'opération, en nous indiquant dans son message d'erreur que l'on utilise des objets qui ne sont pas des nombres pour l'opérateur d'addition.
Remarque : Il existe heureusement d'autres façon de manipuler les chaines de caractéres. R fourni de nombreuses fonctions pour cela, et certains packages sont conçus spécialement pour faciliter le travail sur les chaines. Nous revienderons sur les chaines dans la partie avancée de ce cours.
Opérations entre logical
Les opérations mathématiques entre vecteurs de type logical
sont possibles, mais elles sont un peu particulères. R va en effet convertir chaque vecteur logical
en vecteur de double
avec as.double()
, puis effecter l'opération mathématique classique sur les vecteurs de nombres obtenus.
Regardons ce code :
vect1 = c(TRUE, FALSE, TRUE)
vect2 = c(TRUE, TRUE)
vect3 = vect1 + vect2
print(vect3) #2 1 2
Le vecteur vect1
est en réalité converti pendant l'opération en c(1,0,1)
et vect2
en c(1,1)
. La somme de ces deux vecteurs donne bien c(2,1,2)
. Du fait de ce comportement, il est en pratique peu pertinant d'essayer de faire des opérations mathématiques sur des vecteurs de type logical
car il est rare que ce genre de résultats aient du sens.
Opérations entre vecteurs de différents types
Nous avons vu pour l'instant uniquement des opérations entre vecteurs de même type. Que ce passe-il si on essaye de réaliser des opérations entre vecteurs de types différents ? Les choses sont plutôt simples :
- Si l'un des vecteurs est de type
character
alors l'opération est impossible car nous avons vu que toute opération entre vecteurs de ce type donne une erreur. Il n'est donc pas plus possible de réaliser des opérations entre uncharacter
et un autre type de vecteurs. - Si l'opération implique un vecteur de
logical
(et pas chaines bien entendu) celui ci sera automatiquement transformé en vecteurs dedouble
, exactement comme quand on réalise des opérations entreslogicial
. La suite de l'opération se déroulera alors normalement, comme entre vecteurs de nombres.
De façon général, il n'y a pas beaucoup de sens à essayer d'ajouter entre eux des vecteurs de types différents, cela revient à essayer d'ajouter des pommes avec carottes.
#Ce code fonctionne, mais a-il vraiment beaucoup de sens ?
vect1 = (2,4)
vect2 = (TRUE, FALSE)
vect3 = vect1 + vect2
print(vect3) # 2 1 2
Manipuler les éléments d'un vecteur
Nous vennons de voir comment réaliser des opérations mathématiques entre vecteurs, ce qui a un intêret principalement pour les vecteurs numériques. Nous allons voir maintenant comment manipuler directement les éléments d'un vecteur : selectionner certains éléments, les modifier, ajouter des éléments, combiner des vecteurs ensembles, etc.
Extraire des éléments d'un vecteur
Commencons par voir comment selectionner un ou plusieurs éléments d'un vecteur.
Extraire un seul élément
Les éléments d'un vecteur sont numérotés de 1
jusqu' à n
, où n
est la longeur du vecteur. Pour obtenir l'élément numéro i
d'un vecteur, il suffit d'écrire mon_vecteur[i]
. Regardons le code suivant :
vect1 = c(1,2,3)
print(vect1[2] #on affiche le second élément
premier_element = vect1[1] #on affecte le premier élément à notre variable premier_element
presidents = c("Washington", "Adams", "Jefferson")
second_president = presidents[2] #on affecte "Adams" à notre variable second_president
Plutôt facile non ?
Exclure un élément :
Il arrive parfois que l'on souhaite non pas selectionner un élément en particulier d'un vecteur, mais au contraire en exclure un et selectionner tous les autres. Pour ceci, il suffit simplement de mettre un signe -
devant l'indice de l'élément que l'on souhaite exclure.
presidents = c("Washington", "Adams", "Jefferson")
tous_sauf_le_premier = presidents[-1]
print(tous_sauf_le_premier) #affiche "Adams" "Jefferson"
Cette technique de selection est bien pratique quand on souhaite ne pas garder un élément d'un vecteur. Utilisons là pour supprimer le premier élément de notre vecteur presidents
:
presidents = c("Washington", "Adams", "Jefferson")
presidents = presidents[-1] #on prend tous les éléments sauf le premier
print(presidents ) #affiche "Adams" "Jefferson"
Avec un peu t'entrainement, vous pensez naturellement à utiliser cette selection négative pour exclure certaines valeurs et ne garder que celles qui vous intéressent.
Selectionner plusieurs éléments d'un vecteur
Il arrive souvent que l'on souhaite extraire ou exclure plusieurs éléments d'un vecteur. Pour cela, il suffit d'indiquer entre les []
non plus un seul nombre, mais un vecteur c(1,2)
des éléments que l'on souhaite selectionner ou exclure. Regardons cela avec un exemple :
presidents = c("Washington", "Adams", "Jefferson")
deux_premiers_presidents = presidents[c(1,2)]
print(deux_premiers_presidents) #"Washington" "Adams"
premier_et_troisieme_president = presidents[c(1,3)]
print(premier_et_troisieme_president) "Washington" "Jefferson"
Vous pouvez aussi passer le vecteur des indices à selectionner en tant que variable :
presidents = c("Washington", "Adams", "Jefferson")
index = c(1,2)
deux_premiers_presidents = presidents[index]
print(deux_premiers_presidents) #"Washington" "Adams"
Cette façon de faire est trés souvent utilisée en pratique, par exemple pour selectionner seulement les éléments qui vont intéressent et dont vous avez récupéré les indices dans une autre variable.
Exclure certains éléments
Vous pouvez utiliser la même technique vu précédent qui consiste à mettre un -
devant les indices pour préciser que vous voulez tous les indices sauf ceux indiqués :
presidents = c("Washington", "Adams", "Jefferson")
troiseme_president = presidents[c(-1,-2)]
print(troiseme_president) #"Jefferson"
Là aussi vous pouvez indiquer les indices à exclure dans une variable :
presidents = c("Washington", "Adams", "Jefferson")
index = c(-1,-2)
troisieme_president = presidents[index]
print(troiseme_president) #"Jefferson"
Remarque : Vous ne pouvez pas à la fois utiliser des indices positifs et négatifs dans votre vecteur de selection.En effet cela n'aurai pas de sens. Si vous essayez quand même de le faire, les indices positifs seront ignorés et R se comportera comme si le vecteur contenait uniquement des nombres négatifs.
presidents = c("Washington", "Adams", "Jefferson")
index = c(1,-2)
troisieme_president = presidents[index] #Seul les élements négatifs sont pris en compte dans index
print(troiseme_president) #"Washington" "Jefferson"
Selectionner des éléments avec un vecteur logique
Il existe une troisiéme façon de selectionner des éléments dans un vecteur, qui est d'utiliser non pas un vecteur de nombres comme vecteur d'index, mais un vecteur de boleans. Chaque indice sera selectionné si il est associé à TRUE
, et il ne sera pas selectionné si il est associé à FALSE
.
presidents = c("Washington", "Adams", "Jefferson")
index = c(TRUE,TRUE,FALSE)
presidents = presidents[index]
print(presidents) #"Washington" "Adams"
Cette technique est trés souvent utilisée, car il existe de nombreuses fonctions qui retournent un vecteur de booleans. Par exemple la fonction is.na()
vous indiquera pour chaque indice de votre vecteur TRUE
il la valeur correspondante est une donnée manquante, et FALSE
sinon.
Regardons comment on peut utiliser cette fonction pour supprimer les valeurs manquantes de notre vecteur revenus
qui comporte deux valeurs NA
.
revenus = c(10000, NA, 25000, 43000, NA)
index_NA = is.na(revenus)
print(index_NA) #FALSE TRUE FALSE FALSE TRUE
revenus = revenus(!index_NA) #on selectionne tous les élements sauf ceux manquants
print(revenus)
On remarque bien que la fonction is.na()
renvois un vecteur logical
où chaque élément indique si l'élément correspondant du vecteur que l'on a passé à la fonction est manquant ou non. Comme nous ne voulons pas selectionner les valeurs manquantes (qui sont marquée comme TRUE
dans notre vecteur index_NA
), on doit "inverser" notre vecteur d'index pour que les TRUE
deviennent des FALSE
et vis versa. Cela se fait avec l'opérateur !
qui inverse la valeur d'un bolléen (transforme les TRUE
en FALSE
et les FALSE
en TRUE
). Nous reparlerons de cet opérateur dans le chapitre suivant.
Une fois l'inversion du vecteur d'index effectuée, on à plus qu'à selectionner les éléments correspondants dans notre vecteur revenus
à l'aide de l'instruction revenus = revenus(!index_NA)
.
Là encore, retenez bien cette façon de faire qui est trés courante pour enlever les données manquantes.
Retirer et ajouter des éléments à un vecteur
Pour retirer des éléments d'un vecteur, il suffit de le redéfinir en précisant les éléments à exclure à l'aide des techniques que nous vennons de voir :
presidents = c("Washington", "Adams", "Jefferson")
presidents = presidents[c(-3)] #on retire le troisiéme élement du vecteur
Cette technique est là encore trés utile à retenir.
Ajouter des éléments à un vecteur
Pour ajouter des éléments à un vecteur, il suffit d'utiliser à nouveau c()
avec le vecteur d'origine et les nouveaux éléments à rajouter.
presidents = c("Washington", "Adams", "Jefferson")
presidents = c(presidents, "Madison") #on ajoute le 4éme président des US
Vous pouvez bien entendu ajouter plusieurs éléments à la suite :
presidents = c("Washington", "Adams", "Jefferson")
presidents = c(presidents, "Madison", "Monroe") #on ajoute le 4éme président des US
Ajouter différents vecteurs ensembles :
Il est possible d'utiliser cette technique pour combiner différents vecteurs ensembles :
presidents1 = c("Washington", "Adams", "Jefferson")
presidents2 = c("Madison", "Monroe")
presidents = c(presidents1, presidents2)
print(presidents)
#"Washington" "Adams" "Jefferson" "Madison" "Monroe"
Bien entendu, cela fonctionne aussi bien pour les vecteurs de type character
comme dans ces exemples, mais également pour tous les autres types de vecteurs. Si vous essayez de combiner ensemble deux vecteurs de types différents, alors le nouveau vecteur obtenu sera converti en un type unique selon les régles que nous avons vu au chapitre précédent.
presidents1 = c("Washington", "Adams", "Jefferson")
presidents = c(president1, c(2.4))
print(presidents) #"Washington" "Adams" "Jefferson" "2.4"
Notre second vecteur de nombres à été converti en chaines de caractéres, car ce type l'emporte sur tous les autres.
A retenir :
- Les opérations entre vecteurs fonctionnent membre à membre.
- Les opérations mathématiques fonctionnent sur les vecteurs de nombres de façon habituelle, sont impossibles sur les vecteurs de
character
et fonctionnent sur leslogical
qui sont en réalité convertis en nombres. - Les opérations qui fonctionnent et impliquent des
NA
donnent toujoursNA
, quelque soit les autres éléments en jeux. Cela est vrai également pourNaN
, donne toujoursNaN
, sauf si il est associé à unNA
. - Vous pouvez selectionner les éléments d'un vecteur avec la syntaxe
mon_vecteur[5]
pour selectionner la 5éme composante du vecteur. Vous pouvez également passer un vecteur pour selectionner plusieurs composantes, commemon_vecteur[c(5,10)]
. - Vous pouvez selectionner tous les éléments d'un vecteur sauf certains, en utilisant la même syntaxe mais avec un
-
devant. Par exemple :mon_vecteur[c(-1, -4)]
. - Il existe une troisième façon de faire, qui consiste à utiliser un vecteur
logical
a la place :mon_vecteur[c(TRUE, FALSE)]
. - Il est possible d'ajouter bout à bout deux vecteurs ensemble avec
c()
:vect3 = c(vect1, vect2)
. Si les deux vecteurs ne sont pas de même type, alors ils seront converti en un type unique comme vu au chapitre précédent.