Dernière mise à jour: 2021-02-23 21:22.

Dans ce fichier on va apprendre à extraire et manipuler des parties des structures de données dans R.

Pour télécharger et ouvrir ce fichier, vérifiez d’abord d’être dans le bon dossier de travail, puis copier-coller ces lignes dans l’invite de commande R:

download.file("http://homepages.ulb.ac.be/~mgagliol/INFOD203/indexation.r",
              "indexation.r")
file.edit("indexation.r")

Pour le cours INFOD203: Une partie de la matière dans ce fichier est rendue obsolète par le tidyverse. Ceci dit, ça reste correcte, et peut être utile pour une meilleure connaissance du langage R. Plus précisement les sections optionnelles sont: 2.1.3, 2.2, 2.3, 2.4, 2.5.2.

Pour le cours SOCAD460: tout le fichier reste pertinent.

L’indexation permets de lire/écrire une partie des valeurs faisant partie d’un vecteur, un data.frame, ou d’autres structures «indexables» (list, matrix, array, …)

Si x est un objet indexable et i est un index:

  • Lecture des valeurs dans x: x[i]
  • Écriture des valeurs dans x: x[i] <- y

Voire aussi ancienne version du syllabus, sec. 5.2-3, pages 54-65.

1 Indexation d’un vecteur

  • directe, par index numérique (entier naturel \(\geq 1\))
  • par index booléen ("logical")

Des exemples de vecteurs qu’on va utiliser dans la suite

mots <- c("un", "deux", "trois", "quatre", "cinq")
mots
[1] "un"     "deux"   "trois"  "quatre" "cinq"  
nombres <- 1:5
nombres
[1] 1 2 3 4 5
dizaines <- seq(from = 10, to = 50, by = 10) # séquence de 10 à 50, avec un pas de 10
dizaines
[1] 10 20 30 40 50

1.1 Index numérique ("integer")

Soit x un vecteur de longueur n == length(x) Tout vecteur i de nombres entiers entre 1 et n (inclus) peut être utilisé pour indexer x ainsi: x[i], le résultat x[i] étant un vecteur de la même longueur que i obtenu en concaténant les éléments de x correspondants aux positions indiqués dans i

mots
[1] "un"     "deux"   "trois"  "quatre" "cinq"  
mots[2] # rappel: 2 est un vecteur de longueur 1
[1] "deux"
2:4     # 2:4 est c(2, 3, 4), vecteur de longueur 3
[1] 2 3 4
mots[2:4] 
[1] "deux"   "trois"  "quatre"
c(1:3, 5) #la combinaison de deux vecteurs est un vecteur
[1] 1 2 3 5
mots[c(1:3, 5)]
[1] "un"    "deux"  "trois" "cinq" 
c(1:2, c(4, 5)) #la combinaison de deux vecteurs est un vecteur
[1] 1 2 4 5
mots[c(1:2, c(4, 5))]
[1] "un"     "deux"   "quatre" "cinq"  
dizaines
[1] 10 20 30 40 50
dizaines[2]
[1] 20
dizaines[2:4]
[1] 20 30 40

Si on ne connaît pas la longueur à l’avance, on peut utiliser length ainsi:

length(mots)
[1] 5
3:length(mots)
[1] 3 4 5
mots[3:length(mots)] 
[1] "trois"  "quatre" "cinq"  
dizaines[4:length(dizaines)]
[1] 40 50

on peut utiliser une indexation directe pour répéter des éléments ou en changer l’ordre

mots[c(5, 4, 3, 2, 1)]
[1] "cinq"   "quatre" "trois"  "deux"   "un"    
nombres[c(4, 4, 4)]
[1] 4 4 4
dizaines[c(1, 2, 1, 5, 2, 2, 3)]
[1] 10 20 10 50 20 20 30

Au lieu des valeurs explicites on peut utiliser une variable comme index

index <- c(2, 1, 3, 2)
index
[1] 2 1 3 2
dizaines[index]
[1] 20 10 30 20
mots[index]
[1] "deux"  "un"    "trois" "deux" 

Avec l’indexation directe, la longueur du résultat est toujours égale à la longueur de l’index:
length(x[i]) == length(i)

Attention a ne pas utiliser d’index == 0, ni d’index > n. Dans les deux cas, R nous ne donne pas un message d’erreur, mais le résultat risque d’introduire des erreurs par la suite, difficiles à dépister:

dizaines[0] # pas de message d'erreur, résultat vide
numeric(0)
dizaines[6] # pas de message d'erreur, résultat `== NA`
[1] NA

NA (Not Available) est une valeur spéciale utilisé par par R pour désigner les valeurs manquantes (par exemple, une réponse refusée par un des sujets dans les résultats d’un questionnaire).

1.2 Index booléen ("logical")

L’indexation la plus utilisé, nous permettant de sélectionner des éléments pas sur base de leur position, mais par une condition logique sur leur valeur. Par exemple:

dizaines 
[1] 10 20 30 40 50
dizaines[dizaines <= 32] # valeurs inférieurs ou égales à `32`
[1] 10 20 30
dizaines[dizaines < 23 | dizaines > 39] # valeurs inférieurs à 23 ou supérieurs à 39 
[1] 10 20 40 50

Comment ça marche ? C’est possible car:

  • on peut utiliser un vecteur booléen comme index;
  • le résultat des expressions en index est bien un vecteur booléen, vrai là où la condition est satisfaite
dizaines <= 32
[1]  TRUE  TRUE  TRUE FALSE FALSE
dizaines[dizaines <= 32]
[1] 10 20 30
dizaines < 23
[1]  TRUE  TRUE FALSE FALSE FALSE
dizaines > 39
[1] FALSE FALSE FALSE  TRUE  TRUE
dizaines < 23 | dizaines > 39
[1]  TRUE  TRUE FALSE  TRUE  TRUE
dizaines[dizaines < 23 | dizaines > 39]
[1] 10 20 40 50

Voyons cela plus en le détail. Soit x un vecteur de longueur n == length(x). Tout vecteur i de valeurs booléens (class "logical") de longueur n peut être utilisé pour indexer x ainsi: x[i], le résultat étant un vecteur d’une longueur égale au nombre de TRUE dans i, obtenu en sélectionnant les éléments de x correspondants aux valeurs TRUE dans i:

mots
[1] "un"     "deux"   "trois"  "quatre" "cinq"  
mots[c(FALSE, TRUE, FALSE, FALSE, FALSE)] 
[1] "deux"
mots[c(FALSE, TRUE, TRUE, TRUE, FALSE)]  
[1] "deux"   "trois"  "quatre"
mots[c(FALSE, FALSE, TRUE, FALSE, TRUE)] 
[1] "trois" "cinq" 
dizaines
[1] 10 20 30 40 50
dizaines[c(FALSE, TRUE, FALSE, FALSE, FALSE)] 
[1] 20
dizaines[c(FALSE, TRUE, TRUE, TRUE, FALSE)]  
[1] 20 30 40
dizaines[c(FALSE, FALSE, TRUE, FALSE, TRUE)]
[1] 30 50

Même chose indirectement, avec une variable:

index <- c(TRUE, FALSE, FALSE, FALSE, TRUE)
index
[1]  TRUE FALSE FALSE FALSE  TRUE
dizaines[index]
[1] 10 50
mots[index]
[1] "un"   "cinq"

L’intérêt de cette indexation est d’utiliser le résultat d’un opérateur booléen appliqué au vecteur comme index pour le vecteur même (voir slides sur la logique ou Syllabus p. 57-8).
Par exemple:

dizaines >= 20 # le résultat est un vecteur booléen de la même taille que dizaines
[1] FALSE  TRUE  TRUE  TRUE  TRUE
dizaines[dizaines >= 20] # je peux donc l'utiliser pour indexer dizaines
[1] 20 30 40 50

Même chose indirectement, avec une variable:

index <- (dizaines >= 20)
index
[1] FALSE  TRUE  TRUE  TRUE  TRUE
dizaines[index]
[1] 20 30 40 50

Je peux utiliser des opérateurs pour combiner différents conditions. Dizaines entre 20 et 40:

dizaines[dizaines >= 20 & dizaines <= 40]
[1] 20 30 40

Dizaines inférieurs à 20 ou supérieurs à 40

dizaines[dizaines < 20 | dizaines > 40]
[1] 10 50

Dizaines à la fois inférieur à 20 et supérieurs à 40:

dizaines[dizaines < 20 & dizaines > 40]   
numeric(0)

il y en n’a pas… le résultat est un vecteur «vide»: numeric(0) qui signifie: vecteur de classe "numeric" et longueur 0. Remarque: l’indexation par booléen peut seulement rendre un sous-ensemble des valeurs dans le vecteur indexé, sans changements d’ordre ni répétitions. Tandis que l’indexation directe nous permet de combiner les éléments du vecteur comment on veux, en changeant leur ordre, ou les répétant.
Par exemple, on ne peut pas reproduire les indexations suivantes avec des booléens:

dizaines[c(2, 2, 2, 2, 2, 2, 2, 2, 2)]
[1] 20 20 20 20 20 20 20 20 20
mots[c(3, 2, 1)]
[1] "trois" "deux"  "un"   
nombres[c(2, 1, 3, 2, 4, 5, 2)]
[1] 2 1 3 2 4 5 2

Remarque: dans l’indexation par entiers, length(x[i]) == length(i). Dans l’indexation par booléens, on doit avoir length(i) == length(x), sinon on fait du recyclage!

1.3 Indexation, assignation, recyclage

Tout comme pour les opérateurs (voir initiation), pour l’assignation aussi il faut faire gaffe au recyclage.

Indépendamment du type d’indexation utilisé, pour éditer les valeurs indexées on peut faire suivre l’indexation par une assignation: par ex x[i] <- val assigne la/les valeur(s) en val aux éléments de x indexés par i.

Quand on fait une assignation après indexation ( x[i] <- v ) on a trois possibilités:

  1. les objets des deux cotés ont la même taille ( length(x[i]) == length(v) ) Dans ce cas, les valeurs en v sont copiés un à un en x[i], en gardant leur ordre Exemples:
dizaines
[1] 10 20 30 40 50
dizaines[4] <- 44
dizaines
[1] 10 20 30 44 50
dizaines[c(2, 5)] <- c(22, 55)
dizaines
[1] 10 22 30 44 55
  1. l’objet à droite du <- a taille 1 ( length(v) == 1 ) Dans ce cas, la valeur v est copié dans chaque place indexé x[i].
    Exemples:
dizaines
[1] 10 22 30 44 55
dizaines[c(1, 3)] <- -1
dizaines
[1] -1 22 -1 44 55
dizaines < 30
[1]  TRUE  TRUE  TRUE FALSE FALSE
dizaines[dizaines < 30] <- 0
dizaines
[1]  0  0  0 44 55
  1. les deux objets à gauche et à droite de <- ont des tailles différents (length(x[i]) != length(v)). À éviter à touts prix ! Dans ce cas, R décide de:

    • faire un «recyclage» des éléments à droite du <- si length(x[i]) > length(v)
    • laisser tomber les éléments en trop si length(x[i]) < length(v)

    Cette particularité de R est la cause de nombreux «bugs»… Exemples de recyclage:

dizaines
[1]  0  0  0 44 55
dizaines[1:4] <- c(1,2) # Recyclage sans aucun warning car length(x[i])==4 est un multiple de length(v)==2
dizaines
[1]  1  2  1  2 55
dizaines[1:3] <- c(-1, -2) # Recyclage avec warning: length(x[i])==3 n'est pas un multiple de length(v)==2 
Warning in dizaines[1:3] <- c(-1, -2): number of items to replace is not a
multiple of replacement length
dizaines
[1] -1 -2 -1  2 55
dizaines[4:5] <- c(400, 500, 600) # length(x[i])==2, length(v)==3 : Le 600 «de trop» à droite du '<-'
Warning in dizaines[4:5] <- c(400, 500, 600): number of items to replace is not
a multiple of replacement length
                                  # est perdue, avec le même warning
dizaines
[1]  -1  -2  -1 400 500

Remarques: L’assignation au point 2 ci-dessus est en effet un cas particulier de recyclage, le seul qui ait du sens! Il faut éviter soigneusement la situation au point 3. C’est quand même important de la connaître, pour dépister les erreurs (surtout dans le cas où on n’a pas de warning).

En somme, pour assigner x[i] <- v

  • soit on utilise un valeur v de la même taille que le résultat de l’indexation x[i]:
    length(x[i]) == length(v)
  • soit on utilise un valeur v de taille 1:
    length(v) == 1

1.4 En somme

On peut indexer un vecteur en utilisant un index:

  • entier (integer):

    • la longueur du résultat est la même que celle de l’index
    • l’ordre des éléments dépends de l’ordre des entiers dans l’index
  • booléen (logical):

    • la longueur de l’index doit être égal à celle du vecteur indexé (danger de recyclage)
    • la longueur du résultat est égal au nombre de TRUE dans l’index
    • l’ordre des éléments dans le résultat est le même que dans le vecteur indexé
    • l’index est crée à partir d’une expression logique, typiquement impliquant le vecteur indexé

2 Indexation d’un data.frame ou d’une matrice

  • par index numérique (entier naturel >= 1)
  • par index booléen (logical)
  • par nom des colonnes/lignes

La différence avec les vecteurs est que pour un data.frame (ou pour une matrix) on a besoin de deux indices: celui pour les lignes, et celui pour les colonnes.

Pour voir des exemples compréhensibles, plutôt que utiliser un data.frame avec des centaines de lignes, on va créer un simple exemple, avec la fonction data.frame (voir initiation.html):

args(data.frame)
function (..., row.names = NULL, check.rows = FALSE, check.names = TRUE, 
    fix.empty.names = TRUE, stringsAsFactors = default.stringsAsFactors()) 
NULL

Dans cet exemple, on crée un data frame avec 5 lignes et 3 colonnes, nommées "mots", "nombres" et "romain". Pour le moment on ignore le paramètre stringsAsFactors, on verra sa signification dans la leçon sur les facteurs.

df <- data.frame(mots = c("un","deux","trois","quatre","cinq"),
                 nombres = 1:5,
                 romain = c("I","II","III","IV","V"),
                 stringsAsFactors = FALSE) # les colonnes "character" ne vont pas devenir des facteurs

Voici le résultat, qu’on a assigné à la variable df:

df
    mots nombres romain
1     un       1      I
2   deux       2     II
3  trois       3    III
4 quatre       4     IV
5   cinq       5      V
class(df)
[1] "data.frame"
dim(df)
[1] 5 3
str(df)
'data.frame':   5 obs. of  3 variables:
 $ mots   : chr  "un" "deux" "trois" "quatre" ...
 $ nombres: int  1 2 3 4 5
 $ romain : chr  "I" "II" "III" "IV" ...

On va aussi créer un exemple de matrice, contenant les entiers de 1 à 12, par colonne (voir initiation.html):

args(matrix)
function (data = NA, nrow = 1, ncol = 1, byrow = FALSE, dimnames = NULL) 
NULL

La fonction matrix prends un vecteur de valeurs et les dispose par colonne en une matrice nrow x ncol.

m <- matrix(1:12, nrow = 4, ncol = 3)
m
     [,1] [,2] [,3]
[1,]    1    5    9
[2,]    2    6   10
[3,]    3    7   11
[4,]    4    8   12
class(m)
[1] "matrix"
dim(m)
[1] 4 3
str(m)
 int [1:4, 1:3] 1 2 3 4 5 6 7 8 9 10 ...

Soit donc

  • X un data.frame ou une matrice de m lignes et n colonnes,
  • i un index pour les lignes,
  • j un index pour les colonnes.
  • Pour indexer lignes et colonnes: X[i, j]
  • Pour indexer les lignes, en gardant toutes les colonnes: X[i, ]
  • Pour indexer les colonnes, en gardant toutes les lignes: X[ , j]

En somme: le champ qui reste vide n’est pas indexé.

Exemples:

df
    mots nombres romain
1     un       1      I
2   deux       2     II
3  trois       3    III
4 quatre       4     IV
5   cinq       5      V
df[2, 3]
[1] "II"
df[2, ]
  mots nombres romain
2 deux       2     II
df[, 2] 
[1] 1 2 3 4 5
df[, c(1, 3)]
    mots romain
1     un      I
2   deux     II
3  trois    III
4 quatre     IV
5   cinq      V
df[1:3, ]
   mots nombres romain
1    un       1      I
2  deux       2     II
3 trois       3    III
df[c(1,3,5), c(TRUE, FALSE, TRUE)]
   mots romain
1    un      I
3 trois    III
5  cinq      V
df[c(1,1), c(1,3,2)]
    mots romain nombres
1     un      I       1
1.1   un      I       1
df$nombres >= 4
[1] FALSE FALSE FALSE  TRUE  TRUE
df[df$nombres >= 4, ]
    mots nombres romain
4 quatre       4     IV
5   cinq       5      V
m
     [,1] [,2] [,3]
[1,]    1    5    9
[2,]    2    6   10
[3,]    3    7   11
[4,]    4    8   12
m[3, 1]
[1] 3
m[2, ]
[1]  2  6 10
m[, 3]
[1]  9 10 11 12
m[2:4, 2:3]
     [,1] [,2]
[1,]    6   10
[2,]    7   11
[3,]    8   12
m[c(1, 4), ]
     [,1] [,2] [,3]
[1,]    1    5    9
[2,]    4    8   12
m[, c(1, 2, 1)]
     [,1] [,2] [,3]
[1,]    1    5    1
[2,]    2    6    2
[3,]    3    7    3
[4,]    4    8    4

2.1 Indexation par nom

Outre à l’indexation par entiers et par booléen, un data frame peut être indexé en utilisant les noms de ses lignes/colonnes.

names(df) # noms des colonnes, classe "character"
[1] "mots"    "nombres" "romain" 

Moins utilisés: rownames/colnames

colnames(df) # même chose: pour un data.frame "names" et "colnames" donnent le même résultat
[1] "mots"    "nombres" "romain" 
rownames(df) # noms des lignes, classe "character", par défaut "1", "2", "3", ...
[1] "1" "2" "3" "4" "5"

2.1.1 Indexation des colonnes par nom avec $

df$"mots"
[1] "un"     "deux"   "trois"  "quatre" "cinq"  
df$mots
[1] "un"     "deux"   "trois"  "quatre" "cinq"  
df$nombres
[1] 1 2 3 4 5

Remarque: avec $, les "" ne sont pas nécessaires, ce qui permets de compléter les noms avec la touche <Tab>.

2.1.2 Indexation des lignes/colonnes avec []

Alternative tidy: utiliser dplyr::select.

Tout comme l’indexation directe, mais avec les noms à la place des nombres. Remarque: ici les "" sont nécessaires:

df[, "mots"]    # équivaut à df$mots
[1] "un"     "deux"   "trois"  "quatre" "cinq"  
df[, "nombres"] # équivaut à df$nombres
[1] 1 2 3 4 5
df[, c("romain", "nombres")] 
  romain nombres
1      I       1
2     II       2
3    III       3
4     IV       4
5      V       5

L’avantage par rapport à $ est que dans ce cas on peut utiliser des variables de classe "character", contenants des noms des colonnes qu’on veut sélectionner

index <- "mots"
df[, index]
[1] "un"     "deux"   "trois"  "quatre" "cinq"  
index <- c("nombres", "mots")
class(index)
[1] "character"
df[, index]
  nombres   mots
1       1     un
2       2   deux
3       3  trois
4       4 quatre
5       5   cinq

Indexation des lignes par nom: peu utilisé

df[c("1", "3"), ]
   mots nombres romain
1    un       1      I
3 trois       3    III

même résultat que

df[c(1, 3), ]
   mots nombres romain
1    un       1      I
3 trois       3    III

2.1.3 Assignation des noms des variables

Alternative tidy: utiliser dplyr::rename.

Pour changer les noms des lignes/colonnes, on peut leur assigner des nouveaux valeurs, par ex:

df
    mots nombres romain
1     un       1      I
2   deux       2     II
3  trois       3    III
4 quatre       4     IV
5   cinq       5      V
rownames(df)
[1] "1" "2" "3" "4" "5"
letters #ce vecteur est défini dans la librairie de base 
 [1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s"
[20] "t" "u" "v" "w" "x" "y" "z"
letters[1:5]
[1] "a" "b" "c" "d" "e"
rownames(df) <- letters[1:5]
df
    mots nombres romain
a     un       1      I
b   deux       2     II
c  trois       3    III
d quatre       4     IV
e   cinq       5      V
rownames(df)
[1] "a" "b" "c" "d" "e"

Les exemples suivants fonctionnent aussi en remplaçant “names” avec “colnames”

df
    mots nombres romain
a     un       1      I
b   deux       2     II
c  trois       3    III
d quatre       4     IV
e   cinq       5      V
names(df)
[1] "mots"    "nombres" "romain" 
names(df) <- c("M", "N", "R")
df
       M N   R
a     un 1   I
b   deux 2  II
c  trois 3 III
d quatre 4  IV
e   cinq 5   V
names(df)
[1] "M" "N" "R"

On peut aussi indexer les noms, dans le deux cas

names(df)[1:2] <- c("En mots", "En chiffres")
df
  En mots En chiffres   R
a      un           1   I
b    deux           2  II
c   trois           3 III
d  quatre           4  IV
e    cinq           5   V
names(df)
[1] "En mots"     "En chiffres" "R"          

Une matrice aussi peut avoir des noms des lignes/colonnes, accessibles avec rownames et colnames (mais pas names):

rownames(m) <- LETTERS[1:4]
colnames(m) <- letters[1:3]
m
  a b  c
A 1 5  9
B 2 6 10
C 3 7 11
D 4 8 12
rownames(m)
[1] "A" "B" "C" "D"
colnames(m)
[1] "a" "b" "c"

2.1.4 En somme

Pour indexer un data.frame, on peut utiliser les trois façons d’indexer (directe, booléenne, par nom), de façon indépendante pour lignes et colonnes, par exemple, dans notre df restauré:

df <- data.frame(mots = c("un","deux","trois","quatre","cinq"),
                 nombres = 1:5,
                 romain = c("I","II","III","IV","V"),
                 stringsAsFactors = FALSE)
df
    mots nombres romain
1     un       1      I
2   deux       2     II
3  trois       3    III
4 quatre       4     IV
5   cinq       5      V
df[df$nombres != 3, ]
    mots nombres romain
1     un       1      I
2   deux       2     II
4 quatre       4     IV
5   cinq       5      V
df[df$nombres <= 3, c(2, 1)]
  nombres  mots
1       1    un
2       2  deux
3       3 trois
df[c(5,3,5), c(2,3)]
    nombres romain
5         5      V
3         3    III
5.1       5      V

2.2 Fonction subset

Alternative tidy: utiliser dplyr::filter et dplyr::select pour indexer lignes et colonnes, respectivement.

En alternative à l’indexation booléenne par ligne, on va utiliser la fonction subset. Cette fonction prends trois arguments, dans l’ordre:

  1. le data.frame dont on veut extraire un sous-ensemble des données;
  2. le paramètre subset, qui est un index booléen pour sélectionner un sous-ensemble des lignes;
  3. le paramètre select, optionnel, un index pour sélectionner un sous-ensemble des colonnes (par nom, entier ou booléen).

Exemples:

df[df$nombres <= 3, ] # indexation par ligne
   mots nombres romain
1    un       1      I
2  deux       2     II
3 trois       3    III
subset(df, nombres <= 3) # même chose, avec subset
   mots nombres romain
1    un       1      I
2  deux       2     II
3 trois       3    III

L’avantage de la fonction subset c’est qu’elle permet d’utiliser les noms des colonnes directement dans la condition pour sélectionner les lignes, dans ces exemples nombres plutôt que df$nombres.

subset(df, nombres <= 3, c(2, 1))
  nombres  mots
1       1    un
2       2  deux
3       3 trois

Pour mieux apprécier l’avantage de subset, on va voir un exemple plus complexe, en utilisant un jeu de données du paquet "questionr":

library("questionr")
data(package = "questionr") #juste pour vérifier le nom des données qui nous intéressent
data("hdv2003")

Voici maintenant un data.frame un peu plus grand, contenant des réponses à un questionnaire de l’INSEE (France).

?hdv2003 # Page d'aide avec lien à l'enquête
class(hdv2003)
[1] "data.frame"
str(hdv2003)
'data.frame':   2000 obs. of  20 variables:
 $ id           : int  1 2 3 4 5 6 7 8 9 10 ...
 $ age          : int  28 23 59 34 71 35 60 47 20 28 ...
 $ sexe         : Factor w/ 2 levels "Homme","Femme": 2 2 1 1 2 2 2 1 2 1 ...
 $ nivetud      : Factor w/ 8 levels "N'a jamais fait d'etudes",..: 8 NA 3 8 3 6 3 6 NA 7 ...
 $ poids        : num  2634 9738 3994 5732 4329 ...
 $ occup        : Factor w/ 7 levels "Exerce une profession",..: 1 3 1 1 4 1 6 1 3 1 ...
 $ qualif       : Factor w/ 7 levels "Ouvrier specialise",..: 6 NA 3 3 6 6 2 2 NA 7 ...
 $ freres.soeurs: int  8 2 2 1 0 5 1 5 4 2 ...
 $ clso         : Factor w/ 3 levels "Oui","Non","Ne sait pas": 1 1 2 2 1 2 1 2 1 2 ...
 $ relig        : Factor w/ 6 levels "Pratiquant regulier",..: 4 4 4 3 1 4 3 4 3 2 ...
 $ trav.imp     : Factor w/ 4 levels "Le plus important",..: 4 NA 2 3 NA 1 NA 4 NA 3 ...
 $ trav.satisf  : Factor w/ 3 levels "Satisfaction",..: 2 NA 3 1 NA 3 NA 2 NA 1 ...
 $ hard.rock    : Factor w/ 2 levels "Non","Oui": 1 1 1 1 1 1 1 1 1 1 ...
 $ lecture.bd   : Factor w/ 2 levels "Non","Oui": 1 1 1 1 1 1 1 1 1 1 ...
 $ peche.chasse : Factor w/ 2 levels "Non","Oui": 1 1 1 1 1 1 2 2 1 1 ...
 $ cuisine      : Factor w/ 2 levels "Non","Oui": 2 1 1 2 1 1 2 2 1 1 ...
 $ bricol       : Factor w/ 2 levels "Non","Oui": 1 1 1 2 1 1 1 2 1 1 ...
 $ cinema       : Factor w/ 2 levels "Non","Oui": 1 2 1 2 1 2 1 1 2 2 ...
 $ sport        : Factor w/ 2 levels "Non","Oui": 1 2 2 2 1 2 1 1 1 2 ...
 $ heures.tv    : num  0 1 0 2 3 2 2.9 1 2 2 ...
dim(hdv2003) # 2000 observations (répondants) de 20 variables (questions)
[1] 2000   20
summary(hdv2003)
       id              age           sexe     
 Min.   :   1.0   Min.   :18.00   Homme: 899  
 1st Qu.: 500.8   1st Qu.:35.00   Femme:1101  
 Median :1000.5   Median :48.00               
 Mean   :1000.5   Mean   :48.16               
 3rd Qu.:1500.2   3rd Qu.:60.00               
 Max.   :2000.0   Max.   :97.00               
                                              
                                                 nivetud        poids         
 Enseignement technique ou professionnel court       :463   Min.   :   78.08  
 Enseignement superieur y compris technique superieur:441   1st Qu.: 2221.82  
 Derniere annee d'etudes primaires                   :341   Median : 4631.19  
 1er cycle                                           :204   Mean   : 5535.61  
 2eme cycle                                          :183   3rd Qu.: 7626.53  
 (Other)                                             :256   Max.   :31092.14  
 NA's                                                :112                     
                   occup                           qualif    freres.soeurs   
 Exerce une profession:1049   Employe                 :594   Min.   : 0.000  
 Chomeur              : 134   Ouvrier qualifie        :292   1st Qu.: 1.000  
 Etudiant, eleve      :  94   Cadre                   :260   Median : 2.000  
 Retraite             : 392   Ouvrier specialise      :203   Mean   : 3.283  
 Retire des affaires  :  77   Profession intermediaire:160   3rd Qu.: 5.000  
 Au foyer             : 171   (Other)                 :144   Max.   :22.000  
 Autre inactif        :  83   NA's                    :347                   
          clso                              relig    
 Oui        : 936   Pratiquant regulier        :266  
 Non        :1037   Pratiquant occasionnel     :442  
 Ne sait pas:  27   Appartenance sans pratique :760  
                    Ni croyance ni appartenance:399  
                    Rejet                      : 93  
                    NSP ou NVPR                : 40  
                                                     
                         trav.imp           trav.satisf  hard.rock  lecture.bd
 Le plus important           : 29   Satisfaction  :480   Non:1986   Non:1953  
 Aussi important que le reste:259   Insatisfaction:117   Oui:  14   Oui:  47  
 Moins important que le reste:708   Equilibre     :451                        
 Peu important               : 52   NA's          :952                        
 NA's                        :952                                             
                                                                              
                                                                              
 peche.chasse cuisine    bricol     cinema     sport        heures.tv     
 Non:1776     Non:1119   Non:1147   Non:1174   Non:1277   Min.   : 0.000  
 Oui: 224     Oui: 881   Oui: 853   Oui: 826   Oui: 723   1st Qu.: 1.000  
                                                          Median : 2.000  
                                                          Mean   : 2.247  
                                                          3rd Qu.: 3.000  
                                                          Max.   :12.000  
                                                          NA's   :5       

Prenons par exemple le sous-ensemble des femmes retraitées

femmes_retraite <- hdv2003[hdv2003$age >= 65 & hdv2003$sexe == "Femme", ]
dim(femmes_retraite)
[1] 216  20

La même opération avec subset?

femmes_retraite <- subset(hdv2003, age >= 65 & sexe == "Femme")

2.3 dput

Pour indexer le colonnes par nom, on voudrait pouvoir copier/coller plutôt que recopier les noms, pour nous simplifier la vie et éviter des fautes de frappe.

names(df)
[1] "mots"    "nombres" "romain" 

Copier et éditer le résultat de names va être compliqué. La fonction dput est conçue dans ce but: en général, dput(x) imprime comme résultat une ligne de commande qu’on peut directement copier-coller dans le script pour reconstruire l’objet x.

dput(names(df))
c("mots", "nombres", "romain")

Copier-coller le résultat de dput dans le script, puis effacer les noms sauf ceux qui nous intéressent, nous permets d’éviter les fautes de frappe. Mais, attention aux virgules!

df[df$nombres <= 3, c("mots", "romain")] # indexation par ligne et par colonne
   mots romain
1    un      I
2  deux     II
3 trois    III
subset(df, nombres <= 3, c("mots", "romain")) # la même chose, avec subset
   mots romain
1    un      I
2  deux     II
3 trois    III
# Pour indexer une seule colonne on peut omettre la fonction `c`
df[df$nombres <= 3, "nombres"] 
[1] 1 2 3
subset(df, nombres <= 3, "nombres") 
  nombres
1       1
2       2
3       3

2.4 Sélectionner plusieurs niveaux avec %in%

subset(df, mots == "un" & mots == "trois" ) # ensemble vide!
[1] mots    nombres romain 
<0 rows> (or 0-length row.names)

Pour sélectionner plusieurs valeurs d’une variable on doit utiliser la disjonction

subset(df, mots == "un" | mots == "trois" )
   mots nombres romain
1    un       1      I
3 trois       3    III

Pour plus de deux niveaux ça devient peu lisible et long…

subset(df, mots == "un" | mots == "trois" | mots == "quatre")
    mots nombres romain
1     un       1      I
3  trois       3    III
4 quatre       4     IV

Dans ce cas il vaut mieux utiliser l’opérateur d’appartenance \(\in\): en R, %in% (voir slides sur la logique.

"un" %in% c("un","trois","quatre")
[1] TRUE
"deux" %in% c("un","trois","quatre")
[1] FALSE

Remarque: le résultat est un vecteur!

c("un", "deux") %in% c("un","trois","quatre")
[1]  TRUE FALSE

En somme on peut utiliser %in% pour sélectionner plusieurs niveaux d’une variable, par exemple:

df
    mots nombres romain
1     un       1      I
2   deux       2     II
3  trois       3    III
4 quatre       4     IV
5   cinq       5      V
subset(df, mots %in% c("un","trois","quatre"))
    mots nombres romain
1     un       1      I
3  trois       3    III
4 quatre       4     IV

2.5 Indexation et assignation d’un data.frame

Les mêmes considérations vues pour les vecteurs valent pour les data.frame, avec la différence que, dans ce cas, il ne s’agit pas de comparer la longueur de deux vecteurs, mais leur dimension, donc nombre de lignes et nombre des colonnes. On peut bien entendu assigner un vecteur de longueur n à une colonne d’un data.frame ayant n lignes.

En somme on a deux cas qui ont du sens:

  1. assignation d’un objet de même taille/dimension
  2. assignation d’un vecteur de longueur 1

Attention l’assignation d’une colonne dans un data.frame est sujète aussi au recyclage. L’un des avantages de la classe du tidyverse tibble est d’empecher ceci: tout tentatif d’assigner à une colonne d’un tibble de n ligne un vecteur de longueur différente de 1 ou n genère un erreur.

Exemples avec:

df
    mots nombres romain
1     un       1      I
2   deux       2     II
3  trois       3    III
4 quatre       4     IV
5   cinq       5      V

2.5.1 Assignation d’une nouvelle colonne

Alternative tidy: rajouter des colonnes avec dplyr::mutate.

letters # Cette variable contient les lettres minuscules
 [1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s"
[20] "t" "u" "v" "w" "x" "y" "z"
letters[3] 
[1] "c"
letters[1:nrow(df)] # On peut l'utiliser pour créer une nouvelle colonne
[1] "a" "b" "c" "d" "e"
df$lettres # La colonne n'existe pas encore
NULL
class(NULL) # `NULL` est une valeur spéciale qui représente des choses qui n'existent pas
[1] "NULL"

Pour créer une nouvelle colonne, on peut l’indexer à gauche du <-:

df$lettres <- letters[1:nrow(df)]
df 
    mots nombres romain lettres
1     un       1      I       a
2   deux       2     II       b
3  trois       3    III       c
4 quatre       4     IV       d
5   cinq       5      V       e

Assignation d’une nouvelle colonne avec un seul valeur (objet de taille 1)

df$zeros <- 0 
df
    mots nombres romain lettres zeros
1     un       1      I       a     0
2   deux       2     II       b     0
3  trois       3    III       c     0
4 quatre       4     IV       d     0
5   cinq       5      V       e     0

2.5.2 Assignation d’une partie des valeurs

Avec une indexation, on peut sélectionner d’abord un sous-ensemble des lignes et colonnes, et leur assigner une valeur de taille correspondante

df[c(1, 3, 5), "zeros"]
[1] 0 0 0
df[c(1, 3, 5), "zeros"] <- c(NA, -1, -2)
df
    mots nombres romain lettres zeros
1     un       1      I       a    NA
2   deux       2     II       b     0
3  trois       3    III       c    -1
4 quatre       4     IV       d     0
5   cinq       5      V       e    -2
df[1:3, c("mots", "nombres")]      # `3` lignes et `2` colonnes
   mots nombres
1    un       1
2  deux       2
3 trois       3
dim(df[1:3, c("mots", "nombres")]) # dimension: `3 2`
[1] 3 2

Si on construit un data.frame avec la même taille (peu importe les noms des colonnes)…

smalldf <- data.frame(m = paste("moins", c("un", "deux", "trois")),
                      n = c(-1, -2, -3), 
                      stringsAsFactors = FALSE)
smalldf
            m  n
1    moins un -1
2  moins deux -2
3 moins trois -3
dim(smalldf)
[1] 3 2
str(smalldf)
'data.frame':   3 obs. of  2 variables:
 $ m: chr  "moins un" "moins deux" "moins trois"
 $ n: num  -1 -2 -3

…on peut l’assigner à un sous-ensemble des valeurs

df[1:3, c("mots", "nombres")] <- smalldf
df
         mots nombres romain lettres zeros
1    moins un      -1      I       a    NA
2  moins deux      -2     II       b     0
3 moins trois      -3    III       c    -1
4      quatre       4     IV       d     0
5        cinq       5      V       e    -2

Pour l’assignation on ne peut pas utiliser subset.

2.5.3 Effacer une colonne

Alternative tidy: utiliser dplyr::select avec le signe - devant les noms des colonnes à effacer.

Pour éliminer une colonne, on peut lui assigner la valeur NULL

df
         mots nombres romain lettres zeros
1    moins un      -1      I       a    NA
2  moins deux      -2     II       b     0
3 moins trois      -3    III       c    -1
4      quatre       4     IV       d     0
5        cinq       5      V       e    -2
df$nombres <- NULL
df
         mots romain lettres zeros
1    moins un      I       a    NA
2  moins deux     II       b     0
3 moins trois    III       c    -1
4      quatre     IV       d     0
5        cinq      V       e    -2
df[, c("lettres", "zeros")] <- NULL
df
         mots romain
1    moins un      I
2  moins deux     II
3 moins trois    III
4      quatre     IV
5        cinq      V

3 Indexation d’une liste

dl <- list(nombres = c(0, -1, 2.2), mat = m, dat = df)
dl
$nombres
[1]  0.0 -1.0  2.2

$mat
  a b  c
A 1 5  9
B 2 6 10
C 3 7 11
D 4 8 12

$dat
         mots romain
1    moins un      I
2  moins deux     II
3 moins trois    III
4      quatre     IV
5        cinq      V

Une liste peut être indexé comme un vecteur, en utilisant [], avec une différence importante: le résultat de l’indexation est encore une liste

dl[1]
$nombres
[1]  0.0 -1.0  2.2
class(dl[1]) # c'est une liste
[1] "list"
length(dl[1]) # de longueur 1
[1] 1
names(dl[1]) # gardant les noms
[1] "nombres"
dl[c(2, 1)]
$mat
  a b  c
A 1 5  9
B 2 6 10
C 3 7 11
D 4 8 12

$nombres
[1]  0.0 -1.0  2.2
class(dl[c(2, 1)]) # c'est une liste
[1] "list"
length(dl[c(2, 1)]) # de longueur 2
[1] 2
names(dl[c(2, 1)]) # gardant les noms
[1] "mat"     "nombres"

Pour obtenir les éléments avec leur classe, il faut utiliser un double crochet:

dl[[1]]
[1]  0.0 -1.0  2.2
class(dl[[1]]) # c'est un vecteur numérique
[1] "numeric"
length(dl[[1]]) # de longueur 3
[1] 3
names(dl[[1]]) # le nom est perdu car les vecteurs n'ont pas de nom
NULL

En autre, une liste peut être indexée par nom, tout comme les colonnes des data frames:

  • avec $, permettant d’obtenir directement les éléments:
dl$nombres
[1]  0.0 -1.0  2.2
class(dl$nombres) # c'est un vecteur numérique
[1] "numeric"
length(dl$nombres) # de longueur 3
[1] 3
names(dl$nombres) # le nom est perdu car les vecteurs n'ont pas de nom
NULL
  • avec crochets, simples ou doubles, cette fois avec la même signification:
dl["nombres"]
$nombres
[1]  0.0 -1.0  2.2
class(dl["nombres"]) # c'est une liste
[1] "list"
length(dl["nombres"]) # de longueur 1
[1] 1
names(dl["nombres"]) # gardant les noms
[1] "nombres"
dl[["nombres"]]
[1]  0.0 -1.0  2.2
class(dl[["nombres"]]) # c'est un vecteur numérique
[1] "numeric"
length(dl[["nombres"]]) # de longueur 3
[1] 3
names(dl[["nombres"]]) # le nom est perdu
NULL
dl[c("mat", "nombres")]
$mat
  a b  c
A 1 5  9
B 2 6 10
C 3 7 11
D 4 8 12

$nombres
[1]  0.0 -1.0  2.2
class(dl[c("mat", "nombres")]) # c'est une liste
[1] "list"
length(dl[c("mat", "nombres")]) # de longueur 2
[1] 2
names(dl[c("mat", "nombres")]) # gardant les noms
[1] "mat"     "nombres"
dl[c(TRUE, FALSE, TRUE)]
$nombres
[1]  0.0 -1.0  2.2

$dat
         mots romain
1    moins un      I
2  moins deux     II
3 moins trois    III
4      quatre     IV
5        cinq      V
class(dl[c(TRUE, FALSE, TRUE)]) # c'est une liste
[1] "list"
length(dl[c(TRUE, FALSE, TRUE)]) # de longueur 2
[1] 2
names(dl[c(TRUE, FALSE, TRUE)]) # gardant les noms
[1] "nombres" "dat"    

Remarque: l’indexation par double crochet est limitée aux indices de longueur 1.

dl[1:2] # les deux premiers éléments de la liste, comme prévu
$nombres
[1]  0.0 -1.0  2.2

$mat
  a b  c
A 1 5  9
B 2 6 10
C 3 7 11
D 4 8 12

L’indexation pour double crochet avec indices de longueur majeure a une signification particulière, et peu intuitive: il s’agit d’une indexation «récursive»:

dl[[1:2]] # deuxième élément du premier élément de la liste
[1] -1
dl[[c(2, 6)]] # sixième élément du deuxième élément de la liste
[1] 6
dl[[c(2, 3, 1)]] # donne erreur car il n'y a pas assez de niveaux
Error in dl[[c(2, 3, 1)]]: recursive indexing failed at level 2

À éviter donc pour questions de clarté.


Retour à l’index