9. Tableaux multidimensionnels
En R, les tableaux multi dimensionnels sont appelés array
. Il existe 2 cas particuliers d’un array
:
le vecteur qui est un
array
de dimension 1 ;la matrice (
matrix
) qui est unarray
de dimension 2.
Un vecteur de dimensions est un vecteur d’entiers strictement positifs. Si la longueur de ce vecteur est k
, on a un array
de dimension k
. Chacune des dimensions est indexée de 1
jusqu’à la valeur donnée par le vecteur dimension.
9.1. Tableau
Les valeurs contenues dans un vecteur peuvent être structurées en array
en utilisant l’attribut dim
.
Construisons un vecteur z
de 1500 elements:
> z <- sample(seq(1,100), size=1500, replace=T)
> str(z)
int [1:1500] 75 63 34 73 65 9 79 60 37 45 ...
> z[1:18]
[1] 75 63 34 73 65 9 79 60 37 45 10 52 16 35 63 72 89 40
A la création, l’attribut dim
n’existe pas:
> attributes(z)
NULL
On peut lui affecter un vecteur dimension:
> dim(z) <- c(3,5,100)
ce qui permet de considérer z
comme un array
3 x 5 x 100:
> str(z)
int [1:3, 1:5, 1:100] 75 63 34 73 65 9 79 60 37 45 ...
> attributes(z)
$dim
[1] 3 5 100
> z[1:3,1:5,1]
[,1] [,2] [,3] [,4] [,5]
[1,] 75 73 79 45 16
[2,] 63 65 60 10 35
[3,] 34 9 37 52 63
Astuce
Pour les array
de dimension 1 et 2, les fonctions array()
et matrix()
permettent une construction plus naturelle.
Question
Observer comment les valeurs sont distribuées dans la structure multi dimensionnelle. Quel est l’indice qui varie le plus vite ? Le plus lentement ?
Si on considère un array
a
dont le vecteur dimension est c(3,4,2)
, alors il y a 3 x 4 x 2 = 24 valeurs dans a
. Ces valeurs sont stockées dans l’ordre suivant :
a[1,1,1]
a[2,1,1]
…
a[2,4,2]
a[3,4,2]
Illustrons ceci:
> a <- sample(seq(1,100), size=24)
> str(a)
int [1:24] 58 27 79 75 81 71 11 45 8 43 ...
> dim(a) <- c(3,4,2)
> a[1,1,1]
[1] 58
> a[2,1,1]
[1] 27
> a[3,1,1]
[1] 79
> a[1,2,1]
[1] 75
9.2. Indexation et sous tableaux
Chaque élément d’un array
est accessible individuellement avec une syntaxe du type:
> a[1,1,1]
[1] 58
On peut extraire des sous sections d’un array
plutôt que des éléments individuels, en passant des vecteurs à l’opérateur d’indexation plutôt que des scalaires. Si le champ est vide, la totalité des indices est utilisée sur la dimension correspondante. Par exemple, a[2,,]
est un array
4 x 2, ce qui revient à dire que son vecteur dimension est c(4,2)
. Ce serait équivalent de construire la sous section de la façon suivante:
> c(a[2,1,1], a[2,2,1], a[2,3,1], a[2,4,1], a[2,1,2], a[2,2,2], a[2,3,2], a[2,4,2])
Important
La syntaxe a[2,,]
signifie qu’on fixe le premier indice à 2
et que l’on prend toutes les valeurs du deuxième, et toutes les valeurs du troisième.
Si on s’intéresse aux valeurs que contient cette sélection (slicing):
> a[2,,]
[,1] [,2]
[1,] 27 15
[2,] 81 40
[3,] 45 31
[4,] 12 25
> a
, , 1
[,1] [,2] [,3] [,4]
[1,] 58 75 11 43
[2,] 27 81 45 12
[3,] 79 71 8 86
, , 2
[,1] [,2] [,3] [,4]
[1,] 20 67 90 80
[2,] 15 40 31 25
[3,] 85 61 36 72
> a[,,]
, , 1
[,1] [,2] [,3] [,4]
[1,] 58 75 11 43
[2,] 27 81 45 12
[3,] 79 71 8 86
, , 2
[,1] [,2] [,3] [,4]
[1,] 20 67 90 80
[2,] 15 40 31 25
[3,] 85 61 36 72
Le vecteur dimension retourné par la fonction dim()
peut être utilisé des deux cotés de l’opérateur d’assignation:
> dim(a) <- c(3,4,2) # on the left side
> d <- dim(a[2,,]) # on the right side
> d
[1] 4 2
L’utilisation d’index négatifs pour exclure (plutôt que sélectionner) est possible également pour les array
. Ici, on écarte la première colonne et la première ligne de a[,,2]
:
> a[-1,-1,2]
[,1] [,2] [,3]
[1,] 40 31 25
[2,] 61 36 72
Si un seul indice (ou un vecteur d’indices) est utilisé pour l’indexation d’un array
, on considère ce dernier comme un simple vecteur (sans multi dimension) et l’élément correspondant est retourné:
> a[1]
[1] 58
> a[c(1,2,3,4)]
[1] 58 27 79 75
9.3. Indexation par matrice
Pour l’indexation d’un array
, on vient de voir qu’on peut utiliser un vecteur d’index. On peut également passer une matrice à l’opérateur d’indexation. La matrice définit une séquence d’index. Elle doit contenir autant de colonnes que la dimension de l”array
et autant de lignes que le nombre d’élément que l’on veut extraire.
Prenons un exemple avec un array
de dimension 4 x 5:
> x <- array(1:20, dim=c(4,5)) # Generate a 4 by 5 array.
> x
[,1] [,2] [,3] [,4] [,5]
[1,] 1 5 9 13 17
[2,] 2 6 10 14 18
[3,] 3 7 11 15 19
[4,] 4 8 12 16 20
Si on veut extraire les éléments x[1,3]
, x[2,2]
et x[3,1]
, on crée la matrice d’indexation associée:
> i <- array(c(1:3,3:1), dim=c(3,2))
> i # i is a 3 by 2 index array.
[,1] [,2]
[1,] 1 3
[2,] 2 2
[3,] 3 1
Chaque ligne de la matrice correspond bien aux indices que l’on veut extraire. L’extraction est ensuite directe:
> x[i] # Extract x[1,3], x[2,2] and x[3,1]
[1] 9 6 3
On peut bien sûr utiliser l’opérateur d’indexation pour une affectation:
> x[i] <- 0 # Replace those elements by zeros.
> x
[,1] [,2] [,3] [,4] [,5]
[1,] 1 5 0 13 17
[2,] 2 0 10 14 18
[3,] 0 7 11 15 19
[4,] 4 8 12 16 20
>
Avertissement
Les indices négatifs ne sont pas permis dans les matrices d’indexation, mais les valeurs NA
et 0
le sont. Les lignes de la matrice d’indexation contenant un 0
sont ignorées. Les lignes de la matrice d’indexation contenant un NA
produisent des NA
.
Avertissement
Une matrice d’indexation doit être numérique. Si elle ne l’est pas, elle est traitée comme un vecteur d’indexation.
9.4. La fonction array()
Jusqu’à présent, les array
ont été construits par utilisation d’un attribut dim
associé à un vecteur. On peut également utiliser la fonction array()
(voir la documentation).
On l’utilise de la façon suivante:
> z <- array(data_vector, dim_vector)
Par exemple, si le vecteur h
contient 24 éléments (ou moins) on peut construire un array
z
de la façon suivante:
> z <- array(h, dim=c(3,4,2))
Si h
est exactement de dimension 24, le résultat est identique à l’instruction:
> z <- h ; dim(z) <- c(3,4,2)
Démonstration:
> h <- seq(1,24)
> z <- array(h, dim=c(3,4,2))
> z
, , 1
[,1] [,2] [,3] [,4]
[1,] 1 4 7 10
[2,] 2 5 8 11
[3,] 3 6 9 12
, , 2
[,1] [,2] [,3] [,4]
[1,] 13 16 19 22
[2,] 14 17 20 23
[3,] 15 18 21 24
Sans surprise, si h
a moins de 24 éléments, la règle de recyclage est utilisée:
> h <- seq(1,16)
> z <- array(h, dim=c(3,4,2))
> z
, , 1
[,1] [,2] [,3] [,4]
[1,] 1 4 7 10
[2,] 2 5 8 11
[3,] 3 6 9 12
, , 2
[,1] [,2] [,3] [,4]
[1,] 13 16 3 6
[2,] 14 1 4 7
[3,] 15 2 5 8
Avertissement
Essayer de forcer la dimension de h
produit une erreur.
> dim(h) <- c(3,4,2) Error in dim(h) <- c(3, 4, 2) dims [product 24] do not match the length of object [16]
Une utilisation « extrême » de la règle de recyclage permet de construire un array
rempli de 0
:
> z <- array(0, c(3,4,2))
Cette construction initialise le vecteur dimension:
> dim(z)
[1] 3 4 2
et l’instruction z[1:24]
construit un vecteur des valeurs de z
:
> z[1:24]
[1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 1 2 3 4 5 6 7 8
Un array
peut être utilisé avec les opérateurs arithmétiques et le résultat est un array
construit élément par élément:
> x <- array(1:20, dim=c(4,5))
> x
[,1] [,2] [,3] [,4] [,5]
[1,] 1 5 9 13 17
[2,] 2 6 10 14 18
[3,] 3 7 11 15 19
[4,] 4 8 12 16 20
> 2*x+1
[,1] [,2] [,3] [,4] [,5]
[1,] 3 11 19 27 35
[2,] 5 13 21 29 37
[3,] 7 15 23 31 39
[4,] 9 17 25 33 41
Important
On peut convertir un array
ou une matrix
en vecteur avec la fonction de coercition as.vector()
.
> X<-matrix(1:4, nrow=2)
> vec <- as.vector(X)
> vec
[1] 1 2 3 4
9.5. Calcul matriciel
Comme précisé plus haut, une matrice est un cas particulier d’un array
qui dans ce cas est de dimension 2. Il existe cependant plusieurs fonctions spécifiques aux matrices, ce qui justifie de traiter ce cas particulier. La fonction de transposition t()
est un exemple:
> x
[,1] [,2] [,3] [,4] [,5]
[1,] 1 5 9 13 17
[2,] 2 6 10 14 18
[3,] 3 7 11 15 19
[4,] 4 8 12 16 20
> t(x)
[,1] [,2] [,3] [,4]
[1,] 1 2 3 4
[2,] 5 6 7 8
[3,] 9 10 11 12
[4,] 13 14 15 16
[5,] 17 18 19 20
Les fonctions nrow()
(nombre de lignes) et ncol()
(nombre de colonnes) également:
> nrow(x)
[1] 4
> ncol(x)
[1] 5
9.5.1. Multiplication matricielle
Le simple opérateur de multiplication *
effectue des opérations élément par élément lorsqu’il est utilisé avec des matrices.
On peut effectue une vraie multiplication matricielle, au sens mathématique du terme, avec l’opérateur %*%
:
> x <- matrix(1:4, nrow=2)
> x
[,1] [,2]
[1,] 1 3
[2,] 2 4
> y <- matrix(5:8, nrow=2)
> y
[,1] [,2]
[1,] 5 7
[2,] 6 8
> x*y
[,1] [,2]
[1,] 5 21
[2,] 12 32
> x %*% y
[,1] [,2]
[1,] 23 31
[2,] 34 46
Si l’opération n’est pas valide, un message d’erreur est affiché:
> x <- matrix(1:6, nrow=2) > x [,1] [,2] [,3] [1,] 1 3 5 [2,] 2 4 6 > y <- matrix(7:12, nrow=3) > y [,1] [,2] [1,] 7 10 [2,] 8 11 [3,] 9 12 > x*y Error in x * y : non-conformable arrays > x %*% y [,1] [,2] [1,] 76 103 [2,] 100 136
Astuce
Extraire un vecteur colonne n’est pas toujours direct car R essaye systématiquement de structurer le résultat avec la plus petite dimension, c’est à dire un vecteur ligne.
> x[,3]
[1] 5 6
On peut passer le paramètre drop
pour obtenir le résultat souhaité.
> x[,3, drop=FALSE]
[,1]
[1,] 5
[2,] 6
La multiplication matricielle fonctionne évidemment si un des opérandes est un vecteur, pour peu que les règles de dimension soient satisfaites:
> v <- 7:9
> x %*% v
[,1]
[1,] 76
[2,] 100
9.5.2. Construction de matrices par ligne et par colonne
Les matrices peuvent être construites par concaténation avec les fonctions R core rbind()
et cbind()
:
rbind()
construit la matrice par concaténation verticale, en « ajoutant » des lignescbind()
construit la matrice par concaténation horizontale, en « ajoutant » des colonnes
Un exemple:
> x <- rbind(seq(1,5,2), seq(2,6,2))
> x
[,1] [,2] [,3]
[1,] 1 3 5
[2,] 2 4 6
> x <- cbind(1:2, 3:4, 5:6)
> x
[,1] [,2] [,3]
[1,] 1 3 5
[2,] 2 4 6
Avertissement
La règle de recyclage s’applique également avec les fonctions rbind()
et cbind()
mais ce n’est pas une bonne pratique de la laisser se mettre en oeuvre.
Les fonctions rbind()
et cbind()
peuvent prendre des matrices en argument:
> A<-matrix(c(1:15),nrow=5) > A [,1] [,2] [,3] [1,] 1 6 11 [2,] 2 7 12 [3,] 3 8 13 [4,] 4 9 14 [5,] 5 10 15 > B<-matrix(c(16:30),nrow=5) > B [,1] [,2] [,3] [1,] 16 21 26 [2,] 17 22 27 [3,] 18 23 28 [4,] 19 24 29 [5,] 20 25 30 > X<-cbind(A,B) > X [,1] [,2] [,3] [,4] [,5] [,6] [1,] 1 6 11 16 21 26 [2,] 2 7 12 17 22 27 [3,] 3 8 13 18 23 28 [4,] 4 9 14 19 24 29 [5,] 5 10 15 20 25 30 > Y<-rbind(A,B) > Y [,1] [,2] [,3] [1,] 1 6 11 [2,] 2 7 12 [3,] 3 8 13 [4,] 4 9 14 [5,] 5 10 15 [6,] 16 21 26 [7,] 17 22 27 [8,] 18 23 28 [9,] 19 24 29 [10,] 20 25 30
Important
Le résultat produit par rbind()
ou cbind()
est une matrix
. Par conséquent on peut utiliser ces fonctions pour produire un vecteur ligne ou colonne.
> x<-c(1:3)
> x
[1] 1 2 3
> rbind(x)
[,1] [,2] [,3]
x 1 2 3
> cbind(x)
x
[1,] 1
[2,] 2
[3,] 3
9.6. Exercices
Créer les matrices x
et y
.
Question
Essayer de prédire le résultat des instructions suivantes:
> 2*x
> x*x
> x%*%x
> x%*%y
> t(y)
> solve(x)
Vérifier dans la console
Question
Essayer de prédire le résultat des instructions suivantes:
> x[1,]
> x[2,]
> x[,2]
> y[1,2]
> y[,2:3]
Vérifier dans la console
9.7. Application : population urbaine
Pour cet exercice, utiliser les données déjà utilisées dans l’exercice d’application sur les vecteurs et stockées dans le fichier population.RData
pour construire la matrice pop
suivante. En deux étapes :
- la première aggrège les données numériques et formate la matrice en 8 lignes (les années) et 20 colonnes (les villes) ;
- la seconde définit un label pour chacune des lignes et chacune des colonnes.
Angers |
Bordeaux |
Toulouse |
||
1962 |
115273 |
278403 |
323724 |
|
2012 |
149017 |
241287 |
453317 |
Utiliser la fonction array()
pour aggréger les valeurs numériques.
A cette étape les labels des lignes et des colonnes (obtenus avec les fonctions rownames()
et colnames()
) ne sont pas affectés. Vérifiez le.
Utiliser maintenant les vecteurs cities
et years
pour affecter les labels des lignes et des colonnes et obtenir la matrice demandée.
9.7.1. Exploration
Utiliser la fonction summary()
pour afficher les statistiques élémentaires.
Quelles sont les villes ayant plus de 300000 habitants en 1962 ?
Pour ces mêmes villes, combien chacune a t-elle d’habitants en 1968 ?
Calculer le flux migratoire entre 1962 et 1968 pour chacune des villes et identifier celles pour lesquelles il est négatif
Identifier celles qui ont franchi la barre des 300.000 habitants entre 1962 et 1968
Calculer l’augmentation de population dans les 20 plus grandes villes entre 1962 et 2012