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 R. 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 R.

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 :

# install.packages('dplyr')
library(dplyr)

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 R.

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().

R 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 :

  1. Charger le fichier de données nommé naissance_na.txt.

  2. Consulter la structure de l’objet créé (par exemple nommé naissance_na). Que remarquez-vous pour la variable LWT ?

  3. 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 fonction read.table() ou de ses dérivées). Puis vérifier la nouvelle structure de l’objet.

  4. A l’aide de la fonction transform(), transformer en facteur les variables RACE (1=blanche, 2=noire, 3=autres), HT (0=non, 1=oui) et UI (0=non, 1=oui) en une seule commande.

  5. 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).

  6. Utiliser la fonction dplyr::mutate() (du package dplyr) 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.

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
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() :

save(naissance, file = "naissance.Rdata")

L’avantage est que lorsqu’on recharge le fichier avec la fonction load(), il garde la bonne structure :

rm(naissance)
load("naissance.Rdata")
str(naissance)
## '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 :

save(naissance, mon_df, file = "naissance_mon_df.Rdata")
rm(naissance, mon_df)
class(mon_df)
## Error in eval(expr, envir, enclos): object 'mon_df' not found
load("naissance_mon_df.Rdata")
class(mon_df)
## [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, R 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 :

naissance_mon_df <- list(naissance = naissance, mon_df = mon_df)
save(naissance_mon_df, file = "naissance_mon_df.Rdata")
rm(naissance_mon_df)
load("naissance_mon_df.Rdata")

Opérations sur les tableaux de données

Sélection, suppression, ajout

Exercice 1 : Sélection de colonnes

  1. J’ai dénombré 10 façons de sélectionner la variable AGE. En gardant en tête qu’un data.frame est une liste de colonnes, essayer d’en trouver un maximum.

  2. Utiliser la fonction select du package dplyr pour sélectionner la variable AGE 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

  1. Construire un sous-dataframe ne contenant que les individus fumeurs, en utilisant [,], puis subset(), et enfin dplyr::filter().

  2. Donner les identifiants (colonne ID) des individus des individus présentant de l’hypertension (variable HT valant 1).

  3. Sélectionner le sous-dataframe ne contenant que les individus fumeurs et âgés de moins de 20 ans.

  4. 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 :

  1. Supprimer la première ligne du jeu de données.

  2. 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 : 85, 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 :

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
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
nais2 <- naissance[, c(1, 7:10)]
head(nais2)
##   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
nais_fus1 <- merge(nais1, nais2)
head(nais_fus1)
##   ID AGE LWT RACE      SMOKE PTL HT UI FVT  BWT
## 1  4  28 120    3     Fumeur   1  0  1   0  709
## 2 10  29 130    1 Non-Fumeur   0  0  1   2 1021
## 3 11  34 187    2     Fumeur   0  1  0   0 1135
## 4 13  25 105    3 Non-Fumeur   1  1  0   0 1330
## 5 15  25  85    3 Non-Fumeur   0  0  1   0 1474
## 6 16  27 150    3 Non-Fumeur   0  0  0   0 1588
# Ici, vu que les individus sont dans le même ordre on aurait pu utiliser la
# fonction cbind(), et la colonne ID n'est pas utile :
nais_fus2 <- cbind(nais1, nais2[, -1])
head(nais_fus2)
##   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
identical(nais_fus1, nais_fus2)
## [1] FALSE
# 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 :

desordre <- sample(1:nrow(naissance), nrow(naissance))
nais3 <- naissance[desordre, c(1, 7:10)]
head(nais3)
##      ID HT UI FVT  BWT
## 179  69  0  0   0 2424
## 164  49  0  0   0 2282
## 165  50  0  0   0 2296
## 129 225  0  0   1 4593
## 4    88  0  1   2 2594
## 72  163  0  0   2 3321
nais_fus3 <- merge(nais1, nais3)
head(nais_fus3)
##   ID AGE LWT RACE      SMOKE PTL HT UI FVT  BWT
## 1  4  28 120    3     Fumeur   1  0  1   0  709
## 2 10  29 130    1 Non-Fumeur   0  0  1   2 1021
## 3 11  34 187    2     Fumeur   0  1  0   0 1135
## 4 13  25 105    3 Non-Fumeur   1  1  0   0 1330
## 5 15  25  85    3 Non-Fumeur   0  0  1   0 1474
## 6 16  27 150    3 Non-Fumeur   0  0  0   0 1588
identical(nais_fus1, nais_fus3)
## [1] FALSE

Exercice 7 : join

  1. Consulter l’aide de dplyr::sample_n et trouver un moyens simple de permuter les lignes du jeu de données.

  2. Consulter l’aide de dplyr::join() (pour plus d’informations sur les fusions avec dplyr consulter cette page), puis utiliser la fonction dplyr::left_join() pour fusionner les tables nais1 et nais3.

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() :

nais_orderID <- naissance[order(naissance$ID), ]
knitr::kable(head(nais_orderID))
ID AGE LWT RACE SMOKE PTL HT UI FVT BWT
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
136 16 27 150 3 Non-Fumeur 0 0 0 0 1588

Maintenant, si on veut trier le tableau suivant l’âge par ordre décroissant :

nais_decAGE <- naissance[order(naissance$AGE, decreasing = TRUE), ]
knitr::kable(nais_decAGE[1:10, ])
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() :

nais_decAGE_decLWT <- naissance[order(naissance$AGE, naissance$LWT, decreasing = TRUE), 
    ]
knitr::kable(nais_decAGE_decLWT[1:10, ])
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 :

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()

  1. Utiliser la fonction dplyr::arrange() pour reproduire les tris précédents.

  2. 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 ?

  3. Rajouter une variable, nommée lettre au dataframe, contenant des lettres majuscules choisies alétoirement. On peut utiliser sample(letters, nrow(naissance), replace = TRUE) pour générer ce tirage aléatoire.

  4. 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 :

nais <- as.tbl(naissance)
nais
## # A tibble: 190 x 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
## # … with 180 more rows
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 :

str(naissance[, 2])
##  num [1:190] 19 33 20 21 18 21 22 17 29 26 ...
str(nais[, 2])
## Classes 'tbl_df', 'tbl' and 'data.frame':    190 obs. of  1 variable:
##  $ AGE: num  19 33 20 21 18 21 22 17 29 26 ...

Exercice 9 : dplyr::arrange()

  1. Utiliser la fonctiondplyr::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

  1. À l’aide des fonctions dplyr::group_by(), dplyr::summarize(), dplyr::n(), dplyr::filter() et dplyr::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.

  2. 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.