Manipulations de données avec le package dplyr
Crédits photos : SarahRichterArt.
a semaine dernière, je discutais avec un étudiant qui débute dans l’analyse de données avec R. Il devait réaliser des manipulations sur un tableau de données (on appelle cela un data frame en R), plus précisément il avait besoin de sélectionner certaines lignes, créer de nouvelles variables, calculer des moyennes, etc…
Il avait réussi à faire la plupart de ces manipulations, mais avec beaucoup de difficultés, parce qu’il avait eu du mal à identifier les fonctions R nécessaires, puis à les utiliser. Quand je lui ai parlé du package dplyr, il m’a dit qu’il le connaissait très mal, et qu’il ne s’était pas rendu compte qu’il aurait pu l’utiliser pour ses manipulations. J’ai trouvé cela dommage parce qu’il est facile d’acquérir les bases de la manipulation de data frame avec le package dplyr. Mais aussi parce que ce package est vraiment puissant, notamment grâce à des fonctions complémentaires des fonctions dont je vais parler dans cet article, et que vous pourrez apprendre à utiliser dans un second temps.
Manipulations avec le package dplyr
Comme le package “ggplot2”, le package “dplyr” fait parti du super package tidyverse. Vous pouvez donc le charger individuellement, ou par l’intermédiaire de ce super package tidyverse :
library(dplyr)
library(tidyverse)
Les principales fonctions à connaître pour réaliser des manipulations de données sont :
- filter() : pour sélectionner des lignes,
- select() : pour sélectionner des colonnes,
- mutate() : pour créer des variables,
- summarise() : pour résumer des données, en calculant des paramètres descriptifs,
- group_by() : pour regrouper les données (avant de calculer un paramètre descriptif par exemple).
Ces cinq fonctions peuvent s’utiliser les unes après les autres, en utilisant le symbole “%>%” (pipe en anglais), que l’on pourrait traduire par “et puis”.
Le package dispose d’une cheat sheet que vous pouvez télécharger à partir de R Studio et de l’onglet Help –> Cheatsheets –> Data Transformation with dplyr.
Les data utilisées
Je vais illustrer chacune des fonctions citées précédemment avec un exemple, en employant le jeu de données “iris”, qui appartient au package “dataset”, chargé par défaut à chaque ouverture de session.
Le jeu de données “iris” comporte quatre variables numériques (“Sepal.Length” “Sepal.Width” “Petal.Length” “Petal.Width” ), et une variable catégorielle (Species). Voici les 6 premières lignes du jeu de données :
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
Et ici, les 6 dernières lignes :
tail(iris)
## Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## 145 6.7 3.3 5.7 2.5 virginica
## 146 6.7 3.0 5.2 2.3 virginica
## 147 6.3 2.5 5.0 1.9 virginica
## 148 6.5 3.0 5.2 2.0 virginica
## 149 6.2 3.4 5.4 2.3 virginica
## 150 5.9 3.0 5.1 1.8 virginica
La nature des données (numérique / catégorielle) est fournie par la sortie de la fonction str() (qui veut dire structure).
str(iris)
## 'data.frame': 150 obs. of 5 variables:
## $ Sepal.Length: num 5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
## $ Sepal.Width : num 3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
## $ Petal.Length: num 1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
## $ Petal.Width : num 0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
## $ Species : Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...
Sélection de lignes avec la fonction filter()
En fonction d'un seul critère
Le jeu de données iris comporte trois espèces d’iris différentes : “setosa”, “versicolor”, et “virginica”. Si je souhaite n’afficher que les données de l’espèce versicolor, alors, je vais filtrer les lignes correspondant à cette espèce, en utilisant la fonction filter() :
iris %>%
filter(Species=="versicolor")
## Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## 1 7.0 3.2 4.7 1.4 versicolor
## 2 6.4 3.2 4.5 1.5 versicolor
## 3 6.9 3.1 4.9 1.5 versicolor
## 4 5.5 2.3 4.0 1.3 versicolor
## 5 6.5 2.8 4.6 1.5 versicolor
... ... ... ... ....
## 45 5.6 2.7 4.2 1.3 versicolor
## 46 5.7 3.0 4.2 1.2 versicolor
## 47 5.7 2.9 4.2 1.3 versicolor
## 48 6.2 2.9 4.3 1.3 versicolor
## 49 5.1 2.5 3.0 1.1 versicolor
## 50 5.7 2.8 4.1 1.3 versicolor
Si je souhaite constituer un jeu de données avec ce sous-groupe de données correspondant à l’espèce versicolor, je vais simplement réaliser une assignation en amont du filtrage en utilisant “iris_versicolor <-” .
NB : “iris_versicolor” est simplement un nom que j’ai donné, vous pouvez utiliser “toto” si vous préférez 😉
En fonction d'un double critère
Imaginons maintenant que je souhaite sélectionner les lignes correspondant à une valeur de Sepal.Length < 5.8 ET une valeur de Petal.Length > 3.8. Pour cela j’utilise le signe “&”, comme ceci :.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut elit tellus, luctus nec ullamcorper mattis, pulvinar dapibus leo.
iris %>%
filter(Sepal.Length<5.8 & Petal.Length>3.8)
## Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## 1 5.5 2.3 4.0 1.3 versicolor
## 2 5.7 2.8 4.5 1.3 versicolor
## 3 5.2 2.7 3.9 1.4 versicolor
## 4 5.6 3.0 4.5 1.5 versicolor
## 5 5.6 2.5 3.9 1.1 versicolor
## 6 5.4 3.0 4.5 1.5 versicolor
## 7 5.6 3.0 4.1 1.3 versicolor
## 8 5.5 2.5 4.0 1.3 versicolor
## 9 5.5 2.6 4.4 1.2 versicolor
## 10 5.6 2.7 4.2 1.3 versicolor
## 11 5.7 3.0 4.2 1.2 versicolor
## 12 5.7 2.9 4.2 1.3 versicolor
## 13 5.7 2.8 4.1 1.3 versicolor
## 14 4.9 2.5 4.5 1.7 virginica
## 15 5.7 2.5 5.0 2.0 virginica
## 16 5.6 2.8 4.9 2.0 virginica
Pour sélectionner les lignes ayant une valeur de Sepal.Length < 5.8 OU une valeur de Petal.Length < 1.6, on utilise le symbole | (avec les touches ALT GR + 6) :
iris %>%
filter(Sepal.Length<5.8 | Petal.Length<1.6)
## 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
## 7 4.6 3.4 1.4 0.3 setosa
## 8 5.0 3.4 1.5 0.2 setosa
## 9 4.4 2.9 1.4 0.2 setosa
... ... ... ... ... ... ... ... ... ...
En fonction de multiples critères de type catégoriel
Si je souhaite filtrer les lignes en fonction de plusieurs modalités d’une variable catégorielle, j’utilise le symbole %in%, suivi du vecteur, comme ceci :
iris %>%
filter(Species %in% c("setosa", "versicolor"))
## 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
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
## 93 5.8 2.6 4.0 1.2 versicolor
## 94 5.0 2.3 3.3 1.0 versicolor
## 95 5.6 2.7 4.2 1.3 versicolor
## 96 5.7 3.0 4.2 1.2 versicolor
## 97 5.7 2.9 4.2 1.3 versicolor
## 98 6.2 2.9 4.3 1.3 versicolor
## 99 5.1 2.5 3.0 1.1 versicolor
## 100 5.7 2.8 4.1 1.3 versicolor
En echaînant les filtrages
Il est également possible de réaliser plusieurs filtres successivement :
iris %>%
filter(Sepal.Length<5.8 | Petal.Length<1.6) %>%
filter(Species=="versicolor")
## Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## 1 5.5 2.3 4.0 1.3 versicolor
## 2 5.7 2.8 4.5 1.3 versicolor
## 3 4.9 2.4 3.3 1.0 versicolor
## 4 5.2 2.7 3.9 1.4 versicolor
## 5 5.0 2.0 3.5 1.0 versicolor
## 6 5.6 2.9 3.6 1.3 versicolor
## 7 5.6 3.0 4.5 1.5 versicolor
## 8 5.6 2.5 3.9 1.1 versicolor
## 9 5.7 2.6 3.5 1.0 versicolor
## 10 5.5 2.4 3.8 1.1 versicolor
## 11 5.5 2.4 3.7 1.0 versicolor
## 12 5.4 3.0 4.5 1.5 versicolor
## 13 5.6 3.0 4.1 1.3 versicolor
## 14 5.5 2.5 4.0 1.3 versicolor
## 15 5.5 2.6 4.4 1.2 versicolor
## 16 5.0 2.3 3.3 1.0 versicolor
## 17 5.6 2.7 4.2 1.3 versicolor
## 18 5.7 3.0 4.2 1.2 versicolor
## 19 5.7 2.9 4.2 1.3 versicolor
## 20 5.1 2.5 3.0 1.1 versicolor
## 21 5.7 2.8 4.1 1.3 versicolor
Remarque : si R vous renvoie un message d’erreur, il se peut qu’il existe un conflit entre la fonction “filter()” du package dplyr et la fonction filter() du package stats. Dans ce cas, il suffit d’indiquer qu’il s’agit d’utiliser la fonction du package “dplyr” en utilisant la synthaxe dplyr::filter, comme ceci :
iris %>%
dplyr::filter(Species=="versicolor")
Il existe d’autres possibilités pour filtrer les lignes d’un jeu de données avec le package dplyr. Pour en apprendre davantage, je vous recommande l’article “Data Wrangling Part 3: Basic and more advanced ways to filter rows“.
Sélection des variables avec la fonction select()
En indiquant les variables à conserver
Par exemple, ici, je créé un data frame “iris_length” qui ne contient que les variables Sepal.Length et Petal.Length. Pour cela, j’indique ces deux variables en argument de la fonction select() :
iris_length <- iris %>%
select(Sepal.Length, Petal.Length)
head(iris_length)
## Sepal.Length Petal.Length
## 1 5.1 1.4
## 2 4.9 1.4
## 3 4.7 1.3
## 4 4.6 1.5
## 5 5.0 1.4
## 6 5.4 1.7
En indiquant les variables à supprimer
Parfois, on peut avoir besoin de seulement supprimer ou plusieurs variables. Par exemple, ici, si je souhaite retirer la variable “Species” j’utilise le symbole “-“, comme ceci :
iris2 <- iris %>%
select(-Species)
head(iris2)
## Sepal.Length Sepal.Width Petal.Length Petal.Width
## 1 5.1 3.5 1.4 0.2
## 2 4.9 3.0 1.4 0.2
## 3 4.7 3.2 1.3 0.2
## 4 4.6 3.1 1.5 0.2
## 5 5.0 3.6 1.4 0.2
## 6 5.4 3.9 1.7 0.4
Remarque : on peut sélectionner des variables contiguës en utilisant le symbole “:”. Par exemple ici, je souhaite garder les variables allant de “Sepal.Length” à “Petal.Width”.
iris3 <- iris %>%
select(Sepal.Length : Petal.Width)
head(iris3)
## Sepal.Length Sepal.Width Petal.Length Petal.Width
## 1 5.1 3.5 1.4 0.2
## 2 4.9 3.0 1.4 0.2
## 3 4.7 3.2 1.3 0.2
## 4 4.6 3.1 1.5 0.2
## 5 5.0 3.6 1.4 0.2
## 6 5.4 3.9 1.7 0.4
En fonction du nom des variables
On peut, par exemple, choisir de sélectionner les variables en fonction de leur nom. Pour cela, on utilise les fonctions starts_with() ou ends_with(). Cela est très pratique puisque ça évite d’indiquer toutes les variables individuellement.
iris_petal <- iris %>%
select(starts_with("Petal"))
head(iris_petal)
## Petal.Length Petal.Width
## 1 1.4 0.2
## 2 1.4 0.2
## 3 1.3 0.2
## 4 1.5 0.2
## 5 1.4 0.2
## 6 1.7 0.4
De même, avec ends_with() :
iris_width <- iris %>%
select(ends_with("Width"))
head(iris_width)
## Sepal.Width Petal.Width
## 1 3.5 0.2
## 2 3.0 0.2
## 3 3.2 0.2
## 4 3.1 0.2
## 5 3.6 0.2
## 6 3.9 0.4
En fonction du type de données
On peut encore sélectionner les variables en fonction de leur nature (numérique ou catégorielle). Pour cela, on utilise notamment la fonction select_if() en combinaison avec les fonctions is.numeric(), ou is.factor():
iris_num <- iris %>%
select_if(is.numeric)
head(iris_num)
## Sepal.Length Sepal.Width Petal.Length Petal.Width
## 1 5.1 3.5 1.4 0.2
## 2 4.9 3.0 1.4 0.2
## 3 4.7 3.2 1.3 0.2
## 4 4.6 3.1 1.5 0.2
## 5 5.0 3.6 1.4 0.2
## 6 5.4 3.9 1.7 0.4
On voit ici que la variable Species qui est de type catégoriel (ou factor) n’a pas été sélectionné.
Il existe d’autres possibilités de sélection ou de dé-sélection des variables avec le package dplyr. Pour en apprendre davantage, je vous recommande l’article “Data Wrangling Part 1: Basic to Advanced Ways to Select Columns“.
Créer une nouvelle variable avec la fonction mutate()
En plus des manipulations que nous venons de voir, le package dplyr permet de créer facilement une nouvelle variable avec la fonction mutate(). Par exemple, ici, je crée une nouvelle variable, nommée “new_var” qui est le produit des variables “Sepal.Length” et “Sepal.Width”
iris_new <- iris %>%
mutate(new_var = Sepal.Length * Sepal.Width)
head(iris_new)
## Sepal.Length Sepal.Width Petal.Length Petal.Width Species new_var
## 1 5.1 3.5 1.4 0.2 setosa 17.85
## 2 4.9 3.0 1.4 0.2 setosa 14.70
## 3 4.7 3.2 1.3 0.2 setosa 15.04
## 4 4.6 3.1 1.5 0.2 setosa 14.26
## 5 5.0 3.6 1.4 0.2 setosa 18.00
## 6 5.4 3.9 1.7 0.4 setosa 21.06
Résumer des données avec la fonction summarise()
Selon une seule variable
Par exemple, ici, je calcule la moyenne de la variable Sepal.Length, que j’appelle avg :
iris %>%
summarise(avg = mean(Sepal.Length))
## avg
## 1 5.843333
Il est possible d’utiliser des fonctions :
- de positions : mean(), median(), min(), max(), quantile(),
- de dispersion: sd(), IQR(), mad(),
- retournant des valeurs en fonction d’un indice: first(), last(), nth(),
- de comptage n(), n_distinct(),
- logique: any(), all()
Selon plusieurs variables
Pour obtenir l’écart type de toutes les variables numériques, on utilise la fonction summarise_all():
iris %>% select_if(is.numeric) %>%
summarise_all(sd)
## Sepal.Length Sepal.Width Petal.Length Petal.Width
## 1 0.8280661 0.4358663 1.765298 0.7622377
Il existe encore deux variantes de la fonction summarise, avec les fonctions summarise_if() et summarise_at(). Voici un exemple qui permet d’obtenir la même chose que le code précédent :
iris %>%
summarise_if(is.numeric, sd)
## Sepal.Length Sepal.Width Petal.Length Petal.Width
## 1 0.8280661 0.4358663 1.765298 0.7622377
Vous trouverez davantage d’information sur leur utilisation dans l’article “Data Wrangling Part 4: Summarizing and slicing your data” de Suzan Baert .
LA fonction group_by()
Cette fonction est très utile pour calculer des paramètres sur des groupes de données. Par exemple si je veux obtenir la médiane de la variable Sepal.Length pour les trois espèces d’iris :
iris %>%
group_by(Species) %>%
summarise(med=median(Sepal.Length))
## # A tibble: 3 x 2
## Species med
## <fct> <dbl>
## 1 setosa 5
## 2 versicolor 5.9
## 3 virginica 6.5
Autres fonctions utiles
Parmi les autres fonctions que contient la package dplyr, j’utilise aussi très souvent les fonctions rename() pour renommer les variables, et arrange(), pour ordonner les données :
La fonction rename()
iris_new_name <- iris %>%
rename(Longueur_sepal = Sepal.Length)
names(iris_new_name)
## [1] "Longueur_sepal" "Sepal.Width" "Petal.Length" "Petal.Width"
## [5] "Species"
La fonction arrange()
Par exemple, si je veux ordonner le jeux de données iris par valeur croissante de Sepal.Length :
iris %>%
arrange(Sepal.Length)%>%
slice(1:5)
## Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## 1 4.3 3.0 1.1 0.1 setosa
## 2 4.4 2.9 1.4 0.2 setosa
## 3 4.4 3.0 1.3 0.2 setosa
## 4 4.4 3.2 1.3 0.2 setosa
## 5 4.5 2.3 1.3 0.3 setosa
La fonction slice() permet d’afficher directement le nombre de lignes désiré.
Pour ordonner dans le sens décroissant, on utilise la fonction desc() dans la fonction arrange() :
iris %>%
arrange(desc(Sepal.Length))%>%
slice(1:5)
## Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## 1 7.9 3.8 6.4 2.0 virginica
## 2 7.7 3.8 6.7 2.2 virginica
## 3 7.7 2.6 6.9 2.3 virginica
## 4 7.7 2.8 6.7 2.0 virginica
## 5 7.7 3.0 6.1 2.3 virginica
D'autres fonctions
De façon plus ou moins fréquente, j’utilise encore des fonctions pour fusionner des tableaux :
- lef_join(),
- right_join(),
- inner_join(),
- full_join(),
- bind_rows(),
- intersect(),
- setdiff(),
- union().
Vous trouverez des informations sur ces fonctions dans les articles suivants:
Conclusion
J’espère que ce petit article d’introduction à la manipulation des données sous R vous aura, à la fois, convaincu de la facilité d’utilisation du package dplyr et de ses grandes possibilités.
J’espère aussi que cet article permettra, à ceux qui n’utilisent pas encore ce package, de franchir le pas. A mon sens, lorsqu’on fait de l’analyse de données avec R, il est tout aussi important de savoir utiliser le package dplyr que le package ggplot2.
Pour celles et ceux qui veulent en apprendre encore davantage , vous trouverez d’autres exemple, en français, dans le document “Introduction à R et au tidyverse” de Julien Barnier, ou encore dans cet article de ThinkR.
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
Super bon cet article.
Merci.
Bjr Madame :
Mes meilleures félicitations pour ce que vous faites (vos articles relatifs à R).
Les statistiques sont une discipline très demandées pour tous les domaines :
Biologie
Médecine
Agronomie et Agro-industrie
Agriculture
Les Nouvelles Technologies
L’industrie ….
Je suis un Ingénieur Agronome, j’apprécie beaucoup vos articles sur les capacités du logiciel R.
Ainsi, j’ai une demande auprès de vous :
Un article sur le Modèle de Régression Linéaire Multiple, avec un exemple clair
(Tout le Process)
Mes respects
Merci.
Toujours très utile
Merci Gerard.
Bonjour Madame,
J’ai une base de données où je voudrais sélectionner certaines lignes pour leur attribuer une valeur : de telle période à telle période, l’oiseau était en “Vol” ou “Posé”.
J’ai créer une nouvelle colonne appelé “Localisation” mais comme la variable temporelle a un format “%Y-%m-%d %H:%M:%S” je ne peux pas la filtrer comme une valeur numérique.
Pouvez-vous m’aider svp ?
Merci pour tous vos articles !
Bonjour,
si vous utilisez le package lubridate pour gérer vos données de date et de temps, vous pourrez filtrer vos lignes, ou créer une nouvelle variable facilement. Pour plus d’infos, consultez cet article : https://statistique-et-logiciel-r.com/gerer-les-dates-et-les-heures-avec-le-package-lubridate/
Bonne continuation.
Bonjour madame,
Je me demande si je peux résoudre le probleme suivant avec dplyr :
suite à l’exportation des résultats d’une enquête comportants plusieurs questions à choix multiples (exemple : bleu, vert, jaune), je me retrouve avec des occurrences reprenant chaque réponses choisies séparées par un ; dans une cellule pour une seule variable (exemple : bleu, bleu;vert, bleu;jaune, etc… ) . Du coup, R interprète cela comme étant une seule et même réponse.
J’ai déjà essayé de diviser ces réponses obtenues (dichotomisation) mais pas moyen.
En tout cas, merci de vos articles, beaucoup m’ont été bien utile !
Bonjour Hélène,
est ce qu ece n’est pas un problème d’importation (utilisation de read.csv, à la place de read.csv2 par exemple) ?
Sinon, regardez du côté de la fonction separate() du package tidyr.
Bonne continuation.
Bonjour,
Débutant sur R, j’apprends toujours beaucoup de vos article.
group_by est elle compatible avec des dates : A partir d’un fichier météo journalier sur plusieurs années, je veux calculer une moyenne de température entre le 15 avril et le 01 aout et ceci par année.
Merci pour votre aide.
Bonjour,
si je devais faire ça je créerai une variable periode qui correspondrait à l’intervalle de date souhaité, puis je ferai un group by sur la variable periode.
Vous trouverez des infos sur la gestion des dates et du temps, dans cet article : https://statistique-et-logiciel-r.com/gerer-les-dates-et-les-heures-avec-le-package-lubridate/
Bonne continuation
Bonjour Madame, vos articles sont très intéressants et j y apprends beaucoup, merci.
J’ai un soucis avec la fonctions group_by() et summarise() du package dplyr.
Voici la fonction que j’ai écris: avg %group_by(Objet) %>% summarise(avg=mean(Helico))
A chaque fois ça m’écrit: `summarise()` ungrouping output (override with `.groups` argument).
Ok merci et bien des choses à vous
Bonjour
C’est juste un message d’avertissement amical. Par défaut, s’il y a un regroupement avant le résumé, il supprime une variable de groupe, c’est-à-dire la dernière spécifiée dans le group_by. S’il n’y a qu’une seule variable de regroupement, il n’y aura pas d’attribut de regroupement après le résumé et s’il y en a plus d’un, c’est-à-dire ici, il y en a deux, donc, l’attribut de regroupement est réduit à 1. Cf : https://stackoverflow.com/questions/62140483/how-to-interpret-dplyr-message-summarise-regrouping-output-by-x-override
Bonne continuation