TD 2. Structures de base

Cours sur les vecteurs et cours sur les matrices

L’objectif de ce cours est de pouvoir définir et manipuler les structures propres à l’environnement R :

  • Le vecteur
  • La matrice
  • Le data frame
  • La liste
  • L’objet spécial NA

Les Vecteurs

En R, tout est un vecteur. Contrairement à certains autres langages de programmation, il n’y a pas de notion de scalaire (un simple nombre). Un scalaire est simplement un vecteur de longueur 1.

Règle : Dans un vecteur simple, tous les éléments doivent être du même format : numeric, character, factor ou logical.

Définir un vecteur

Pour définir un vecteur, le plus rapide et le plus simple est de passer par la fonction c() (pour “concaténation”).

Exemple :

# Définition d'un vecteur de taille 5 contenant des notes d'élèves
notes_histoire <- c(11, 20, 8, 17, 15)

# Définition d'un vecteur de taille 5 contenant des prénoms
prenom <- c('Damien', 'Estelle', 'Julie', 'Maxime', 'Pierre')
prenom2 <- c('Pierre', 'Julie', 'Damien', 'Estelle', 'Maxime')

Numeric vs Character

Attention ! : Saisir un chiffre ne signifie pas la création automatique un objet numeric. La preuve :

# Définition d'un vecteur contenant des chiffre enregistrés au format numeric
notes_maths <- c('14', '13', '11', '9', '4')

# Affichage de l'objet pour voir la différence entre character et numeric
notes_maths
## [1] "14" "13" "11" "9"  "4"

Les fonctions pour convertir

Aussi, il est possible de convertir les modes de vos objets :

  • as.numeric() : vous permet de convertir votre enregistrement au format numeric. Fonction à n’utiliser que sur des enregistrements au format character pour éviter certains problèmes.
  • as.character() : vous permet de convertir n’importe quel enregistrement au format character.
  • as.factor() : conversion d’un objet au format factor.

Connaître le format de son objet

Il existe une fonction qui vous permet rapidement de connaître le format de votre objet : la fonction class().

# Rechercher le mode de l'objet notes_maths
class(notes_maths)
## [1] "character"

Ici, character signifie que nous manipulons un vecteur dont tous les éléments sont au format character. Nous allons changer ce format, qui ne convient pas.

Effectuer une conversion

# Conversion de l'objet notes_maths au format numeric
notes_maths <- as.numeric(notes_maths)

# Appelons l'objet note_maths pour vérifier
notes_maths
## [1] 14 13 11  9  4

Manipuler un vecteur par indiçage

Les éléments de votre vecteur peuvent tous être utilisés de manière individuelle ou partielle. Nous parlons alors d’indiçage. Pour entrer dans l’objet, il faut utiliser les [ ]. Par exemple, si vous souhaitez connaître la note en mathématiques du deuxième élève :

# Récupérer la note en maths du deuxième élève
notes_maths[2]
## [1] 13
# Récupérer le vecteur complet sans la troisième note
notes_maths[-3]
## [1] 14 13  9  4

Manipuler un vecteur à partir des valeurs d’un autre vecteur

Il est aussi possible d’extraire des éléments d’un vecteur selon les valeurs d’un autre vecteur. Attention ! : Les valeurs du second vecteur doivent être compatibles avec celles du premier.

# Définir le vecteur élèves contenant les indices des élèves
eleves <- c(1, 2, 3) 

# Pour écrire une suite de nombre il est possible d'écrire :
eleves <- 1:3

# Récupérer les notes des élèves définis ci-avant
notes_maths[eleves]
## [1] 14 13 11
#Récupérer les notes du Troisième, du Premier et du Quatrième élèves
notes_maths[c(3, 1, 4)]
## [1] 11 14  9

Nommer les éléments d’un vecteur

Au travers de ces exemples, nous comprenons que l’indiçage fonctionne à partir de la position de l’élément recherché. Ce n’est pas la seule possibilité ! Il est possible de passer par le nom de l’élément. Aussi, il est possible (et même recommandé dans les codes de grande taille) de donner un label (un nom) à chacun des éléments d’un vecteur. Pour cela, nous utilisons la fonction names(). Ces labels font alors partie des attributs du vecteur.

# Associer les notes à un nom d'élève
names(notes_maths) <- prenom
names(notes_histoire) <- prenom2

Maintenant, si vous appelez l’objet note_maths ou notes_histoire, vous verrez au dessus de chaque note, le nom de l’élève associé.

Manipuler un vecteur avec les noms des éléments

Pourquoi nommer les éléments du vecteur? Cela permet une référence plus flexible. Si vous souhaitez connaître la note de Julie par la méthode de l’indiçage, il vous faut savoir la position de Julie dans le vecteur. Ici, dans le vecteur notes_maths, il s’agit de la position 3. Or, dans les codes en milieu professionnel, vous avez des milliers d’éléments dans vos vecteurs. Maintenant, la seule chose que vous avez besoin de savoir, c’est qu’un de ces éléments se nomme Julie, peu importe sa position !

# Appeler la note en maths de Julie
notes_maths['Julie']
## Julie 
##    11
# Appeler la note en Histoire de Julie
notes_histoire['Julie']
## Julie 
##    20

De même, il est possible de récupérer des éléments du vecteur à partir des noms données aux éléments.

# Récupérer les notes de maths de Damien, Estelle et Maxime
notes_maths[c('Damien', 'Estelle', 'Maxime')]
##  Damien Estelle  Maxime 
##      14      13       9

Connaître la longueur d’un vecteur

Le dernier attribut propre au vecteur est sa longueur. Pour connaître la valeur de cette attribut, il suffit d’utiliser la fonction length().

# Obtenir la longeur du vecteur notes_maths
length(notes_maths)
## [1] 5

Les Matrices

R est langage dédié aux calculs et à la manipulation des données. Ces calculs sont majoritairement utilisés sur des données de grandes dimensions. Si le vecteur est caractérisé par sa longueur, la matrice est caractérisé par sa dimension, valant 2. Nous avons des informations en horizontal (les lignes) et en vertical (les colonnes).

Règle : Tous les éléments de la matrice doivent être de même format !

La fonction ‘matrix()’

Pour manipuler des matrices, il convient d’utiliser la fonction matrix(). Celle-ci possède différents arguments de bases, que nous allons détailler. Pour que nous les étudions ensemble, veuillez saisir ‘matrix’ dans la fenêtre Help.

  • data = : les données à enregistrer dans la matrice
  • nrow = : le nombre de lignes à créer
  • ncol = : le nombre de colonnes à créer
  • byrow = : (Saisir TRUE ou FALSE) R fonctionne en mode ‘colonne’. Cela signifie qu’il va d’abord remplir la première colonne, puis la deuxième, etc… Dans notre conception naturelle, notre cerveau complète ligne par ligne. C’est pourquoi byrow = T permet de dire à R de compléter ligne après ligne.
  • dimnames = : Nommer les dimensions de la matrice (il y en a 2). Que désigne les lignes? les colonnes? Nous pourrons traiter cette option une fois la partie de cours sur les listes traitée.

Création d’une première matrice

# Définition d'une simple matrice : 1 colonne, plusieurs lignes
mat_test <- matrix(notes_maths)

# Appeler mat_test pour vérifier ce que nous avons défini
mat_test
##      [,1]
## [1,]   14
## [2,]   13
## [3,]   11
## [4,]    9
## [5,]    4

Dans cet exemple, nous voyons que les options par défaut de la fonction matrix() ont créé une matrice de dimension 5 lignes, 1 colonne. Pour vérifier cela, utilisons la fonction dim().

# Obtenir les dimensions de la matrice
dim(mat_test)
## [1] 5 1

La fonction dim() renvoie un vecteur à plusieurs éléments. Le premier indique ici le nombre de lignes (5) et le second, le nombre de colonnes (1). Nous retrouvons la caractéristique ‘colonne’ du langage R.

Compiler plusieurs vecteurs en un seul

Nous allons à présent reprendre nos deux vecteurs de notes et construire une matrice unique contenant toutes ces valeurs.

Attention ! : Nos deux vecteurs de notes n’étaient pas rangés selon le même ordre des noms d’élèves : notes_maths était ordonné selon le vecteur prenom et notes_histoire selon prenom2.

# Rassembler dans un même vecteur toutes les notes
notes <- c(notes_maths, notes_histoire[prenom])

Cette manipulation permet de créer un vecteur unique contenant d’abord les valeurs de notes_maths puis de notes_histoire. Notons que nous sommes entrés dans le second vecteur de notes pour y sélectionner les valeurs selon l’ordre des prénoms du premier vecteur de notes.

Enregistrer des valeurs dans une matrice

Maintenant, nous allons enregistrer les valeurs de ce long vecteur dans une matrice.

# Définition d'une matrice contenant toutes les notes
mat_notes <- matrix(data = notes,
                    nrow = 2, # Il y aura 1 ligne par matière
                    ncol = 5, # Il y aura 1 colonne par élève
                    byrow = TRUE) # Nous saisissons ligne par ligne

# Afficher pour vérifier
mat_notes
##      [,1] [,2] [,3] [,4] [,5]
## [1,]   14   13   11    9    4
## [2,]    8   17   20   15   11

Renommer les dimensions d’une matrice

Ici, la matrice précédemment créée est brute. Les éléments définis n’ont pas de nom. Nous allons ainsi définir les noms de lignes et de colonnes.

# Nommer les colonnes
colnames(mat_notes) <- prenom

# Nommer les lignes
rownames(mat_notes) <- c('Maths', 'Histoire')

# Afficher le résultat
mat_notes
##          Damien Estelle Julie Maxime Pierre
## Maths        14      13    11      9      4
## Histoire      8      17    20     15     11

Transposer une matrice

La convention veut que les variables soient positionnées en colonnes et les individus en lignes. Selon vous cette matrice respecte-elle la convention ?

Rassurez-vous, tout n’est pas à refaire ! Souvenez-vous de vos cours de maths sur les matrices. Nous allons effectuer une transposé.

# Application de la fonction transposé
t_mat_notes <- t(mat_notes)

# Afficher ce qui a été défini
t_mat_notes
##         Maths Histoire
## Damien     14        8
## Estelle    13       17
## Julie      11       20
## Maxime      9       15
## Pierre      4       11

Tout comme pour les vecteurs, il est possible d’entrer et de se déplacer dans une matrice. Pour obtenir un élément d’une matrice, il est possible d’indiquer la position de l’élément en indiquant dans le [] d’abord la coordonnée ligne, puis la coordonnée colonne.

# Extraire la Note du Troisième élève dans la deuxième matière
t_mat_notes[3, 2]
## [1] 20
# Extraire toute la troisième ligne
t_mat_notes[3, ]
##    Maths Histoire 
##       11       20
# Extraire toute la première colonne
t_mat_notes[, 1]
##  Damien Estelle   Julie  Maxime  Pierre 
##      14      13      11       9       4

De cette façon, il est tout à fait possible de redéfinir une valeur. Imaginons que nous avons oublié des points sur la note du deuxième étudiant dans la première matière.

# Rappel : Ancienne valeur
t_mat_notes[2, 1]
## [1] 13
# Changer une valeur à partir de l'indiçage
t_mat_notes[2, 1] <- 15

# Afficher nouvelle valeur
t_mat_notes[2, 1]
## [1] 15

Il est possible d’extraire plusieurs éléments (plusieurs élèves et/ou plusieurs matières). Le résultat sera alors une sous-matrice. C’est-à-dire une matrice de dimensions inférieures construite à partir des éléments de la première matrice. Par conséquent, les noms donnés à la matrice mère seront appliqués à la matrice fille

# Extraire une sous-matrice à partir de l'indicage
t_mat_notes[c(1,4), c(1,2)]
##        Maths Histoire
## Damien    14        8
## Maxime     9       15

Comme pour les vecteurs, il est possible d’extraire des éléments sans connaître les positions de ces derniers, juste à partir des noms donnés. Il est même possible de combiner les deux (indices + noms). Pour illustrer cela, nous allons extraire la même sous-matrice que précédemment mais avec les noms de lignes et les indices de colonnes.

# Extraire les notes de Julie - Rappel : les élèves sont en lignes dans t_mat_notes
t_mat_notes['Julie', ]
##    Maths Histoire 
##       11       20
# Extraire les résulats en Maths - Rappel : les matières sont en colonnes dans t_mat_notes
t_mat_notes[, 'Maths']
##  Damien Estelle   Julie  Maxime  Pierre 
##      14      15      11       9       4
# Extraire une sous-matrice à partir des noms d'éléments d'une première matrice
t_mat_notes[c('Damien', 'Maxime'), c(1, 2)]
##        Maths Histoire
## Damien    14        8
## Maxime     9       15

Nous avons bien produit la même matrice fille.

Ajouter des lignes

Que faire si l’on souhaite rajouter un élève (ligne) ou une matière (colonne)?

Il existe en effet deux fonctions, rbind() et cbind(), qui permettent respectivement d’ajouter des lignes (row) et des colonnes (column).

# Rajouter les notes en Maths (18/20) et en Histoire (12/20) de Léa
Lea <- c(18, 12)
t_mat_notes <- rbind(t_mat_notes, Lea)

# Afficher la nouvelle matrice
t_mat_notes
##         Maths Histoire
## Damien     14        8
## Estelle    15       17
## Julie      11       20
## Maxime      9       15
## Pierre      4       11
## Lea        18       12

Vous pouvez constater qu’en ayant défini au préalable le vecteur Lea contenant les notes à ajouter, R a automatiquement utilisé le nom du vecteur comme nom de ligne.

Attention ! : Ceci est uniquement dû au fait d’avoir défini dans R des noms de lignes, via la fonction rownames().

Ajouter de colonnes

De la même façon, nous allons rajouter des notes d’Anglais en créant une nouvelle colonne.

# Ajout d'une colonne contenant les notes des étudiants en Anglais
Anglais <- c(20, 12, 9, 13, 7, 16)
t_mat_notes <- cbind(t_mat_notes, Anglais)

# Afficher le résultat
t_mat_notes
##         Maths Histoire Anglais
## Damien     14        8      20
## Estelle    15       17      12
## Julie      11       20       9
## Maxime      9       15      13
## Pierre      4       11       7
## Lea        18       12      16

Attention ! : Les dimensions des éléments ajoutés doivent être compatibles avec la matrice de base. Nous allons voir un exemple d’erreur de compatibilité ensemble, en ajoutant un élève n’ayant que deux notes.

Les problèmes de dimensions

# Création d'une erreur 
Paul <- c(13, 15)
rbind(t_mat_notes, Paul)
## Warning in rbind(t_mat_notes, Paul): number of columns of result is not a
## multiple of vector length (arg 2)
##         Maths Histoire Anglais
## Damien     14        8      20
## Estelle    15       17      12
## Julie      11       20       9
## Maxime      9       15      13
## Pierre      4       11       7
## Lea        18       12      16
## Paul       13       15      13

Nous voyons ici un message d’errerur apparaitre en rouge dans la console R : number of columns of result is not a multiple of vector length (arg 2).

En effet, il est demandé à R d’ajouter deux éléments dans un objet destiné à en accueillir trois. Il remplit par défaut en repartant du début du vecteur.

Que faire si Paul a bel et bien été absent lors de l’évaluation d’Hitoire?

Insérer une valeur manquante

Parmi les variables définies dans le langage de base de R, nous avons le NA, pour not available. La donnée n’est pas disponible.

# Enregistrer les 
Paul <- c(13, NA, 15)
t_mat_notes <- rbind(t_mat_notes, Paul)

# Afficher le résultat
t_mat_notes
##         Maths Histoire Anglais
## Damien     14        8      20
## Estelle    15       17      12
## Julie      11       20       9
## Maxime      9       15      13
## Pierre      4       11       7
## Lea        18       12      16
## Paul       13       NA      15

Nous constatons que, dans la nouvelle matrice, Paul a bien été considéré comme absent.

Les Data Frame

Après avoir vu les vecteurs et les matrices, nous allons voir une autre façon de ranger les données : le data frame.

Le data frame est l’objet qui se rapproche le plus des tableurs EXCEL. Visuellement, il ressemble beaucoup à une matrice mais présente l’avantage de pouvoir contenir des colonnes de modes différents. Nous pouvons stocker dans une colonne des numeric puis dans une autre des character et dans une dernière des boolean (TRUE ou FALSE).

La fonction ‘data.frame()’

Un data frame est défini à partir de la fonction data.frame().

Exemple :

# Définir un data frame de base
df_test <- data.frame(c(1:5), c(6:10))


# Appeler le résultat
df_test
##   c.1.5. c.6.10.
## 1      1       6
## 2      2       7
## 3      3       8
## 4      4       9
## 5      5      10

Nous retrouvons encore une fois la logique ‘colonne’ de R. Le premier vecteur a été enregistré comme une première colonne. De même pour le second.

R est rarement utilisé pour construire des data frames à partir de rien. L’utilité des data frames réside surtout dans la manipulation de données structurées. Par exemple, la matrice que nous avons construit dans la section est une donnée structurée. Il semble intéressant de la manipuler, voire de la compléter en la convertissant en data frame.

Attention ! : Si une matrice pourra toujours être convertie en data frame, la réciproque est fausse ! Seuls les data frames contenant des données au format unique pourront être convertis en matrice.

Convertir des données en ‘data.frame’

Pour réaliser cette conversion, il est nécessaire d’utiliser la fonction as.data.frame(). Les données à convertir sont souvent des vecteurs ou des matrices.

# Conversion de la matrice t_mat_notes en data frame
df_notes <- as.data.frame(t_mat_notes)

Manipuler les éléments d’un data frame

Comme tous les objets R, nous pouvons les visualiser dans une fenêtre de l’environnement RStudio à partir de la fonction View().

Toutes les manipulations vues pour les matrices avec [] sont valables pour les data frames, que cela soit avec les indices ou avec les noms des éléments.

# Extraire la première ligne du data frame
df_notes[1, ]
##        Maths Histoire Anglais
## Damien    14        8      20
# Extraire la note en Maths de Pierre
df_notes['Pierre', 'Maths']
## [1] 4

Le raccourci $

Le data frame offre un raccourci pour manipuler les vecteurs colonnes, le $.

# Extraire les notes d'Histoire 
df_notes$Histoire
## [1]  8 17 20 15 11 12 NA

Le résultat est un vecteur brut dont les éléments ne sont pas nommés.

Remarque ! : R est un langage orienté data et statistiques. Aussi, nous allons nous efforcer dè à présent d’utiliser le langage associé en ne parlant plus de colonnes mais de variables, ni de lignes mais d’individus. Cette remarque n’est pas posée par hasard, nous avons ici un jeu de données contenant des individus (élèves) décrits par plusieurs variables (les matières). Nous allons créer et enregistrer de nouvelles variables.

Construire une nouvelle variable

Nous avons vu que l’intérêt du data frame était de pouvoir réunir dans une même table des informations ayant des formats différents. Créons donc une variable character.

# Ajouter la filière des élèves
df_notes$Filiere <- c('ES', 'L', 'S', 'L', 'S', 'ES', 'L')

En regardant le résultat dans la fenêtre graphique de RStudio, nous observons que la variable a été ajoutée. Aussi, nous remarquon que l’alignement est différent par rapport aux matières. Les numeric sont alignés à droite et le reste le character à gauche. Aussi, en passant le pointeur de la souris sur le nom de la colonne, un message apparaît pour indiquer le format de la variable.

Nous commençons à apercevoir l’ergonomie apportée par les données structurées en data frames.

Questionner les données du data frame

Le raccourci $ apporte beaucoup de souplesse dès qu’il s’agit de faire des recherches approfondies du jeu de données. Par exemple : Comme ne selectionner que les étudiants venant de L.

# Extraire uniquement les élèves venant de L
df_notes[df_notes$Filiere == 'L', ]
##         Maths Histoire Anglais Filiere
## Estelle    15       17      12       L
## Maxime      9       15      13       L
## Paul       13       NA      15       L

Nous aurons l’occasion de passer toute une séance sur la manipulation des jeux de données. Nous pourrons approfondir ces avantages à ce moment-là. L’important, ici, était surtout de présenter l’objet data frame.

Les Listes

Le dernier objet à connaître, et que nous utiliserons peu dans ce cours introductif - voire pas du tout - est la liste. Nous la construisons à partir de la fonction list().

Il sagit d’un mode de stockage général et polyvalent offert par le langage R. Il est très utilisé dans la construction d’algorithmes complexes qui nécessitent de stocker au sein d’un même objet des éléments de mode différents (data frames, vecteurs, matrices, voire même d’autres listes). Pour mieux comprendre, imaginez qu’il s’agit d’une bibliothèque et que vous mettez sur chaque étagère des livres, journaux, magazines, voire de simples feuilles de cours.

Définir une liste

Nous nous limiterons à cet exemple :

# Construire une liste
list_notes <- list(notes_histoire, notes_maths, t_mat_notes, df_notes)

Longeur d’une liste

Une liste se caractérise par sa longueur, ici 4.

# Longueur de la liste
length(list_notes)
## [1] 4

Chaque argument de la fonction list() devient un objet de la liste. Ici, les deux premiers objets sont des vecteurs (numeric), le troisième est une matrice (matrix) et le quatrième est un data frame (dataframe).

Manipuler une liste

Comme pour les vecteurs, matrices et data frames, on entre dans une liste avec le []. La seule différence est que le résultat de cet appel est unn objet. Il est alors possible d’entrer dans l’objet appelé.

# Extraire le premier objet d'une liste
list_notes[1]
## [[1]]
##  Pierre   Julie  Damien Estelle  Maxime 
##      11      20       8      17      15

Sur ce premier essai, nous observons que R a renvoyé l’intégralité du premier objet, comme nous l’avons demandé. Que faire si l’on souhaite connaitre la note de Damien dans cette même matière sans repasser par le vecteur d’origine? On entre dans le résultat avec [] toujours.

# Extraire la note de Damien : troisième élément du premier objet de la liste
list_notes[[1]][3]
## Damien 
##      8
list_notes[[1]]['Damien']
## Damien 
##      8

Si la liste est une bibliothèque, alors le premier objet de la liste correspond à la première étagère et les éléments du l’objet sont les livres sur l’étagère. Pour indiquer à R que nous descendons, nous devons bloquer l’objet dans lequel nous souhaitons entrer. Pour cela, les [] deviennent des [[]]. Derrière, nous pouvons appeler les éléments de l’objet soit par indiçage, soit en utilisant le nom de l’élément, comme vu jusqu’à maintenant.

C’est a priori tout ce que nous verrons sur les listes dans ce cours introductif.