SAVOR: Part III
dplyr
and %>%
Objectifs
Ce document donne tout d’abord quelques considérations sur l’importation de données et sur les strucutres de bases de R, avant de donner quelques éléments introductifs sur l’utilisation du package dplyr
.
Introduction à dplyr
Il existe un très grand nombre de packages additionnels dans . Certains apportent de nouvelles fonctionnalités (méthodes statistiques qui n’étaient pas disponible auparavant), et d’autres apportent des améliorations à des fonctionnalités déjà présentes dans .
Le package dplyr
rentre essentiellement dans la 2ème catégorie. Il propose de re-définir la manipulation de données dans R, avec une syntaxe plus simple, tout en améliorant les performances lorsqu’on gère des grands tableaux de données.
Installer le package dplyr
puis le charger :
Gestion de fichiers de données
Avant de se lancer dans l’utilisation de dplyr
, nous allons revoir quelques principes dans la gestion des fichiers de données sous .
Importation
Le plus simple pour importer des données est d’utiliser l’outil graphique de Rstudio “Import Dataset”, présent dans l’onglet “Environnement” ou dans le menu “File”.
Sinon, on peut bien entendu utiliser la fonction read.table()
et ses dérivées (read.csv()
, read.delim()
…).
Importer le fichier naissance.txt
, puis regarder la structure du tableau créé via la fonction str()
.
considère toutes les variables comme des suite de nombres entiers (car effectivement il n’y a pas de nombres à virgule), ce qui est correcte pour les variables quantitatives (comme l’âge ou encore le poids de l’enfant BWT
). Par contre, certaines variables sont qualitatives (comme SMOKE
), donc si on veut des facteurs, il faut le forcer.
Transformer la variable SMOKE
en facteur, et renommer les niveaux en “Non-Fumeur” (0 dans le fichier txt) et “Fumeur” (1 dans le fichier txt).
Exercice 0 :
Charger le fichier de données nommé
naissance_na.txt
.Consulter la structure de l’objet créé (par exemple nommé
naissance_na
). Que remarquez-vous pour la variableLWT
?Pour régler le problème, modifier le contenu de la case “NA.strings” lors du chargement du fichier de données avec “Import Dataset” (ou modifier le paramètre
na.strings
de la fonctionread.table()
ou de ses dérivées). Puis vérifier la nouvelle structure de l’objet.A l’aide de la fonction
transform()
, transformer en facteur les variablesRACE
(1=blanche, 2=noire, 3=autres),HT
(0=non, 1=oui) etUI
(0=non, 1=oui) en une seule commande.-
Toujours à l’aide de la fonction
transform()
, créer deux nouvelles variables (utiliser deux commandes différentes, puis essayer en une seule fois) :POIDS
, contenant le poids de la mère en g (1 livre = 453 g).RAPPORT
, contenant le rapport entre le poids de l’enfant et celui de la mère (en grammes tous les deux).
Utiliser la fonction
dplyr::mutate()
(du packagedplyr
) pour répondre à la question précédente en une seule commande.
Exportation
Pour écrire une table de données, on utilise la fonction write.table()
ou ses dérivées.
Code
mon_df <- data.frame(Partie = c("Intro", "Bases", "Manip"), Temps = c(0.5, 1.5, 1.5))
mon_df
Partie Temps
1 Intro 0.5
2 Bases 1.5
3 Manip 1.5
Code
write.csv(mon_df, file = "mon_df.csv")
Sauvegarde
On peut aussi sauvegarder une table au format Rdata
(qui est un format propre à R). Pour cela on utilise save()
:
Code
save(naissance, file = "naissance.Rdata")
L’avantage est que lorsqu’on recharge le fichier avec la fonction load()
, il garde la bonne structure :
'data.frame': 189 obs. of 10 variables:
$ ID : int 85 86 87 88 89 91 92 93 94 95 ...
$ AGE : int 19 33 20 21 18 21 22 17 29 26 ...
$ LWT : int 182 155 105 108 107 124 118 103 123 113 ...
$ RACE : int 2 3 1 1 1 3 1 3 1 1 ...
$ SMOKE: Factor w/ 2 levels "Non-Fumeur","Fumeur": 1 1 2 2 2 1 1 1 2 2 ...
$ PTL : int 0 0 0 0 0 0 0 0 0 0 ...
$ HT : int 0 0 0 0 0 0 0 0 0 0 ...
$ UI : int 1 0 0 1 1 0 0 0 0 0 ...
$ FVT : int 0 3 1 2 0 0 1 1 1 0 ...
$ BWT : int 2523 2551 2557 2594 2600 2622 2637 2637 2663 2665 ...
On remarque qu’on a pas besoin d’affecter le résultat de load()
à un objet. Ceci est dû au fait, qu’on peut sauvegarder plusieurs objets de la session dans un même fichier .Rdata
. Et on pourra tous les recharger ensuite :
Error in eval(expr, envir, enclos): object 'mon_df' not found
[1] "data.frame"
Ce comportement est pratique, mais peut parfois s’avérer trompeur. Si on a beaucoup d’objets dans notre environnement, et qu’on charge un objet .Rdata
avec plusieurs objets (dont on ne se souvient pas, ou dont on ne connaît pas le nom), il est difficile de s’y retrouver. De plus, lors du chargement, peut écraser des objets déjà présents avec le même nom.
Conseil : mettre tout ce qu’on veut sauver dans une liste, et sauver cette liste sous format .Rdata
en gardant le même nom :
Opérations sur les tableaux de données
Sélection, suppression, ajout
Exercice 1 : Sélection de colonnes
J’ai dénombré 10 façons de sélectionner la variable
AGE
. En gardant en tête qu’undata.frame
est une liste de colonnes, essayer d’en trouver un maximum.Utiliser la fonction
select
du packagedplyr
pour sélectionner la variableAGE
et regarder comment elle se comporte.
Exercice 2 : Sélection de plusieurs colonnes
Essayer tous les commandes précédentes pour extraire, en même temps, les 4 colonnes ID
, AGE
, LWT
et SMOKE
.
Exercice 3 : Sélection de lignes
Sélectionner les 5 premières lignes, puis les 5 dernières lignes (en 2 commandes) en utilisant une commande de base puis la fonction dplyr::slice()
. Afficher le résultat dans une jolie table grâce à la fonction kable()
du package knitr
.
Exercice 4 : Filtre
Construire un sous-dataframe ne contenant que les individus fumeurs, en utilisant
[,]
, puissubset()
, et enfindplyr::filter()
.Donner les identifiants (colonne
ID
) des individus des individus présentant de l’hypertension (variableHT
valant 1).Sélectionner le sous-dataframe ne contenant que les individus fumeurs et âgés de moins de 20 ans.
Donner les indices des individus ayant un poids inférieur à 110 (le poids de la mère est en livres) ou ayant un bébé de poids inférieur à 2,5 kg.
Exercice 5 : Suppression de lignes ou de colonnes
En essayant avec [,]
, subset()
et les fonctions de dplyr
:
Supprimer la première ligne du jeu de données.
Supprimer les colonnes “AGE”, “LWT”, “RACE”, “BWT”.
Indice : utiliser la même syntaxe que pour la sélection mais avec le signe -
pour indiquer les éléments à supprimer…
Exercice 6 : Ajout de lignes ou de colonnes
Ajouter un une ligne (un individu) possédant les valeurs suivantes : 1, 20, 143, 2, "Non-Fumeur", 0, 0, 0, 0, 2500
NB: On peut rajouter une colonne (une variable), à l’aide de $
, transform()
ou mutate()
(comme vu précédemment).
Fusion
La fonction merge()
permet de fusionner deux tableaux de données :
Code
head(naissance)
ID AGE LWT RACE SMOKE PTL HT UI FVT BWT
1 85 19 182 2 Non-Fumeur 0 0 1 0 2523
2 86 33 155 3 Non-Fumeur 0 0 0 3 2551
3 87 20 105 1 Fumeur 0 0 0 1 2557
4 88 21 108 1 Fumeur 0 0 1 2 2594
5 89 18 107 1 Fumeur 0 0 1 0 2600
6 91 21 124 3 Non-Fumeur 0 0 0 0 2622
Code
nais1 <- naissance[, 1:6]
head(nais1)
ID AGE LWT RACE SMOKE PTL
1 85 19 182 2 Non-Fumeur 0
2 86 33 155 3 Non-Fumeur 0
3 87 20 105 1 Fumeur 0
4 88 21 108 1 Fumeur 0
5 89 18 107 1 Fumeur 0
6 91 21 124 3 Non-Fumeur 0
ID HT UI FVT BWT
1 85 0 1 0 2523
2 86 0 0 3 2551
3 87 0 0 1 2557
4 88 0 1 2 2594
5 89 0 1 0 2600
6 91 0 0 0 2622
ID AGE LWT RACE SMOKE PTL HT UI FVT BWT
1 1 20 143 2 Non-Fumeur 0 0 0 0 2500
2 4 28 120 3 Fumeur 1 0 1 0 709
3 10 29 130 1 Non-Fumeur 0 0 1 2 1021
4 11 34 187 2 Fumeur 0 1 0 0 1135
5 13 25 105 3 Non-Fumeur 1 1 0 0 1330
6 15 25 85 3 Non-Fumeur 0 0 1 0 1474
Code
ID AGE LWT RACE SMOKE PTL HT UI FVT BWT
1 85 19 182 2 Non-Fumeur 0 0 1 0 2523
2 86 33 155 3 Non-Fumeur 0 0 0 3 2551
3 87 20 105 1 Fumeur 0 0 0 1 2557
4 88 21 108 1 Fumeur 0 0 1 2 2594
5 89 18 107 1 Fumeur 0 0 1 0 2600
6 91 21 124 3 Non-Fumeur 0 0 0 0 2622
Code
identical(nais_fus1, nais_fus2)
[1] FALSE
Code
# Les deux dataframes ne sont pas identitiques, car merge a classé par ordre
# croissant suivant la variable de regroupement ID, mais les informations sont
# les mêmes.
On peut également fusionner deux tableaux de données où les individus (qui doivent être les mêmes) ne sont pas dans le même ordre :
Code
ID HT UI FVT BWT
83 176 0 0 0 3475
95 189 0 0 0 3643
55 142 0 0 0 3175
180 71 0 0 2 2438
73 164 0 0 1 3331
127 223 0 0 1 4174
ID AGE LWT RACE SMOKE PTL HT UI FVT BWT
1 1 20 143 2 Non-Fumeur 0 0 0 0 2500
2 4 28 120 3 Fumeur 1 0 1 0 709
3 10 29 130 1 Non-Fumeur 0 0 1 2 1021
4 11 34 187 2 Fumeur 0 1 0 0 1135
5 13 25 105 3 Non-Fumeur 1 1 0 0 1330
6 15 25 85 3 Non-Fumeur 0 0 1 0 1474
Code
identical(nais_fus1, nais_fus3)
[1] TRUE
Exercice 7 : join
Consulter l’aide de
dplyr::sample_n
et trouver un moyens simple de permuter les lignes du jeu de données.Consulter l’aide de
dplyr::join()
(pour plus d’informations sur les fusions avecdplyr
consulter cette page), puis utiliser la fonctiondplyr::left_join()
pour fusionner les tablesnais1
etnais3
.
Trier un tableau de données le long d’une colonne
Pour trier le tableau suivant la variable ID
par ordre croissant, on utilise la fonction order()
:
ID | AGE | LWT | RACE | SMOKE | PTL | HT | UI | FVT | BWT | |
---|---|---|---|---|---|---|---|---|---|---|
190 | 1 | 20 | 143 | 2 | Non-Fumeur | 0 | 0 | 0 | 0 | 2500 |
131 | 4 | 28 | 120 | 3 | Fumeur | 1 | 0 | 1 | 0 | 709 |
132 | 10 | 29 | 130 | 1 | Non-Fumeur | 0 | 0 | 1 | 2 | 1021 |
133 | 11 | 34 | 187 | 2 | Fumeur | 0 | 1 | 0 | 0 | 1135 |
134 | 13 | 25 | 105 | 3 | Non-Fumeur | 1 | 1 | 0 | 0 | 1330 |
135 | 15 | 25 | 85 | 3 | Non-Fumeur | 0 | 0 | 1 | 0 | 1474 |
Maintenant, si on veut trier le tableau suivant l’âge par ordre décroissant :
Code
ID | AGE | LWT | RACE | SMOKE | PTL | HT | UI | FVT | BWT | |
---|---|---|---|---|---|---|---|---|---|---|
130 | 226 | 45 | 123 | 1 | Non-Fumeur | 0 | 0 | 0 | 1 | 4990 |
23 | 108 | 36 | 202 | 1 | Non-Fumeur | 0 | 0 | 0 | 1 | 2836 |
89 | 183 | 36 | 175 | 1 | Non-Fumeur | 0 | 0 | 0 | 0 | 3600 |
33 | 119 | 35 | 121 | 2 | Fumeur | 1 | 0 | 0 | 1 | 2948 |
127 | 223 | 35 | 170 | 1 | Non-Fumeur | 1 | 0 | 0 | 1 | 4174 |
133 | 11 | 34 | 187 | 2 | Fumeur | 0 | 1 | 0 | 0 | 1135 |
2 | 86 | 33 | 155 | 3 | Non-Fumeur | 0 | 0 | 0 | 3 | 2551 |
40 | 127 | 33 | 109 | 1 | Fumeur | 0 | 0 | 0 | 1 | 3033 |
114 | 210 | 33 | 117 | 1 | Non-Fumeur | 0 | 0 | 1 | 1 | 3912 |
21 | 106 | 32 | 121 | 3 | Non-Fumeur | 0 | 0 | 0 | 2 | 2835 |
On remarque qu’il y a des ex-aequo. On peut rajouter une variable sur laquelle trier les ex-aequo, en deuxième argument de order()
:
Code
ID | AGE | LWT | RACE | SMOKE | PTL | HT | UI | FVT | BWT | |
---|---|---|---|---|---|---|---|---|---|---|
130 | 226 | 45 | 123 | 1 | Non-Fumeur | 0 | 0 | 0 | 1 | 4990 |
23 | 108 | 36 | 202 | 1 | Non-Fumeur | 0 | 0 | 0 | 1 | 2836 |
89 | 183 | 36 | 175 | 1 | Non-Fumeur | 0 | 0 | 0 | 0 | 3600 |
127 | 223 | 35 | 170 | 1 | Non-Fumeur | 1 | 0 | 0 | 1 | 4174 |
33 | 119 | 35 | 121 | 2 | Fumeur | 1 | 0 | 0 | 1 | 2948 |
133 | 11 | 34 | 187 | 2 | Fumeur | 0 | 1 | 0 | 0 | 1135 |
2 | 86 | 33 | 155 | 3 | Non-Fumeur | 0 | 0 | 0 | 3 | 2551 |
114 | 210 | 33 | 117 | 1 | Non-Fumeur | 0 | 0 | 1 | 1 | 3912 |
40 | 127 | 33 | 109 | 1 | Fumeur | 0 | 0 | 0 | 1 | 3033 |
111 | 207 | 32 | 186 | 1 | Non-Fumeur | 0 | 0 | 0 | 2 | 3860 |
Enfin, si on veut que le classement soit fait de façon croissante sur LWT
, on peut procéder de la façon suivante :
Code
nais_decAGE_LWT <- naissance[order(naissance$AGE, -naissance$LWT, decreasing = TRUE),
]
nais_decAGE_LWT[1:10, ]
ID AGE LWT RACE SMOKE PTL HT UI FVT BWT
130 226 45 123 1 Non-Fumeur 0 0 0 1 4990
89 183 36 175 1 Non-Fumeur 0 0 0 0 3600
23 108 36 202 1 Non-Fumeur 0 0 0 1 2836
33 119 35 121 2 Fumeur 1 0 0 1 2948
127 223 35 170 1 Non-Fumeur 1 0 0 1 4174
133 11 34 187 2 Fumeur 0 1 0 0 1135
40 127 33 109 1 Fumeur 0 0 0 1 3033
114 210 33 117 1 Non-Fumeur 0 0 1 1 3912
2 86 33 155 3 Non-Fumeur 0 0 0 3 2551
141 22 32 105 1 Fumeur 0 0 0 0 1818
Exercice 8 : dplyr::arrange()
Utiliser la fonction
dplyr::arrange()
pour reproduire les tris précédents.Trier le dataframe suivant la variable
SMOKE
(qui est un facteur). Comment R choisit-il pour déterminer quelle modalité du facteur va apparaître en premier ?Rajouter une variable, nommée
lettre
au dataframe, contenant des lettres majuscules choisies alétoirement. On peut utilisersample(letters, nrow(naissance), replace = TRUE)
pour générer ce tirage aléatoire.Trier le dataframe suivant la nouvelle variable
lettre
.
La classe tlb
Le package s’appuie sur une nouvelle classe d’objets : tbl
. Le résultat affiché lorsqu’on tape le nom du tableau est tronqué pour cette nouvelle classe :
Code
nais <- as_tibble(naissance)
nais
# A tibble: 190 × 10
ID AGE LWT RACE SMOKE PTL HT UI FVT BWT
<dbl> <dbl> <dbl> <dbl> <fct> <dbl> <dbl> <dbl> <dbl> <dbl>
1 85 19 182 2 Non-Fumeur 0 0 1 0 2523
2 86 33 155 3 Non-Fumeur 0 0 0 3 2551
3 87 20 105 1 Fumeur 0 0 0 1 2557
4 88 21 108 1 Fumeur 0 0 1 2 2594
5 89 18 107 1 Fumeur 0 0 1 0 2600
6 91 21 124 3 Non-Fumeur 0 0 0 0 2622
7 92 22 118 1 Non-Fumeur 0 0 0 1 2637
8 93 17 103 3 Non-Fumeur 0 0 0 1 2637
9 94 29 123 1 Fumeur 0 0 0 1 2663
10 95 26 113 1 Fumeur 0 0 0 0 2665
# ℹ 180 more rows
Code
class(nais)
[1] "tbl_df" "tbl" "data.frame"
De plus, le comportement de la sélection de colonne est changé : on garde la structure de tableau de données même lorsqu’on sélectionne une seule colonne :
Code
str(naissance[, 2])
num [1:190] 19 33 20 21 18 21 22 17 29 26 ...
Code
str(nais[, 2])
tibble [190 × 1] (S3: tbl_df/tbl/data.frame)
$ AGE: num [1:190] 19 33 20 21 18 21 22 17 29 26 ...
Exercice 9 : dplyr::arrange()
- Utiliser la fonction
dplyr::arrange()
pour ordonner le tableau de données naissance par age, avec les poids de naissance par ordre décroissant pour les égalités.
Introduction au pipe %>%
Exercice 10
À l’aide des fonctions
dplyr::group_by()
,dplyr::summarize()
,dplyr::n()
,dplyr::filter()
etdplyr::arrange()
, construire le tableau de données donnant le poids moyen à la naissance par âge stratifié sur le statut tabagique, ainsi que le nombre d’observations correpondantes, et ordonné le résultat par ordre de poids moyen croissant en ne sélectionnant que les catéories pour lesquelles le poids moyen est inférieur à 2,5kg.Réécrire le code de la réponse précédente à l’aide de l’opérateur
%>%
Résumé
Le package dplyr
propose un ensemble de fonctions très simples à utiliser et permettant de réaliser toutes les opérations classiques sur les tableaux de données, avec une syntaxe très claire et compréhensible.
Pour aller plus loin, ou voir d’autres exemples, consulter cette page. On renverra également vers cet aide-mémoire.
En dehors du changement de syntaxe, les gains en temps de calcul sont souvent significatifs avec dplyr
. Il existe un autre package populaire pour faire des manipulations sur les (grands) jeux de données : data.table
. La syntaxe est un peu moins intuitive que dplyr
, mais l’efficacité est encore accrue.