Format wide et long : pourquoi, et comment ?

Les formats wide et long

Format wide

Je dis très régulièrement que, pour être traités avec le logiciel R, les jeux de données doivent être au format “tidy”, c’est-à-dire avec une ligne par observation, une variable par colonne, et une valeur au croisement d’une ligne et d’une colonne.

tidy

Le format tidy, d’après  R for Data Science.

Cette appelation “tidy” a été donnée assez récemment par Hadley Wickham.

Il existe deux grands type de format tidy, le format wide et le fomat long.

Par exemple le jeu de données suivant, dérivé du jeu de données iris, est au format wide.

##   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## 1          5.1         3.5          1.4         0.2  setosa
## 2          4.9         3.0          1.4         0.2  setosa
## 3          4.7         3.2          1.3         0.2  setosa
## 4          4.6         3.1          1.5         0.2  setosa
## 5          5.0         3.6          1.4         0.2  setosa
## 6          5.4         3.9          1.7         0.4  setosa 

Le format Long

Bien que le format “wide” soit le format le plus adéquat pour traiter les données, parfois, il peut être utile de passer nos jeux de données en format “long”. Dans ce format, le jeu de données est réduit à trois colonnes :

  • une contenant l’identifiant de l’observation (pas forcément nécessaire et dans ce cas,le jeu de données n’a que les deux variables suivantes)
  • une (ici nommée “variable”) contenant le nom de la variable considérée,
  • une (ici nommée “value”) contenant la valeur.

Par exemple, le jeu de données précédent, passé en format long, est comme cela :

my_iris_long
##    id     variable value
## 1   1 Sepal.Length   5.1
## 2   2 Sepal.Length   4.9
## 3   3 Sepal.Length   4.7
## 4   4 Sepal.Length   4.6
## 5   5 Sepal.Length   5.0
## 6   1  Sepal.Width   3.5
## 7   2  Sepal.Width   3.0
## 8   3  Sepal.Width   3.2
## 9   4  Sepal.Width   3.1
## 10  5  Sepal.Width   3.6
## 11  1 Petal.Length   1.4
## 12  2 Petal.Length   1.4
## 13  3 Petal.Length   1.3
## 14  4 Petal.Length   1.5
## 15  5 Petal.Length   1.4
## 16  1  Petal.Width   0.2
## 17  2  Petal.Width   0.2
## 18  3  Petal.Width   0.2
## 19  4  Petal.Width   0.2
## 20  5  Petal.Width   0.2 

Voici un schéma pour visualiser la transformation du format wide vers le format long :

formats wide et long

Pourquoi utiliser le format long

Parce que parfois, c’est plus pratique de disposer du jeu de données en format long.

Facilité pour calculer des paramètres

Par exemple pour calculer des paramètres descriptifs pour toutes les variables, avec la fonction summarise() du package dplyr. Il suffit alors d’employer group_by(variable), comme ceci :

library(tidyverse)
my_iris_long %>%
    group_by(variable) %>%
    summarise(N=n(), 
              moy=mean(value, na.rm=TRUE),
              med=median(value,na.rm=TRUE),
              ecart_type=sd(value,na.rm=TRUE))
## # A tibble: 4 x 5
##   variable         N   moy   med ecart_type
##   <chr>        <int> <dbl> <dbl>      <dbl>
## 1 Petal.Length     5  1.4    1.4     0.0707
## 2 Petal.Width      5  0.2    0.2     0     
## 3 Sepal.Length     5  4.86   4.9     0.207 
## 4 Sepal.Width      5  3.28   3.2     0.259 

Facilité pour faire du faceting avec ggplot2

Par exemple, si je veux visualiser la densité de distribution des variables Sepal.Length, Sepal.Width, Petal.Length , et Petal.Width du jeu de données “iris”.

Avec le format tidy, il faut faire le plot variable par variable, ou bien faire une boucle. Mais quand on débute avec R, faire des boucles avec des ggplots ce n’est pas la chose la plus simple !

ggplot(iris, aes(Sepal.Length)) +
    geom_density(fill="blue") 
geom_density

Alors que si on a un jeu de données en format long, il suffit d’inclure la variable “variable” dans la fonction facet_wrap(), ou facet_grid() , comme cela:

iris_long <- iris %>% 
select(-Species) %>% 
gather(variable, value) 

ggplot(iris_long, aes(value)) +
  geom_density(fill="blue") +
  facet_wrap(~variable) 

Passer du format wide au format long

Il est très facile de transformer un jeu de données au format wide vers un format long. Pour cela, on utilise la fonction gather() du package tidyr, qui fait parti du super package tidyverse.

Voici un exemple avec le jeu de données “iris” :

head(iris)
##   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## 1          5.1         3.5          1.4         0.2  setosa
## 2          4.9         3.0          1.4         0.2  setosa
## 3          4.7         3.2          1.3         0.2  setosa
## 4          4.6         3.1          1.5         0.2  setosa
## 5          5.0         3.6          1.4         0.2  setosa
## 6          5.4         3.9          1.7         0.4  setosa

iris_long <- iris %>%
    gather(variable, value) 

## Warning: attributes are not identical across measure variables;
## they will be dropped     

Ici, on obtient un warning, car le jeu de données “iris” comporte une variable catégorielle (Species). Dans ce cas, on peut indiquer à la fonction gather(), de ne pas considérer la variable “Species”, en utilisant le signe – (moins), comme cela :

iris_long <- iris %>% 
gather(variable, value,-Species) 

Dans ce cas, une troisième variable est créée et comporte le nom de l’espèce à laquelle se rapporte chacune des lignes :

head(iris_long)
##        Species     variable value
## 1       setosa Sepal.Length   5.1
## 2       setosa Sepal.Length   4.9
## 3       setosa Sepal.Length   4.7
## 4       setosa Sepal.Length   4.6
## 5       setosa Sepal.Length   5.0
## 6       setosa Sepal.Length   5.4 

Quelques exemples de calculs de paramètres decrsiptifs

Comme expliqué précédemment, on utilise la variable nommée “variable” en argument de la fonction group_by(), avant d’utiliser la fonction summarise(), comme ceci:

iris_long %>%
    group_by(variable) %>%
    summarise(N=n(),
              moy=mean(value, na.rm=TRUE),
              med=median(value, na.rm=TRUE),
              sd=sd(value, na.rm=TRUE),
              se=sd/sqrt(N),
              ci=qt(0.975,N-1)*se)
              
## # A tibble: 4 x 7
##   variable         N   moy   med    sd     se     ci
##   <chr>        <int> <dbl> <dbl> <dbl>  <dbl>  <dbl>
## 1 Petal.Length   150  3.76  4.35 1.77  0.144  0.285 
## 2 Petal.Width    150  1.20  1.3  0.762 0.0622 0.123 
## 3 Sepal.Length   150  5.84  5.8  0.828 0.0676 0.134 
## 4 Sepal.Width    150  3.06  3    0.436 0.0356 0.0703
 

Et si on veut calculer ces mêmes paramètres mais en distinguant les espèces, rien de plus simple : on ajoute la variable Species dans la fonction group_by() :

iris_long %>%
    group_by(variable, Species) %>%
    summarise(N=n(),
              moy=mean(value, na.rm=TRUE),
              med=median(value, na.rm=TRUE),
              sd=sd(value, na.rm=TRUE),
              se=sd/sqrt(N),
              ci=qt(0.975,N-1)*se)

## # A tibble: 12 x 8
## # Groups:   variable [?]
##    variable     Species        N   moy   med    sd     se     ci
##    <chr>        <fct>      <int> <dbl> <dbl> <dbl>  <dbl>  <dbl>
##  1 Petal.Length setosa        50 1.46   1.5  0.174 0.0246 0.0494
##  2 Petal.Length versicolor    50 4.26   4.35 0.470 0.0665 0.134 
##  3 Petal.Length virginica     50 5.55   5.55 0.552 0.0780 0.157 
##  4 Petal.Width  setosa        50 0.246  0.2  0.105 0.0149 0.0300
##  5 Petal.Width  versicolor    50 1.33   1.3  0.198 0.0280 0.0562
##  6 Petal.Width  virginica     50 2.03   2    0.275 0.0388 0.0781
##  7 Sepal.Length setosa        50 5.01   5    0.352 0.0498 0.100 
##  8 Sepal.Length versicolor    50 5.94   5.9  0.516 0.0730 0.147 
##  9 Sepal.Length virginica     50 6.59   6.5  0.636 0.0899 0.181 
## 10 Sepal.Width  setosa        50 3.43   3.4  0.379 0.0536 0.108 
## 11 Sepal.Width  versicolor    50 2.77   2.8  0.314 0.0444 0.0892
## 12 Sepal.Width  virginica     50 2.97   3    0.322 0.0456 0.0917 

Et si on souhaite changer l’ordre d’apparition des variables Petal.Length, Petal Width, Sepal.Length et Sepal.Width, il faut :

  1. transformer la variable “variable” en facteur (car elle est considérée comme une chaîne de caractère)
  2. modifier les levels de la variable “variable”.
# permet de voir que la variable "variable" est une chaîne de caractères
str(iris_long)
## 'data.frame':    600 obs. of  3 variables:
##  $ Species : Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...
##  $ variable: chr  "Sepal.Length" "Sepal.Length" "Sepal.Length" "Sepal.Length" ...
##  $ value   : num  5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...

# transformation en facteur
iris_long$variable <- as.factor(iris_long$variable)

# ordre des modalités par défaut
levels(iris_long$variable)
## [1] "Petal.Length" "Petal.Width"  "Sepal.Length" "Sepal.Width"

#modification de l'ordre
iris_long$variable <- factor(iris_long$variable, levels=c("Sepal.Length","Sepal.Width","Petal.Width","Petal.Length"))

# verification
levels(iris_long$variable)
## [1] "Sepal.Length" "Sepal.Width"  "Petal.Width"  "Petal.Length"

iris_long %>%
    group_by(variable) %>%
    summarise(N=n(),
              moy=mean(value, na.rm=TRUE),
              med=median(value, na.rm=TRUE),
              sd=sd(value, na.rm=TRUE),
              se=sd/sqrt(N),
              ci=qt(0.975,N-1)*se)
## # A tibble: 4 x 7
##   variable         N   moy   med    sd     se     ci
##   <fct>        <int> <dbl> <dbl> <dbl>  <dbl>  <dbl>
## 1 Sepal.Length   150  5.84  5.8  0.828 0.0676 0.134 
## 2 Sepal.Width    150  3.06  3    0.436 0.0356 0.0703
## 3 Petal.Width    150  1.20  1.3  0.762 0.0622 0.123 
## 4 Petal.Length   150  3.76  4.35 1.77  0.144  0.285 

Dans la sortie, c’est à présent la variable Sepal.Length qui est en première ligne.

Quelques exemples pour le faceting avec ggplot2

De la même façon, le changement des levels , va entraîner une modification de l’ordre des sous plots dans le faceting. Par exemle, à présent c’est la variable Sepal Length qui est représentée dans le premier sous plot, puis la variable Sepal.Width, etc.. la même façon, le changement des levels , va entraîner une modification de l’ordre des sous plots dans le faceting. Par exemle, à présent c’est la variable Sepal Length qui est représentée dans le premier sous plot, puis la variable Sepal.Width, etc..

ggplot(iris_long, aes(value)) +
geom_density(fill="blue")+
facet_wrap(~variable) 
format long et wide avec R

Et comme précédemment, si vous souhaitez obtenir la densité de ces quatre variables, mais en distinguant chacune des espèces, vous pouvez le faire en rajoutant la variable Species dans les fonctions facet_wrap() ou facet_grid() :

ggplot(iris_long, aes(value)) +
  geom_density(fill="blue")+
  facet_wrap(Species~variable) 
ggplot(iris_long, aes(value)) +
  geom_density(fill="blue") +
  facet_grid(Species~variable) 
facet_grid

Repasser du format long au format wide

Pour cela, il est nécessaire d’utiliser la fonction inverse de la fonction gather(), qui est la fonction spread() (toujours dans le package tidyr). Cette fonction nécessite que le format long dispose d’un identifiant unique pour chaque ligne. Il est donc nécessaire de l’ajouter avant si celui-ci n’est pas présent. Dans l’exemple ci dessous, cela correspond à la variable “id”:

my_iris
##   id Sepal.Length Sepal.Width Petal.Length Petal.Width
## 1  1          5.1         3.5          1.4         0.2
## 2  2          4.9         3.0          1.4         0.2
## 3  3          4.7         3.2          1.3         0.2
## 4  4          4.6         3.1          1.5         0.2
## 5  5          5.0         3.6          1.4         0.2
my_iris_long <- my_iris %>%
    gather(var, val, -id)
my_iris2 <- my_iris_long %>%
    spread(var,val)
my_iris2
##   id Petal.Length Petal.Width Sepal.Length Sepal.Width
## 1  1          1.4         0.2          5.1         3.5
## 2  2          1.4         0.2          4.9         3.0
## 3  3          1.3         0.2          4.7         3.2
## 4  4          1.5         0.2          4.6         3.1
## 5  5          1.4         0.2          5.0         3.6 

Conclusion

J’espère que cet article vous permettra de vous approprier cette astuce consistant à passer d’un format wide à un format long pour faciliter vos analyses et vos visualisations. Si vous avez d’autres astuces, indiquez-les-moi en commentaire.

Si cet article vous a plu, ou vous a été utile, et si vous le souhaitez, vous pouvez soutenir ce blog en faisant un don sur sa page Tipeee 🙏

Bonjour !

vous venez souvent ?

Identifiez-vous pour avoir accès à toutes les fontionnalités !

Aide mémoire off'R ;)

Enregistrez vous pour recevoir gratuitement mes fiches “aide mémoire” (ou cheat sheets) qui vous permettront de réaliser facilement les principales analyses biostatistiques avec le logiciel R et pour être informés des mises à jour du site.