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 un array 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 lignes

  • cbind() 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.

\[\begin{split}x = \left(\begin{matrix} 3 & 2 \\ -1 & 1 \end{matrix}\right) \quad y = \left(\begin{matrix} 1 & 4 & 0 \\ 0 & 1 & -1 \end{matrix}\right)\end{split}\]

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