1. Les formats wide et long
1.1 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.
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.
1 2 3 4 5 6 7 8 |
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 |
1.2 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 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
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 :
2. Pourquoi utiliser le format long ?
Parce que parfois, c’est plus pratique de disposer du jeu de données en format long.
2.1 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 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
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 |
2.2 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 !
1 2 |
ggplot(iris, aes(Sepal.Length)) + geom_density(fill="blue") |
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:
1 2 3 4 5 6 7 |
iris_long <- iris %>% select(-Species) %>% gather(variable, value) ggplot(iris_long, aes(value)) + geom_density(fill="blue")+ facet_wrap(~variable) |
3. 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” :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
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 :
1 2 |
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 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
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 ...................................... ## 51 versicolor Sepal.Length 7.0 ## 52 versicolor Sepal.Length 6.4 ## 53 versicolor Sepal.Length 6.9 ## 54 versicolor Sepal.Length 5.5 ## 55 versicolor Sepal.Length 6.5 ....................................... ## 101 virginica Sepal.Length 6.3 ## 102 virginica Sepal.Length 5.8 ## 103 virginica Sepal.Length 7.1 ## 104 virginica Sepal.Length 6.3 ## 105 virginica Sepal.Length 6.5 ....................................... ## 151 setosa Sepal.Width 3.5 ## 152 setosa Sepal.Width 3.0 ## 153 setosa Sepal.Width 3.2 ## 154 setosa Sepal.Width 3.1 ## 155 setosa Sepal.Width 3.6 ....................................... ## 201 versicolor Sepal.Width 3.2 ## 202 versicolor Sepal.Width 3.2 ## 203 versicolor Sepal.Width 3.1 ## 204 versicolor Sepal.Width 2.3 ## 205 versicolor Sepal.Width 2.8 ## 206 versicolor Sepal.Width 2.8 ....................................... ## 251 virginica Sepal.Width 3.3 ## 252 virginica Sepal.Width 2.7 ## 253 virginica Sepal.Width 3.0 ## 254 virginica Sepal.Width 2.9 ## 255 virginica Sepal.Width 3.0 ....................................... ## 301 setosa Petal.Length 1.4 ## 302 setosa Petal.Length 1.4 ## 303 setosa Petal.Length 1.3 ## 304 setosa Petal.Length 1.5 ## 305 setosa Petal.Length 1.4 ## 351 versicolor Petal.Length 4.7 ## 352 versicolor Petal.Length 4.5 ## 353 versicolor Petal.Length 4.9 ## 354 versicolor Petal.Length 4.0 ## 355 versicolor Petal.Length 4.6 ....................................... ....................................... ## 595 virginica Petal.Width 2.5 ## 596 virginica Petal.Width 2.3 ## 597 virginica Petal.Width 1.9 ## 598 virginica Petal.Width 2.0 ## 599 virginica Petal.Width 2.3 ## 600 virginica Petal.Width 1.8 |
4. Quelques exemples de calcul de paramètres descriptifs
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
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() :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
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 :
- transformer la variable “variable” en facteur (car elle est considérée comme une chaîne de caractère)
-
modifier les levels de la variable “variable”.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
# 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.
5. 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..
1 2 3 |
ggplot(iris_long, aes(value)) + geom_density(fill="blue")+ facet_wrap(~variable) |
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() :
1 2 3 |
ggplot(iris_long, aes(value)) + geom_density(fill="blue")+ facet_wrap(Species~variable) |
1 2 3 |
ggplot(iris_long, aes(value)) + geom_density(fill="blue")+ facet_grid(Species~variable) |
6 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”:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
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_iris_long id var val 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 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.
Et si cet article vous a plu, partagez le 😉
Poursuivez votre lecture :
- 12 conseils pour organiser efficacement vos données dans un tableur
- Comment utiliser ggplot dans une boucle ou dans une fonction ?
- 10Partages
10
Très informatif. Merci
Merci de nous partager cette bonne idée, je vais le tester sur mes cas d’étude.
Bonjour Claire,
Merci beaucoup pour ce post. C’est toujours un plaisir de recevoir le mail me prévenant d’un nouveau post. Je sais que je vais encore apprendre plein de chose.
J’ai une question. Au tout début (1.1) comment passes-tu de la base “iris” à “my_iris”.
J’ai pas bien compris le principe du format “tidy”.
S’agit-il simplement de rajouter une colonne “id” ?
J’avoue qu’il y a un point d’accroche que j’ai du mal à appréhender.
Encore mille merci pour tous ces enseignement.
Bonjour,
j’ai créé le dataset my_iris comme cela :
my_iris <- iris %>%
mutate(id=1:n()) %>%
select(id, everything(),-Species)
my_iris <- my_iris[1:5,] C'était peut être maladroit, parce que le dataset iris est déjà en format tidy : une ligne par observation, et une colonne par variable. Donc non, pas besoin de rajouter une colonne id pour être en format tidy. J'espère que c'est plus compréhensible maintenant ! Bonne continuation