Pivoter les données

Le principe de tidy data (une ligne = une observation, une colonne = une variable, une cellule = une valeur) paraît tellement évident que l’on peut se demander comment un jeu de données peut ne pas être tidy. Malheureusement, la plupart des données que vous allez rencontrer ne seront pas tidy. Et ce, pour deux raisons :

  1. Beaucoup de gens ne connaissent pas le principe de tidy data et il n’est pas évident de le réinventer soi-même à moins de travailler beaucoup de temps avec des données.
  2. Les données sont souvent organisées pour faciliter d’autres utilations que leur analyse. Par exemple, pour que leur saisie soit la plus simple possible sur le terrain.

Cela signifie que, très souvent, il faudra ranger les données, en amont de l’analyse. La première étape est toujours d’identifier les variables et les observations. Parfois, c’est évident. Parfois, pas du tout et il faudra se creuser la tête un peu plus.
La seconde étape est de résoudre ces problèmes courants :

  • Une variable qui est répartie sur plusieurs colonnes
  • Une observation qui est répartie sur plusieurs lignes

En général, un jeu de données n’aura qu’un de ces deux problèmes. Il faudrait vraiment ne pas avoir de chance pour avoir les deux ! Heureusement, les fonctions pivot_longer() et pivot_wider() du package tidyr sont là !

Plus long

Parfois, les valeurs d’une variable se retrouvent dans le nom des colonnes.

Dans la table suivante, les colonnes qui s’appelent 1999 et 2000 sont en fait les valeurs d’une variable annee qui n’apparaît pas. Les valeurs qui sont contenues dans les colonnes 1999 et 2000 représentent les valeurs d’une variable nombre_de_cas. Ce qui fait que chaque ligne contient deux observations au lieu d’une seule.

jeu_court <- tribble(
  ~pays, ~`1999`, ~`2000`,
  "Afghanistan", 745, 2666,
  "Brésil", 37737, 80488,
  "Chine", 212258, 213766,
)
jeu_court
## # A tibble: 3 × 3
##   pays        `1999` `2000`
##   <chr>        <dbl>  <dbl>
## 1 Afghanistan    745   2666
## 2 Brésil       37737  80488
## 3 Chine       212258 213766

Pour passer un jeu de données comme ceci en format plus long, il faut pivoter les colonnes fautives dans une nouvelle paire de variables.

Trois paramètres sont nécessaires pour procéder à cette opération :

  • L’ensemble des colonnes dont les noms sont des valeurs et pas des variables. (Ici : 1999 et 2000)
  • Le nom de la variable qui contiendra le nom de ces dernières. (Ici : annee)
  • Le nom de la variable qui contiendra les valeurs. (Ici : nombre_de_cas)

On appelle la fonction pivot_longer() de la manière suivante :

jeu_court %>% 
  pivot_longer(c(`1999`, `2000`), names_to = "annee", values_to = "nombre_de_cas")
## # A tibble: 6 × 3
##   pays        annee nombre_de_cas
##   <chr>       <chr>         <dbl>
## 1 Afghanistan 1999            745
## 2 Afghanistan 2000           2666
## 3 Brésil      1999          37737
## 4 Brésil      2000          80488
## 5 Chine       1999         212258
## 6 Chine       2000         213766

Pour décrire les colonnes à pivoter, on utilise la notation de dplyr::select(). Dans ce cas, les deux colonnes sont listées individuellement, mais il y a bien d’autres manières de sélectionner des colonnes (allez voir l’aide de select()). Notons que 1999 et 2000 ne sont pas des noms syntaxiquement valide pour des noms de colonnes (car ne commencent pas par une lettre), il faut donc les entourer de ` (accent grave).
Les variables annee et nombre_de_cas n’existent pas dans le jeu de données jeu_court, leur nom doit donc être entouré de guillements ".
Dans le résultat final, les colonnes pivotées sont supprimées et de nouvelles colonnes annee et nombre_de_cas sont apparues. Les relations entre les autres variables sont préservées.

pivot_longer() rend le tableau de données plus long en augmentant le nombre de lignes et en diminuant le nombre de colonnes. Le terme “format long” n’a pas trop de sens finalement, car c’est assez relatif. Il serait plus exact de dire que le tableau A est plus long que le tableau B.

Il existe tout un tas de variantes pour l’utilisation de cette fonction (par exemple, plusieurs variables sont contenues dans le nom des colonnes à pivoter), n’hésitez par à regarder les exemples dans l’aide de la fonction pour se faire une meilleure idée de toutes ses potentialités !

Plus court

pivot_wider() fait l’inverse de pivot_longer(). On l’utilise quand une obervation est répartie sur plusieurs lignes (dans le but de faire une analyse multivariée par exemple).

Dans la table suivante, une observation est une combinaison de pays par année, mais chacune est représentée sur deux lignes.

jeu_long <- tibble(
  pays = rep(c("Afghanistan", "Brésil", "Chine"), each = 4),
  annee = rep(1999:2000, times = 3, each = 2),
  type = rep(c("cas", "population"), times = 6),
  nombre = c(745, 19987071, 2666, 20595360, 37737, 172006362, 80488, 174504898, 212258, 1272915272, 213766, 1280428583)
  )
jeu_long
## # A tibble: 12 × 4
##    pays        annee type           nombre
##    <chr>       <int> <chr>           <dbl>
##  1 Afghanistan  1999 cas               745
##  2 Afghanistan  1999 population   19987071
##  3 Afghanistan  2000 cas              2666
##  4 Afghanistan  2000 population   20595360
##  5 Brésil       1999 cas             37737
##  6 Brésil       1999 population  172006362
##  7 Brésil       2000 cas             80488
##  8 Brésil       2000 population  174504898
##  9 Chine        1999 cas            212258
## 10 Chine        1999 population 1272915272
## 11 Chine        2000 cas            213766
## 12 Chine        2000 population 1280428583

Pour passer un jeu de données comme ceci en format plus court, deux arguments soont nécessaires cette fois :

  • Le nom de la colonne qui contient le nom des futures variables. (Ici : type)
  • Le nom de la colonne qui contient les valeurs. (Ici : nombre)

Ceci étant trouvé, la fonction pivot_wider() s’utilise ainsi :

jeu_long %>%
  pivot_wider(names_from = type, values_from = nombre)
## # A tibble: 6 × 4
##   pays        annee    cas population
##   <chr>       <int>  <dbl>      <dbl>
## 1 Afghanistan  1999    745   19987071
## 2 Afghanistan  2000   2666   20595360
## 3 Brésil       1999  37737  172006362
## 4 Brésil       2000  80488  174504898
## 5 Chine        1999 212258 1272915272
## 6 Chine        2000 213766 1280428583

Et visuellement, cela donne :

Les deux fonctions pivot_wider() et pivot_longer() sont donc complémentaires.

pivot_longer() rend les tables plus étroites et plus longues, tandis que pivot_wider() les rend plus courtes et larges.

Exercices

  1. Pourquoi pivot_wider() et pivot_longer() ne sont-elles pas parfaitement symétriques ? Condidérez attentivement l’exemple suivant :
stocks <- tibble(
  annee = c(2015, 2015, 2016, 2016),
  semestre = c(1, 2, 1, 2),
  resultat = c(1.88, 0.59, 0.92, 0.17)
)
stocks %>% 
  pivot_wider(names_from = annee, values_from = resultat) %>% 
  pivot_longer(`2015`:`2016`, names_to = "annee", values_to = "resultat")
## # A tibble: 4 × 3
##   semestre annee resultat
##      <dbl> <chr>    <dbl>
## 1        1 2015      1.88
## 2        1 2016      0.92
## 3        2 2015      0.59
## 4        2 2016      0.17

(Indice : Regardez la nature de chaque variable)

  1. pivot_longer() a un argument names_ptypes. A quoi sert-il ?

  2. Quelle est l’erreur dans ce code ?

jeu_court %>% 
  pivot_longer(c(1999, 2000), names_to = "year", values_to = "cases")
## Error in `pivot_longer()`:
## ! Can't subset columns past the end.
## ℹ Locations 1999 and 2000 don't exist.
## ℹ There are only 3 columns.
  1. Que va-t-il se passer si vous raccourcissez cette table ? Pourquoi ? Comment pourriez-vous rajouter une colonne pour identifier chaque observation de manière unique ?
people <- tribble(
  ~name,             ~names,  ~values,
  #-----------------|--------|------
  "Phillip Woods",   "age",       45,
  "Phillip Woods",   "height",   186,
  "Phillip Woods",   "age",       50,
  "Jessica Cordero", "age",       37,
  "Jessica Cordero", "height",   156
)
  1. Ranger cette petite table. Faut-il la rendre plus longue ou plus courte ? Quelles sont les variables ?
preg <- tribble(
  ~pregnant, ~male, ~female,
  "yes",     NA,    10,
  "no",      20,    12
)

Indice : Obtenez cette table

## # A tibble: 2 × 3
##   sexe   enceinte nombre
##   <chr>     <dbl>  <dbl>
## 1 male         NA     20
## 2 female       10     22

Traduction librement interprétée issue de R for data science de Hadley Wickham : r4ds.had.co.nz/tidy-data.

Anna Doizy
Anna Doizy
Chercheuse, consultante et formatrice freelance
Libre comme l’R

Méthodologie scientifique et analyses de données statistiques