Parcours
/
R
R
/
Programme
/
Matrices et tableaux
Ma

Matrices et tableaux en R

{one: "%{count} exercice", many: "%{count} d'exercices", other: "%{count} exercices"}

À propos de Matrices et tableaux

Quand on manipule des collections de données atomiques homogènes (par exemple toutes numériques ou toutes de type caractère), les structures suivantes sont disponibles en R :

  • vector pour des données à une dimension, auxquelles on accède avec un seul indice.
  • matrix pour des données à deux dimensions, rectangulaires (pas irrégulières).
  • array pour le cas général à N dimensions. Une matrix n'est qu'un array avec N=2.
Note

Pour les programmeurs Python : cette section peut te sembler familière.

Toute ressemblance avec NumPy n'est pas fortuite (mais R est un peu plus ancien et intègre tout cela dans le langage de base).

Matrices

Une matrice n'est essentiellement qu'un vecteur, auquel s'ajoutent un nombre de lignes et de colonnes qui définissent sa forme.

Il existe une fonction matrix() pour les créer :

# data already available 
matrix(1:6, nrow=2, ncol=3)
#>      [,1] [,2] [,3]
#> [1,]    1    3    5
#> [2,]    2    4    6

# empty matrix to be filled in later
matrix(nrow=2, ncol=3)
#>      [,1] [,2] [,3]
#> [1,]   NA   NA   NA
#> [2,]   NA   NA   NA

Note que les données sont stockées en ordre en colonnes d'abord (c'est-à-dire de haut en bas, puis de gauche à droite). C'est le même ordre qu'en Fortran, Matlab et Julia, mais il diffère de celui de C/C++ et de NumPy.

Pour trouver les dimensions d'une matrice, utilise dim() pour obtenir à la fois le nombre de lignes et de colonnes, ou nrow() et ncol() pour obtenir une seule dimension. Utiliser length() renvoie simplement la longueur du vecteur sous-jacent : le nombre total d'éléments (6 dans l'exemple ci-dessus).

D'autres fonctions produisent une matrice en sortie, par exemple str_split_fixed().

library(stringr)

str_split_fixed(c("split some words", "and some more"), " ", 3)
#>      [,1]    [,2]   [,3]   
#> [1,] "split" "some" "words"
#> [2,] "and"   "some" "more" 

Indexation des matrices

Cela fonctionne exactement comme l'indexation des vecteurs, à ceci près qu'il faut préciser la ou les lignes et la ou les colonnes, dans cet ordre. Laisse une dimension vide pour obtenir la ligne ou la colonne entière.

En reprenant la même matrice que ci-dessus :

m <- matrix(1:6, nrow=2, ncol=3)
m
#>      [,1] [,2] [,3]
#> [1,]    1    3    5
#> [2,]    2    4    6

m[2, 3]    # row 2, col 3
#> [1] 6

m[1:2, 3]  # rows 1:2, col 3
#> [1] 5 6

 m[2, ]    # all of row 2
#> [1] 2 4 6

Note qu'un sous-ensemble réduit à une seule ligne ou à une seule colonne est renvoyé sous forme de vecteur, et non de matrice.

class(m)
#> [1] "matrix" "array" 
class(m[2,])
#> [1] "integer"

Ce type de dimension reduction n'est pas toujours souhaité, en particulier dans un enchaînement de fonctions qui attend des matrices en entrée.

Préciser drop=FALSE garantit d'obtenir une matrice en valeur de retour à partir d'une entrée matricielle. Note qu'une matrice avec une seule ligne ou une seule colonne n'est pas du même type qu'un vecteur.

m[2, , drop = FALSE]
#>      [,1] [,2] [,3]
#> [1,]    2    4    6

La fonction as.matrix() convertit un vecteur en une matrice à une seule colonne.

as.matrix(1:3)
#>      [,1]
#> [1,]    1
#> [2,]    2
#> [3,]    3

Comme pour les vecteurs, on peut modifier des portions d'une matrice simplement en leur affectant de nouvelles valeurs.

m[, 2] <- c(10, 20)
m
#>      [,1] [,2] [,3]
#> [1,]    1   10    5
#> [2,]    2   20    6

Ajout et suppression de lignes et de colonnes

Avec les vecteurs, la fonction c() suffisait pour insérer et ajouter des données. Les matrices ont une structure plus riche, il faut donc être plus précis sur ce que l'on veut.

Les fonctions rbind() et cbind() rattachent une ou plusieurs lignes ou colonnes à la matrice.

m
#>      [,1] [,2] [,3]
#> [1,]    1    3    5
#> [2,]    2    4    6
m2 <- matrix(7:9, nrow = 1, ncol = 3)

rbind(m, m2)
#>      [,1] [,2] [,3]
#> [1,]    1    3    5
#> [2,]    2    4    6
#> [3,]    7    8    9

Supprimer une ligne ou une colonne fait appel à l'indexation négative, comme pour les vecteurs.

m
#>      [,1] [,2] [,3]
#> [1,]    1    3    5
#> [2,]    2    4    6

m[, -2]
     #> [,1] [,2]
#> [1,]    1    5
#> [2,]    2    6

Filtrage et fonctions

Toutes les fonctions qui s'appliquent aux vecteurs fonctionnent aussi sur les matrices, élément par élément. Cependant, elles peuvent perdre la structure de matrice et renvoyer un vecteur.

sqrt(m)
#>          [,1]     [,2]     [,3]
#> [1,] 1.000000 1.732051 2.236068
#> [2,] 1.414214 2.000000 2.449490

sum(m)
#> [1] 21

m[m > 3]
#> [1] 4 5 6

Pour les matrices, les fonctions colSums() et rowSums() peuvent aussi être utiles.

La réduction de dimension est voulue ici, donc les valeurs de retour sont des vecteurs.

# m as defined above
rowSums(m)
#> [1]  9 12
colSums(m)
#> [1]  3  7 11

De même, il existe des fonctions rowMeans() et colMeans() pour calculer des moyennes.

Fonctions générales sur les matrices

Dans d'autres concepts, on a vu que les bibliothèques du Tidyverse peuvent simplifier les opérations sur les vecteurs et les listes (purrr) ou les dataframes (dplyr).

Cependant, les matrices et les tableaux sont en grande partie hors de portée des entrées du Tidyverse (du moins pour l'instant).

La fonction apply() fait partie de Base R et reste le meilleur outil pour un contrôle plus fin de la programmation matricielle.

Les paramètres, dans l'ordre, sont :

  1. La matrice en entrée.
  2. La dimension sur laquelle opérer (1 pour les lignes, 2 pour les colonnes).
  3. La fonction à appliquer.
  4. Éventuellement, des paramètres supplémentaires pour la fonction.

La fonction rowMeans() existe dans Base R, mais on pourrait aussi la reproduire avec apply() et mean().

randm <- sample(1:10, 16, replace = TRUE) |> matrix(4, 4)
randm
#>      [,1] [,2] [,3] [,4]
#> [1,]    4    9    8    3
#> [2,]    6    8    5    5
#> [3,]    4    2    1    2
#> [4,]    4    2    5    4

apply(randm, 1, mean)
#> [1] 6.00 6.00 2.25 3.75

rowMeans(randm)
#> [1] 6.00 6.00 2.25 3.75

Algèbre linéaire

Note

Cette section suppose un niveau de connaissances mathématiques qui dépasse le cadre d'Exercism.

N'hésite pas à la passer si tu n'y comprends rien.

Cependant, R est un langage de data science, et beaucoup de ses utilisateurs ont un bagage en mathématiques appliquées et en statistiques. Ce résumé succinct pourra en intéresser certains.

Le R moderne est « évidemment » un langage de data science : conçu pour travailler principalement avec des données tabulaires, et apprécié d'utilisateurs comme les statisticiens, les épidémiologistes et les spécialistes en sciences sociales. Le principal concurrent dans ce domaine est la pile scientifique Python, avec des paquets comme Pandas ou Polars.

Cependant, dans les décennies précédentes, R était aussi considéré comme un langage d'ingénierie potentiel, en concurrence directe avec Matlab.

Aussi surprenant que cela paraisse, Base R prend toujours en charge toute une gamme d'opérations d'algèbre linéaire sur les vecteurs et les matrices. On ne sait pas trop quelle place ces opérations occupent aujourd'hui dans le code des utilisateurs, mais ces techniques restent très importantes en interne pour implémenter les fonctions statistiques.

L'algèbre linéaire en R est encore enseignée dans certains cours universitaires, comme ce cours d'économétrie. Pour les mathématiques (et non la programmation), on trouve un bon guide sur 3Blue1Brown.

Advanced

La présentation la plus complète de l'algèbre linéaire sur Exercism se trouve dans le programme Julia (les mêmes auteurs que le programme R). Nous pensons que Julia (ou Python là où le module numpy.linalg est disponible) serait aujourd'hui un langage plus adapté que R pour un travail sérieux d'algèbre linéaire.

Diagonales

La diagonale principale d'une matrice carrée (N x N)) A va du coin en haut à gauche (A[1,1]) au coin en bas à droite (A[N,N]).

La fonction diag() se comporte différemment selon l'argument qu'on lui passe.

Avec un vecteur v, diag(v) crée une matrice diagonale.

diag(1:3)
#>      [,1] [,2] [,3]
#> [1,]    1    0    0
#> [2,]    0    2    0
#> [3,]    0    0    3

Avec une matrice carrée A, diag(A) renvoie les éléments de la diagonale principale sous forme de vecteur.

A
#>      [,1] [,2] [,3]
#> [1,]   10    5    2
#> [2,]    9    3    7
#> [3,]    8   10    4
diag(A)
#> [1] 10  3  4

Avec un seul entier k, diag(k) crée une matrice identité k × k : ce à quoi tu ne t'attendais peut-être pas.

Même les ouvrages sérieux sur R ont tendance à ajouter des commentaires du genre « Va comprendre » à ce stade.

diag(3)
#>      [,1] [,2] [,3]
#> [1,]    1    0    0
#> [2,]    0    1    0
#> [3,]    0    0    1

Transposition et inversion

Pour échanger les lignes et les colonnes, la fonction t() transpose n'importe quelle matrice.

m
#>      [,1] [,2] [,3]
#> [1,]    1    3    5
#> [2,]    2    4    6
t(m)
#>      [,1] [,2]
#> [1,]    1    2
#> [2,]    3    4
#> [3,]    5    6

Pour une matrice à valeurs complexes, le résultat n'est pas le conjugué complexe. Utilise Conj(t(A)) pour obtenir la transposée conjuguée (adjointe) de la matrice A.

Pour les tableaux de dimension supérieure, aperm() permute les dimensions.

Pour l'inverse d'une matrice carrée A, on peut utiliser solve(A). Certains trouvent cette syntaxe... inattendue.

Multiplier une matrice par son inverse donne la matrice identité (aux très petites erreurs d'arrondi près).

A <- matrix(sample(1:10, 9, replace = TRUE), 3, 3)
A
#>      [,1] [,2] [,3]
#> [1,]   10    5    2
#> [2,]    9    3    7
#> [3,]    8   10    4

solve(A)
#>             [,1]        [,2]        [,3]
#> [1,]  0.16666667  0.00000000 -0.08333333
#> [2,] -0.05747126 -0.06896552  0.14942529
#> [3,] -0.18965517  0.17241379  0.04310345

A %*% solve(A) |> round(digits = 10)
     [,1] [,2] [,3]
#> [1,]    1    0    0
#> [2,]    0    1    0
#> [3,]    0    0    1

Multiplication

Les opérateurs arithmétiques habituels comme +, -, *, ^ s'appliquent aux matrices élément par élément. En cas d'incompatibilité de dimensions, les règles habituelles de recyclage s'appliquent.

m
#>      [,1] [,2] [,3]
#> [1,]    1    3    5
#> [2,]    2    4    6

m * m
#>      [,1] [,2] [,3]
#> [1,]    1    9   25
#> [2,]    4   16   36

# same calculation, different syntax
m ^ 2
#>      [,1] [,2] [,3]
#> [1,]    1    9   25
#> [2,]    4   16   36

Expliquer les autres types de multiplication de vecteurs et de matrices dépasse notre propos, mais en résumé très bref :

Modifie via GitHub Le lien s'ouvre dans une nouvelle fenêtre ou un nouvel onglet