6. Vecteurs

R manipule deux types de structures de données:

  • les structures de données de base

  • les structures de données composites

La structure de base la plus simple est le vecteur et nous allons étudier son fonctionnement dans ce chapitre.

Pour stocker des données hétérogènes et complexes, on a besoin de structures de données composites, plus évoluées. La data.frame en fait partie. On étudiera son fonctionnement dans le chapitre Data frames.

6.1. Définition

Un vecteur est une entité consistant en une collection ordonnée d’éléments atomiques. Par élément atomique, on entend :

Atomic Types

Data

numeric

Numeric data (approximations of the real numbers)

integer

Integer data (whole numbers). integer is a subclass of numeric

complex

Complex numbers

logical

Boolean

character

Character data (strings)

raw

Binary data

Dans le langage R, les vecteurs:

  • sont homogènes (ne stockent que des données de même type)

  • sont indexables (on peut récupérer un ou plusieurs éléments à partir de leur position)

  • peuvent avoir des éléments nommés

Pour construire un vecteur x contenant 5 éléments (10.4, 5.6, 3.1, 6.4 et 21.7), on utilise la commande suivante:

> x <- c(10.4, 5.6, 3.1, 6.4, 21.7)
> x
[1] 10.4  5.6  3.1  6.4 21.7

C’est une instruction d’affectation pour laquelle on retrouve:

  • à gauche : le nom de la variable

  • à droite : la fonction c() prenant un nombre arbitraire d’arguments et retournant un vecteur dont les éléments sont obtenus par concaténation de ses arguments.

Notons que les vecteurs ont une structure « plate », indépendamment de l’instruction de construction:

> x <- c(10.4, c(5.6, 3.1, c(6.4, 21.7)))
> x
[1] 10.4  5.6  3.1  6.4 21.7

Avertissement

Les affectations peuvent également utiliser la fonction assign() mais son utilisation n’est pas encouragée pour des raisons évidentes de simplicité d’écriture:

> assign("x", c(10.4, 5.6, 3.1, 6.4, 21.7))

L’opérateur <- peut être vu comme un raccourci syntaxique et sera utilisé de façon préférentielle.

Considérons l’affectation suivante:

> y <- c(x, 0, x)
> y
 [1] 10.4  5.6  3.1  6.4 21.7  0.0 10.4  5.6  3.1  6.4 21.7

Un vecteur y est créé en concaténant les arguments de la fonction c() dans l’ordre d’apparition.

Il est possible d’insérer une ou plusieurs valeurs dans un vecteur avec la fonction append():

> append(x,99)
[1] 10.4  5.6  3.1  6.4 21.7 99.0
> append(x,99,3) # third argument describe position where values will be inserted
[1] 10.4  5.6  3.1 99.0  6.4 21.7
> append(x,c(99,100),3) # multiple values insertion
[1]  10.4   5.6   3.1  99.0 100.0   6.4  21.7

6.2. Arithmétique des vecteurs

Voici quelques règles qui régissent le comportement des vecteurs:

  • lorsque des vecteurs sont utilisés dans une expression arithmétique, les opérations sont effectuées élément par élément

  • les vecteurs n’ont pas besoin d’avoir une longueur identique, un processus de recyclage de données permet de construire un vecteur résultat de taille la plus grande taille des vecteurs mis en jeu. En particulier une constante sera transformé en un vecteur par répétition de sa valeur.

Question

Essayer de prédire le résultat de v <- 2*x + y + 1 en répondant préalablement aux questions suivantes:

  • quelle est la longueur de x ?

  • quelle est la longueur de y ?

  • quelle est la longueur de 1 ?

  • quel est le vecteur le plus long ?

  • imaginer la règle de recyclage du vecteur le plus court

Utiliser la console R pour vérifier

En utilisant les vecteurs précédents, le résultat devrait être:

> v <- 2*x + y + 1
Warning message:
In 2 * x + y :
  longer object length is not a multiple of shorter object length
> v
 [1] 32.2 17.8 10.3 20.2 66.1 21.8 22.6 12.8 16.9 50.8 43.5

R construit un nouveau vecteur v :

  • de longueur 11, la longueur de y le plus long vecteur mis en jeu

  • recycle les valeurs de x jusqu’à obtenir un vecteur de longueur 11 : 10.4 5.6 3.1 6.4 21.7 (+) 10.4 5.6 3.1 6.4 21.7 (+) 10.4

  • recycle la valeur 2 jusqu’à obtenir un vecteur de longueur 11 : 2 2 2 2 2 2 2 2 2 2 2

  • recycle la valeur 1 jusqu’à obtenir un vecteur de longueur 11 : 1 1 1 1 1 1 1 1 1 1 1

  • effectue l’arithmétique élément par élément

Question

Quel est le sens du warning affiché dans la console lors de la construction de v ?

Les opérateurs arithmétiques sont :

  • +

  • -

  • *

  • /

  • ^ (exponentiation)

Les opérations mathématiques courantes:

  • log()

  • exp()

  • sin()

  • cos()

  • tan()

  • sqrt()

Les fonctions max() et min() retournent la plus grande et la plus petite valeur du vecteur passé en argument sous la forme d’un vecteur de taille 1 (il n’y a pas de scalaire en R):

> v
 [1] 32.2 17.8 10.3 20.2 66.1 21.8 22.6 12.8 16.9 50.8 43.5
> max(v)
[1] 66.1
> min(v)
[1] 10.3

La fonction range() utilise les fonctions min() et max() pour construire le résultat:

> range(v)
[1] 10.3 66.1

La fonction length() retourne le nombre d’éléments:

> length(v)
[1] 11

La fonction sum() fait ce qu’elle doit faire:

> sum(v)
[1] 315

et la fonction prod() également:

> prod(v)
[1] 1.856438e+15

On a vu précédemment que la fonction summary() produisait des statistiques de premier ordre. La moyenne est obtenue avec mean() et la variance avec var():

> mean(v)
[1] 28.63636
> var(v)
[1] 312.4905

La fonction sort() retourne une permutation du vecteur passé en argument, avec les éléments ordonnés dans le sens croissant:

> v
 [1] 32.2 17.8 10.3 20.2 66.1 21.8 22.6 12.8 16.9 50.8 43.5
> sort(v)
 [1] 10.3 12.8 16.9 17.8 20.2 21.8 22.6 32.2 43.5 50.8 66.1

Astuce

La plupart du temps le tri sera effectué sur une partie d’une data frame avec les fonctions du package dplyr. Si le besoin de trier de simples vecteurs se fait sentir, la fonction sort() dispose de plusieurs arguments explicités dans la documentation. Et d’autres fonctions sont également disponibles : order(), sort.list(), …

Les fonctions max() et min() fonctionnent également avec plusieurs arguments. Construisons 2 vecteurs constitués de valeurs aléatoires. On utilise pour celà la fonction sample()

> a <- sample(1:100, 10, replace=FALSE)
> a
 [1]  3 33 11 80 68 58 15 50  4 99

> b <- sample(1:100, 20, replace=FALSE)
> b
 [1]  99  88  91  28  63 100  13  95   4  32

Sans surprise

> min(a,b)
[1] 3
> max(a,b)
[1] 100

Les fonctions pmax() (parallel max) and pmin() (parallel min) retourne un vecteur de longueur la taille du plus long argument, constitué pour chaque position, de la valeur max ou min prise élément par élément.

Question

Essayer de prédire le résultat retourné par pmax() et pmin() lorsque ces fonctions sont appliquées aux vecteurs a et b. Utiliser la console de R pour vérifier…

6.3. Génération de séquences

La plupart du temps on travaille avec des jeux de données importés mais il est parfois nécessaire de compléter ces données en produisant des vecteurs de taille arbitraire. R dispose de plusieurs mécanismes pour « fabriquer » des données.

6.3.1. L’opérateur :

On a déjà rencontré cet opérateur en début de chapitre.

1:10 est l’équivalent de c(1, 2, ..., 10):

> 1:10
 [1]  1  2  3  4  5  6  7  8  9 10

L’opérateur : a une priorité plus grande que les opérateurs arithmétiques:

> 2*1:10
 [1]  2  4  6  8 10 12 14 16 18 20

Question

Essayer de prédire le résultat des deux séquences ci dessous:

> n <- 10
> 1:n-1
> 1:(n-1)

Vérifier dans la console…

La construction 10:1 est utilisé pour générer une séquence dans l’ordre décroissant:

> 10:1
 [1] 10  9  8  7  6  5  4  3  2  1

6.3.2. La fonction seq()

La fonction seq() peut également être utilisée. Elle comporte 5 arguments, dont certains sont optionnels.

Lorsqu’on l’utilise avec seulement deux arguments, elle se comporte comme l’opérateur ::

> seq(2,10)
[1]  2  3  4  5  6  7  8  9 10

On peut également passer les informations avec des arguments nommés:

> seq(1,10)
 [1]  1  2  3  4  5  6  7  8  9 10
> seq(from=1, to=10)
 [1]  1  2  3  4  5  6  7  8  9 10
> seq(to=10, from=1)
 [1]  1  2  3  4  5  6  7  8  9 10

Les arguments optionnels by=value et length=value permettent de spécifier le pas et la longueur de la séquence. Si aucun n’est fourni la valeur par défaut by=1 est utilisée:

> seq(-1, 1, by=.3)
[1] -1.0 -0.7 -0.4 -0.1  0.2  0.5  0.8

> seq(length=9, from=-5, by=.2)
[1] -5.0 -4.8 -4.6 -4.4 -4.2 -4.0 -3.8 -3.6 -3.4

Le cinquième argument along=vector est utilisé pour construire une séquence de longueur la longueur du vecteur passé en argument:

> seq(3, along=c("a", "b", "c", "d", "e"))
[1] 3 4 5 6 7

6.3.3. La fonction rep()

La fonction rep() est utilisé pour dupliquer des données.

Dans sa forme la plus simple:

> x <- c(10.4, 5.6, 3.1, 6.4, 21.7)
> rep(x, times=2)
 [1] 10.4  5.6  3.1  6.4 21.7 10.4  5.6  3.1  6.4 21.7

Une autre façon de l’utiliser:

> rep(x, each=2)
 [1] 10.4 10.4  5.6  5.6  3.1  3.1  6.4  6.4 21.7 21.7

6.4. Vecteurs logiques

On a beaucoup travaillé jusqu’à présent avec des grandeurs numériques mais R permet également de manipuler des vecteurs logiques. Les valeurs utilisées sont TRUE, FALSE, et NA. Les deux premières sont souvent abrégées par T et F.

Avertissement

T et F sont de simples variables initialisées à TRUE et FALSE et peuvent être écrasées par l’utilisateur. La rigueur imposerait de toujours travailler avec TRUE et FALSE qui sont des variables réservées par le système.

Pour illustrer le fonctionnement des vecteurs logiques, utilisons le vecteur x créé précédemment:

> x
[1] 10.4  5.6  3.1  6.4 21.7

Question

Essayer de prédire le résultat de l’expression x > 6 en inférant la longueur du vecteur résultat et en se souvenant que R travaille élément par élément.

Les opérateurs logiques sont:

  • <

  • <=

  • >

  • >=

  • == (égalité exacte)

  • != (inégalité)

Si c1 et c2 sont des expressions logiques:

  • c1 & c2 exprime le ET logique

  • c1 | c2 exprime le OU logique

  • !c1 exprime la négation

Important

Si les vecteurs logiques sont impliqués dans des expressions arithmétiques, les valeurs logiques TRUE et FALSE sont forcées à 1 et 0

6.5. Valeurs manquantes

Comme au l’a vu dans le paragraphe Nombres spéciaux, il peut y avoir des éléments manquants dans un vecteur identifiés par le nombre spécial NA. En général, toute opération impliquant un NA retourne un NA.

La fonction is.na() retourne un vecteur logique de même taille que celui passé en paramètre et comportant la valeur TRUE si l’élément correspondant est NA:

> z <- c(1:3,NA)
> z
[1]  1  2  3 NA
> is.na(z)
[1] FALSE FALSE FALSE  TRUE

La fonction is.na() retourne TRUE pour les valeurs NA et NaN.

La fonction is.nan() retourne TRUE pour les seules valeurs NaN:

> z <- append(z, NaN)
> z
[1]   1   2   3  NA NaN
> is.na(z)
[1] FALSE FALSE FALSE  TRUE  TRUE
> is.nan(z)
[1] FALSE FALSE FALSE FALSE  TRUE

Important

Lorsqu’on travaille avec des jeux de données réélles, il est de première importance d’identifier les observations complètes (ne comportant aucun NA) et les observations incomplètes (comportant au moins un NA).

6.6. Vecteurs de caractères

Les chaines de caractères se définissent avec la double " ou simple apostrophe '. Un vecteur de chaines de caractères est une séquence ordonnée de chaines de caractères.

A titre d’exemple, la fonction names() appliquée à la data.frame contenant les informations sur les véhicules utilisée dans le chapitre Un aperçu du fonctionnement de R retourne un tel vecteur:

> names(veh)
 [1] "lib_mrq"         "lib_mod_doss"    "lib_mod"
 [4] "dscom"           "cnit"            "tvv"
 [7] "cod_cbr"         "hybride"         "puiss_admin_98"
[10] "puiss_max"       "typ_boite"       "nb_rapp"
[13] "conso_urb"       "conso_exurb"     "conso_mixte"
[16] "co2"             "co_typ_1"        "hc"
[19] "nox"             "hcnox"           "ptcl"
[22] "masse_ordma_min" "masse_ordma_max" "champ_v9"
[25] "date_maj"        "Carrosserie"     "gamme"

Le caractère \ est utilisé comme caractère d’échappement. Ainsi:

  • \\ est affiché \

  • pour afficher le caractère ", la chaine de caractère sera \"

  • \n sera utilisé pour un retour à la ligne

  • \t pour une tabulation

  • etc…

On construit un vecteur de caractères comme un vecteur numérique avec la fonction c():

> x <- c("a", "b", "c")
> x
[1] "a" "b" "c"

La fonction paste(), à laquelle on peut passer un nombre arbitraire d’arguments, permet la concaténation:

> paste("a", "b", "c")
[1] "a b c"

Si les arguments ne sont pas des chaines de caractère, R tente de les forcer:

> paste("a", "b", 1)
[1] "a b 1"
> paste("a", "b", FALSE)
[1] "a b FALSE"

Par défaut l’espace simple est utilisé comme séparateur, mais il est paramétrable:

> paste("a", "b", "c", sep="")
[1] "abc"

paste() peut également prendre des vecteurs en argument, et procéder à la concaténation élément par élément:

> x <- c("a", "b", "c")
> y <- c("d", "e", "f")
> paste(x, y, sep="")
[1] "ad" "be" "cf"

Question

En utilisant la règle de recyclage, essayer de prédire le résultat des 2 instructions suivantes:

> paste(c("X","Y"), 1:8, sep="")
> paste(c("X","Y"), 1:5, c("a", "b"), sep="-")

Vérifier dans la console

6.7. Vecteurs d’index

On peut sélectionner une partie d’un vecteur avec l’opérateur d’indexation [ ].

6.7.1. Sélection par condition logique

On peut passer un vecteur logique à l’opérateur d’indexation. Dans ce cas, les éléments sélectionnés sont ceux pour lesquelles l’élément du vecteur d’indexation prend la valeur TRUE. Ceux correspondant à la valeur FALSE sont écartés. Par exemple:

> x <- c(100,34,NA,78,6,NA,84,NA,27,59)

> is.na(x)
 [1] FALSE FALSE  TRUE FALSE FALSE  TRUE FALSE  TRUE FALSE FALSE

 > !is.na(x)
 [1]  TRUE  TRUE FALSE  TRUE  TRUE FALSE  TRUE FALSE  TRUE  TRUE

 > x[is.na(x)]
[1] NA NA NA

> x[!is.na(x)]
[1] 100  34  78   6  84  27  59

C’est une façon de filtrer les valeur manquantes/non manquantes d’un vecteur.

Astuce

  • on peut construire des vecteur logiques complexes avec les opérateurs & et |

  • on disposera de fonction de plus haut niveau lorsqu’il s’agira de travailler sur une data.frame

Question

Essayer de prédire le résultat de l’instruction suivante:

> (x+1)[(!is.na(x)) & x>40]

Vérifier dans la console

6.7.2. Quels indices sont TRUE ?

La fonction which() est utile pour identifier les indices TRUE en fonction d’un prédicat passé en argument:

> which(is.na(x))
[1] 3 6 8
> which(x>40)
[1]  1  4  7 10

6.7.3. Sélection par index

Dans ce cas les valeurs du vecteur passé à l’opérateur d’indexation doivent être dans l’ensemble {1, 2, …, length(x)}. Les éléments correspondants sont sélectionnés et concaténés, dans cet ordre, pour construire le résultat. Le vecteur d’index peut être de longueur arbitraire:

> x
 [1] 100  34  NA  78   6  NA  84  NA  27  59
> x[3]
[1] NA
> x[1:5]
[1] 100  34  NA  78   6

Question

Essayer de prédire le résultat de l’instruction suivante:

> x[c(5,4,3)]

Vérifier dans la console

6.7.4. Désélection par index

Le signe - est utilisé pour identifier les valeurs à exclure:

> x[-(1:5)]
[1] NA 84 NA 27 59

Question

Essayer de prédire le résultat de l’instruction suivante:

> x[-c(5,4,3)]

Vérifier dans la console

6.7.5. Sélection par nom

Lorsqu’un objet posséde un attribut name pour identifier ses composants, on peut utiliser un vecteur de chaines de caractères pour sélectionner des éléments:

> fruit <- c(5, 10, 1, 20)
> names(fruit)
NULL
> names(fruit) <- c("orange", "banana", "apple", "peach")
> fruit[c("apple","orange")]
 apple orange
     1      5

Il est plus lisible et maintenable de sélectionner des éléments par nom, plutôt que par indice. On retrouvera cette façon de faire lorsqu’on manipulera des data.frame dans le chapitre Data frames.

6.7.6. Affectation

On peut utiliser l’opérateur d’indexation à gauche de l’opérateur d’affectation (LHS) pour affecter des valeurs à une sous partie d’un vecteur. L’expression doit être de la forme vector[index_vector].

Par exemple, on remplace les valeurs manquantes par un 0:

> x[is.na(x)] <- 0
> x
 [1] 100  34   0  78   6   0  84   0  27  59

Une façon (tordue) de prendre la valeur absolue:

> y[y < 0] <- -y[y < 0]

Cet exemple n’est qu’une illustration de l’utilisation de l’indexation, et on choisira évidemment la forme classique:

> y <- abs(y)

6.8. Coercition

R permet la coercition explicite ou implicite entre les objets.

La coercition explicite est effectuée avec la famille de fonctions as.xxx():

> a <- -1:1
> a
[1] -1 0 1
> typeof(a)
[1] "integer"

La règle est que la coercition s’applique du type le plus contraint vers le type le moins contraint, c’est à dire dans l’ordre : logical, integer, double, et character. Certaines opérations sont évidentes:

> as.character(a)
[1] "-1" "0" "1"

> as.complex(a)
[1] -1+0i  0+0i  1+0i

as.numeric() force l’objet vers un type double:

> as.numeric(a)
[1] -1 0 1
> typeof(as.numeric(a))
[1] "double"

Attention aux comparaisons, == teste l’égalité, indépendamment du type:

> a == as.numeric(a)
[1] TRUE TRUE TRUE

alors que la fonction identical() compare également le type:

> identical(a,as.numeric(a) )
[1] FALSE

Lors de la conversion vers le type logique, seules les valeurs nulles sont FALSE

> as.logical(a)
[1]  TRUE FALSE  TRUE

Si la coercition n’a pas de sens, l’opération échoue:

> as.numeric(c("a", "b"))
[1] NA NA
Warning message:
NAs introduced by coercion

La coercition implicite est mise en oeuvre lorsqu’on manipule des éléments inhomogènes:

> a<-c(1,"a")
> typeof(a)
[1] "character"
> a
[1] "1" "a"

> a<-c(FALSE, "a")
> typeof(a)
[1] "character"
> a
[1] "FALSE" "a"

> a<-c(1, TRUE)
> typeof(a)
[1] "double"
> a
[1] 1 1

La coercition implicite n’est pas souhaitable pour des questions de lisibilité et de maintenance:

> as.numeric("1") < 2
[1] TRUE

est meilleur que:

> "1" < 2
[1] TRUE

6.9. Exercices

Définir les vecteurs x et y comme suit:

> x<-c(4,2,6)
> y<-c(1,0,-1)

Question

Essayer de prédire le résultat des instructions suivantes:

> length(x)
> sum(x)
> sum(x^2)
> x+y
> x*y
> x-2
> x^2

Vérifier dans la console

Question

Essayer de prédire le résultat des instructions suivantes:

> 7:11
> seq(2,9)
> seq(4,10,by=2)
> seq(3,30,length=10)
> seq(6,-4,by=-2)

Vérifier dans la console

Question

Essayer de prédire le résultat des instructions suivantes:

> rep(2,4)
> rep(c(1,2),4)
> rep(c(1,2),c(4,4))
> rep(1:4,4)
> rep(1:4,rep(3,4))

Vérifier dans la console

Question

Use the rep function to define simply the following vectors in R:

[1] 6 6 6 6 6 6
[1] 5 8 5 8 5 8 5 8
[1] 5 5 5 5 8 8 8 8

Considérons maintenant l’instruction suivante:

x <- c(5,9,2,3,4,6,7,0,8,12,2,9)

Question

Essayer de prédire le résultat des indexations suivantes:

> x[2]
> x[2:4]
> x[c(2,3,6)]
> x[c(1:5,10:12)]
> x[-(10:12)]

Vérifier dans la console

6.10. Application : population urbaine

Cet exercice d’application utilise un jeu de données sur la population des principales villes françaises entre 1962 et 2012.

6.10.1. Créer l’environnement de travail

  1. Démarrer R Studio

  2. S’assurer qu’aucun projet n’est ouvert, sinon R Studio > File > Close Project

  3. Créer un nouveau projet « Population » R Studio > File > New project... > New Directory > New Project

Le jeu de données concernant la population des 20 plus grandes villes de France entre 1962 et 2012 est stocké au format .RData. Enregistrer ce fichier dans le répertoire du projet.

Indication

Immédiatement après la création du projet, le répertoire doit contenir un dossier masqué et deux fichiers, dont le fichier .RData téléchargé ci dessus..

_images/06-vectors-application.png

Pour extraire les données vers l’espace de travail, cliquer sur le nom du fichier dans le navigateur de fichiers de R Studio.

Plusieurs vecteurs doivent apparaître dans l’explorateur d’objets.

6.10.2. Exploration

On va manipuler ces différents vecteurs pour explorer l’évolution de la population française, dans quelques unes des plus grandes villes, entre 1962 et 2012.

A l’aide de la console R, répondre aux questions suivantes :

  • Combien de villes sont représentées dans les données ? Lesquelles ?

  • Combien d’années sont représentées dans les données ? Lesquelles ?

  • En quelle année Angers a t-elle dépassé pour la première fois 150.000 habitants ?

  • Quelle est la population moyenne de Bordeaux sur les 3 derniers recensements ?

  • Calculer l’évolution de la population parisienne à chacun des recensements ? Utiliser la fonction diff(). Commenter la taille et la composition du vecteur obtenu.

  • Quelles sont les années pour lesquelles l’évolution de population a suivi la même pente (positive ou négative) à Lyon et Toulouse ? La fonction sign() peut être utile.