La version originale de ce chapitre a été écrite par Julien Barnier dans le cadre de son Introduction à R et au tidyverse.

dplyr est une extension facilitant le traitement et la manipulation de données contenues dans une ou plusieurs tables (qu’il s’agisse de data frame ou de tibble). Elle propose une syntaxe claire et cohérente, sous formes de verbes, pour la plupart des opérations de ce type.

Par ailleurs, les fonctions de dplyr sont en général plus rapides que leur équivalent sous R de base, elles permettent donc de traiter des données de grande dimension1.

dplyr part du principe que les données sont tidy (voir la section consacrée aux tidy data). Les fonctions de l’extension peuvent s’appliquer à des tableaux de type data.frame ou tibble, et elles retournent systématiquement un tibble (voir la section dédiée).

Préparation

dplyr fait partie du coeur du tidyverse, elle est donc chargée automatiquement avec :

On peut également la charger individuellement avec :

Dans ce qui suit on va utiliser les données du jeu de données nycflights13, contenu dans l’extension du même nom (qu’il faut donc avoir installé). Celui-ci correspond aux données de tous les vols au départ d’un des trois aéroports de New-York en 2013. Il a la particularité d’être réparti en trois tables :

  • flights contient des informations sur les vols : date, départ, destination, horaires, retard…
  • airports contient des informations sur les aéroports
  • airlines contient des données sur les compagnies aériennes

On va charger les trois tables du jeu de données :

Normalement trois objets correspondant aux trois tables ont dû apparaître dans votre environnement.

Les verbes de dplyr

La manipulation de données avec dplyr se fait en utilisant un nombre réduit de verbes, qui correspondent chacun à une action différente appliquée à un tableau de données.

slice

Le verbe slice sélectionne des lignes du tableau selon leur position. On lui passe un chiffre ou un vecteur de chiffres.

Si on souhaite sélectionner la 345e ligne du tableau airports :

# A tibble: 1 x 8
  faa   name                lat   lon   alt    tz dst   tzone            
  <chr> <chr>             <dbl> <dbl> <int> <dbl> <chr> <chr>            
1 CYF   Chefornak Airport  60.1 -164.    40   -9. A     America/Anchorage

Si on veut sélectionner les 5 premières lignes :

# A tibble: 5 x 8
  faa   name                            lat   lon   alt    tz dst   tzone      
  <chr> <chr>                         <dbl> <dbl> <int> <dbl> <chr> <chr>      
1 04G   Lansdowne Airport              41.1 -80.6  1044   -5. A     America/Ne~
2 06A   Moton Field Municipal Airport  32.5 -85.7   264   -6. A     America/Ch~
3 06C   Schaumburg Regional            42.0 -88.1   801   -6. A     America/Ch~
4 06N   Randall Airport                41.4 -74.4   523   -5. A     America/Ne~
5 09J   Jekyll Island Airport          31.1 -81.4    11   -5. A     America/Ne~

filter

filter sélectionne des lignes d’un tableau de données selon une condition. On lui passe en paramètre un test, et seules les lignes pour lesquelles ce test renvoit TRUE (vrai) sont conservées.

Par exemple, si on veut sélectionner les vols du mois de janvier, on peut filtrer sur la variable month de la manière suivante :

# A tibble: 27,004 x 19
    year month   day dep_time sched_dep_time dep_delay arr_time sched_arr_time
   <int> <int> <int>    <int>          <int>     <dbl>    <int>          <int>
 1  2013     1     1      517            515        2.      830            819
 2  2013     1     1      533            529        4.      850            830
 3  2013     1     1      542            540        2.      923            850
 4  2013     1     1      544            545       -1.     1004           1022
 5  2013     1     1      554            600       -6.      812            837
 6  2013     1     1      554            558       -4.      740            728
 7  2013     1     1      555            600       -5.      913            854
 8  2013     1     1      557            600       -3.      709            723
 9  2013     1     1      557            600       -3.      838            846
10  2013     1     1      558            600       -2.      753            745
# ... with 26,994 more rows, and 11 more variables: arr_delay <dbl>,
#   carrier <chr>, flight <int>, tailnum <chr>, origin <chr>, dest <chr>,
#   air_time <dbl>, distance <dbl>, hour <dbl>, minute <dbl>, time_hour <dttm>

Si on veut uniquement les vols avec un retard au départ (variable dep_delay) compris entre 10 et 15 minutes :

# A tibble: 14,919 x 19
    year month   day dep_time sched_dep_time dep_delay arr_time sched_arr_time
   <int> <int> <int>    <int>          <int>     <dbl>    <int>          <int>
 1  2013     1     1      611            600       11.      945            931
 2  2013     1     1      623            610       13.      920            915
 3  2013     1     1      743            730       13.     1107           1100
 4  2013     1     1      743            730       13.     1059           1056
 5  2013     1     1      851            840       11.     1215           1206
 6  2013     1     1      912            900       12.     1241           1220
 7  2013     1     1      914            900       14.     1058           1043
 8  2013     1     1      920            905       15.     1039           1025
 9  2013     1     1     1011           1001       10.     1133           1128
10  2013     1     1     1112           1100       12.     1440           1438
# ... with 14,909 more rows, and 11 more variables: arr_delay <dbl>,
#   carrier <chr>, flight <int>, tailnum <chr>, origin <chr>, dest <chr>,
#   air_time <dbl>, distance <dbl>, hour <dbl>, minute <dbl>, time_hour <dttm>

Si on passe plusieurs arguments à filter, celui-ci rajoute automatiquement une condition et entre les conditions. La ligne ci-dessus peut donc également être écrite de la manière suivante, avec le même résultat :

Enfin, on peut également placer des fonctions dans les tests, qui nous permettent par exemple de sélectionner les vols avec la plus grande distance :

# A tibble: 342 x 19
    year month   day dep_time sched_dep_time dep_delay arr_time sched_arr_time
   <int> <int> <int>    <int>          <int>     <dbl>    <int>          <int>
 1  2013     1     1      857            900       -3.     1516           1530
 2  2013     1     2      909            900        9.     1525           1530
 3  2013     1     3      914            900       14.     1504           1530
 4  2013     1     4      900            900        0.     1516           1530
 5  2013     1     5      858            900       -2.     1519           1530
 6  2013     1     6     1019            900       79.     1558           1530
 7  2013     1     7     1042            900      102.     1620           1530
 8  2013     1     8      901            900        1.     1504           1530
 9  2013     1     9      641            900     1301.     1242           1530
10  2013     1    10      859            900       -1.     1449           1530
# ... with 332 more rows, and 11 more variables: arr_delay <dbl>,
#   carrier <chr>, flight <int>, tailnum <chr>, origin <chr>, dest <chr>,
#   air_time <dbl>, distance <dbl>, hour <dbl>, minute <dbl>, time_hour <dttm>

select et rename

select permet de sélectionner des colonnes d’un tableau de données. Ainsi, si on veut extraire les colonnes lat et lon du tableau airports :

# A tibble: 1,458 x 2
     lat    lon
   <dbl>  <dbl>
 1  41.1  -80.6
 2  32.5  -85.7
 3  42.0  -88.1
 4  41.4  -74.4
 5  31.1  -81.4
 6  36.4  -82.2
 7  41.5  -84.5
 8  42.9  -76.8
 9  39.8  -76.6
10  48.1 -123. 
# ... with 1,448 more rows

Si on fait précéder le nom d’un -, la colonne est éliminée plutôt que sélectionnée :

# A tibble: 1,458 x 6
   faa   name                             alt    tz dst   tzone              
   <chr> <chr>                          <int> <dbl> <chr> <chr>              
 1 04G   Lansdowne Airport               1044   -5. A     America/New_York   
 2 06A   Moton Field Municipal Airport    264   -6. A     America/Chicago    
 3 06C   Schaumburg Regional              801   -6. A     America/Chicago    
 4 06N   Randall Airport                  523   -5. A     America/New_York   
 5 09J   Jekyll Island Airport             11   -5. A     America/New_York   
 6 0A9   Elizabethton Municipal Airport  1593   -5. A     America/New_York   
 7 0G6   Williams County Airport          730   -5. A     America/New_York   
 8 0G7   Finger Lakes Regional Airport    492   -5. A     America/New_York   
 9 0P2   Shoestring Aviation Airfield    1000   -5. U     America/New_York   
10 0S9   Jefferson County Intl            108   -8. A     America/Los_Angeles
# ... with 1,448 more rows

select comprend toute une série de fonctions facilitant la sélection de multiples colonnes. Par exemple, starts_with, ends_width, contains ou matches permettent d’exprimer des conditions sur les noms de variables :

# A tibble: 336,776 x 2
   dep_time dep_delay
      <int>     <dbl>
 1      517        2.
 2      533        4.
 3      542        2.
 4      544       -1.
 5      554       -6.
 6      554       -4.
 7      555       -5.
 8      557       -3.
 9      557       -3.
10      558       -2.
# ... with 336,766 more rows

La syntaxe colonne1:colonne2 permet de sélectionner toutes les colonnes situées entre colonne1 et colonne2 incluses2 :

# A tibble: 336,776 x 3
    year month   day
   <int> <int> <int>
 1  2013     1     1
 2  2013     1     1
 3  2013     1     1
 4  2013     1     1
 5  2013     1     1
 6  2013     1     1
 7  2013     1     1
 8  2013     1     1
 9  2013     1     1
10  2013     1     1
# ... with 336,766 more rows

select peut être utilisée pour réordonner les colonnes d’une table en utilisant la fonction everything(), qui sélectionne l’ensemble des colonnes non encore sélectionnées. Ainsi, si on souhaite faire passer la colonne name en première position de la table airports, on peut faire :

# A tibble: 1,458 x 8
   name                           faa     lat    lon   alt    tz dst   tzone   
   <chr>                          <chr> <dbl>  <dbl> <int> <dbl> <chr> <chr>   
 1 Lansdowne Airport              04G    41.1  -80.6  1044   -5. A     America~
 2 Moton Field Municipal Airport  06A    32.5  -85.7   264   -6. A     America~
 3 Schaumburg Regional            06C    42.0  -88.1   801   -6. A     America~
 4 Randall Airport                06N    41.4  -74.4   523   -5. A     America~
 5 Jekyll Island Airport          09J    31.1  -81.4    11   -5. A     America~
 6 Elizabethton Municipal Airport 0A9    36.4  -82.2  1593   -5. A     America~
 7 Williams County Airport        0G6    41.5  -84.5   730   -5. A     America~
 8 Finger Lakes Regional Airport  0G7    42.9  -76.8   492   -5. A     America~
 9 Shoestring Aviation Airfield   0P2    39.8  -76.6  1000   -5. U     America~
10 Jefferson County Intl          0S9    48.1 -123.    108   -8. A     America~
# ... with 1,448 more rows

Une variante de select est rename3, qui permet de renommer facilement des colonnes. On l’utilise en lui passant des paramètres de la forme nouveau_nom = ancien_nom. Ainsi, si on veut renommer les colonnes lon et lat de airports en longitude et latitude :

# A tibble: 1,458 x 8
   faa   name                 latitude longitude   alt    tz dst   tzone       
   <chr> <chr>                   <dbl>     <dbl> <int> <dbl> <chr> <chr>       
 1 04G   Lansdowne Airport        41.1     -80.6  1044   -5. A     America/New~
 2 06A   Moton Field Municip~     32.5     -85.7   264   -6. A     America/Chi~
 3 06C   Schaumburg Regional      42.0     -88.1   801   -6. A     America/Chi~
 4 06N   Randall Airport          41.4     -74.4   523   -5. A     America/New~
 5 09J   Jekyll Island Airpo~     31.1     -81.4    11   -5. A     America/New~
 6 0A9   Elizabethton Munici~     36.4     -82.2  1593   -5. A     America/New~
 7 0G6   Williams County Air~     41.5     -84.5   730   -5. A     America/New~
 8 0G7   Finger Lakes Region~     42.9     -76.8   492   -5. A     America/New~
 9 0P2   Shoestring Aviation~     39.8     -76.6  1000   -5. U     America/New~
10 0S9   Jefferson County In~     48.1    -123.    108   -8. A     America/Los~
# ... with 1,448 more rows

Si les noms de colonnes comportent des espaces ou des caractères spéciaux, on peut les entourer de guillemets (") ou de quotes inverses (`) :

# A tibble: 336,776 x 2
   `retard départ` `retard arrivée`
             <dbl>            <dbl>
 1              2.              11.
 2              4.              20.
 3              2.              33.
 4             -1.             -18.
 5             -6.             -25.
 6             -4.              12.
 7             -5.              19.
 8             -3.             -14.
 9             -3.              -8.
10             -2.               8.
# ... with 336,766 more rows

arrange

arrange réordonne les lignes d’un tableau selon une ou plusieurs colonnes.

Ainsi, si on veut trier le tableau flights selon le retard au départ croissant :

# A tibble: 336,776 x 19
    year month   day dep_time sched_dep_time dep_delay arr_time sched_arr_time
   <int> <int> <int>    <int>          <int>     <dbl>    <int>          <int>
 1  2013    12     7     2040           2123      -43.       40           2352
 2  2013     2     3     2022           2055      -33.     2240           2338
 3  2013    11    10     1408           1440      -32.     1549           1559
 4  2013     1    11     1900           1930      -30.     2233           2243
 5  2013     1    29     1703           1730      -27.     1947           1957
 6  2013     8     9      729            755      -26.     1002            955
 7  2013    10    23     1907           1932      -25.     2143           2143
 8  2013     3    30     2030           2055      -25.     2213           2250
 9  2013     3     2     1431           1455      -24.     1601           1631
10  2013     5     5      934            958      -24.     1225           1309
# ... with 336,766 more rows, and 11 more variables: arr_delay <dbl>,
#   carrier <chr>, flight <int>, tailnum <chr>, origin <chr>, dest <chr>,
#   air_time <dbl>, distance <dbl>, hour <dbl>, minute <dbl>, time_hour <dttm>

On peut trier selon plusieurs colonnes. Par exemple selon le mois, puis selon le retard au départ :

# A tibble: 336,776 x 19
    year month   day dep_time sched_dep_time dep_delay arr_time sched_arr_time
   <int> <int> <int>    <int>          <int>     <dbl>    <int>          <int>
 1  2013     1    11     1900           1930      -30.     2233           2243
 2  2013     1    29     1703           1730      -27.     1947           1957
 3  2013     1    12     1354           1416      -22.     1606           1650
 4  2013     1    21     2137           2159      -22.     2232           2316
 5  2013     1    20      704            725      -21.     1025           1035
 6  2013     1    12     2050           2110      -20.     2310           2355
 7  2013     1    12     2134           2154      -20.        4             50
 8  2013     1    14     2050           2110      -20.     2329           2355
 9  2013     1     4     2140           2159      -19.     2241           2316
10  2013     1    11     1947           2005      -18.     2209           2230
# ... with 336,766 more rows, and 11 more variables: arr_delay <dbl>,
#   carrier <chr>, flight <int>, tailnum <chr>, origin <chr>, dest <chr>,
#   air_time <dbl>, distance <dbl>, hour <dbl>, minute <dbl>, time_hour <dttm>

Si on veut trier selon une colonne par ordre décroissant, on lui applique la fonction desc() :

# A tibble: 336,776 x 19
    year month   day dep_time sched_dep_time dep_delay arr_time sched_arr_time
   <int> <int> <int>    <int>          <int>     <dbl>    <int>          <int>
 1  2013     1     9      641            900     1301.     1242           1530
 2  2013     6    15     1432           1935     1137.     1607           2120
 3  2013     1    10     1121           1635     1126.     1239           1810
 4  2013     9    20     1139           1845     1014.     1457           2210
 5  2013     7    22      845           1600     1005.     1044           1815
 6  2013     4    10     1100           1900      960.     1342           2211
 7  2013     3    17     2321            810      911.      135           1020
 8  2013     6    27      959           1900      899.     1236           2226
 9  2013     7    22     2257            759      898.      121           1026
10  2013    12     5      756           1700      896.     1058           2020
# ... with 336,766 more rows, and 11 more variables: arr_delay <dbl>,
#   carrier <chr>, flight <int>, tailnum <chr>, origin <chr>, dest <chr>,
#   air_time <dbl>, distance <dbl>, hour <dbl>, minute <dbl>, time_hour <dttm>

Combiné avec slice, arrange permet par exemple de sélectionner les trois vols ayant eu le plus de retard :

# A tibble: 3 x 19
   year month   day dep_time sched_dep_time dep_delay arr_time sched_arr_time
  <int> <int> <int>    <int>          <int>     <dbl>    <int>          <int>
1  2013     1     9      641            900     1301.     1242           1530
2  2013     6    15     1432           1935     1137.     1607           2120
3  2013     1    10     1121           1635     1126.     1239           1810
# ... with 11 more variables: arr_delay <dbl>, carrier <chr>, flight <int>,
#   tailnum <chr>, origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>,
#   hour <dbl>, minute <dbl>, time_hour <dttm>

mutate

mutate permet de créer de nouvelles colonnes dans le tableau de données, en général à partir de variables existantes.

Par exemple, la table airports contient l’altitude de l’aéroport en pieds. Si on veut créer une nouvelle variable alt_m avec l’altitude en mètres, on peut faire :

# A tibble: 1,458 x 3
   name                             alt  alt_m
   <chr>                          <int>  <dbl>
 1 Lansdowne Airport               1044 318.  
 2 Moton Field Municipal Airport    264  80.5 
 3 Schaumburg Regional              801 244.  
 4 Randall Airport                  523 159.  
 5 Jekyll Island Airport             11   3.35
 6 Elizabethton Municipal Airport  1593 486.  
 7 Williams County Airport          730 223.  
 8 Finger Lakes Regional Airport    492 150.  
 9 Shoestring Aviation Airfield    1000 305.  
10 Jefferson County Intl            108  32.9 
# ... with 1,448 more rows

On peut créer plusieurs nouvelles colonnes en une seule fois, et les expressions successives peuvent prendre en compte les résultats des calculs précédents. L’exemple suivant convertit d’abord la distance en kilomètres dans une variable distance_km, puis utilise cette nouvelle colonne pour calculer la vitesse en km/h.

# A tibble: 336,776 x 3
   distance distance_km vitesse
      <dbl>       <dbl>   <dbl>
 1    1400.       2253.    596.
 2    1416.       2279.    602.
 3    1089.       1753.    657.
 4    1576.       2536.    832.
 5     762.       1226.    634.
 6     719.       1157.    463.
 7    1065.       1714.    651.
 8     229.        369.    417.
 9     944.       1519.    651.
10     733.       1180.    513.
# ... with 336,766 more rows

À noter que mutate est évidemment parfaitement compatible avec les fonctions vues dans le chapitre sur les recodages : fonctions de forcats, if_else, case_when

L’avantage d’utiliser mutate est double. D’abord il permet d’éviter d’avoir à saisir le nom du tableau de données dans les conditions d’un if_else ou d’un case_when :

Utiliser mutate pour les recodages permet aussi de les intégrer dans un pipeline de traitement de données, concept présenté dans la section suivante.

Enchaîner les opérations avec le pipe

Quand on manipule un tableau de données, il est très fréquent d’enchaîner plusieurs opérations. On va par exemple filtrer pour extraire une sous-population, sélectionner des colonnes puis trier selon une variable.

Dans ce cas on peut le faire de deux manières différentes. La première est d’effectuer toutes les opérations en une fois en les emboîtant :

Cette notation a plusieurs inconvénients :

  • elle est peu lisible
  • les opérations apparaissent dans l’ordre inverse de leur réalisation. Ici on effectue d’abord le filter, puis le select, puis le arrange, alors qu’à la lecture du code c’est le arrange qui apparaît en premier.
  • Il est difficile de voir quel paramètre se rapporte à quelle fonction

Une autre manière de faire est d’effectuer les opérations les unes après les autres, en stockant les résultats intermédiaires dans un objet temporaire :

C’est nettement plus lisible, l’ordre des opérations est le bon, et les paramètres sont bien rattachés à leur fonction. Par contre, ça reste un peu “verbeux”, et on crée un objet temporaire tmp dont on n’a pas réellement besoin.

Pour simplifier et améliorer encore la lisibilité du code, on va utiliser un nouvel opérateur, baptisé pipe4. Le pipe se note %>%, et son fonctionnement est le suivant : si j’exécute expr %>% f, alors le résultat de l’expression expr, à gauche du pipe, sera passé comme premier argument à la fonction f, à droite du pipe, ce qui revient à exécuter f(expr).

Ainsi les deux expressions suivantes sont rigoureusement équivalentes :

Ce qui est intéressant dans cette histoire, c’est qu’on va pouvoir enchaîner les pipes. Plutôt que d’écrire :

On va pouvoir faire :

À chaque fois, le résultat de ce qui se trouve à gauche du pipe est passé comme premier argument à ce qui se trouve à droite : on part de l’objet flights, qu’on passe comme premier argument à la fonction filter, puis on passe le résultat de ce filter comme premier argument du select.

Le résultat final est le même avec les deux syntaxes, mais avec le pipe l’ordre des opérations correspond à l’ordre naturel de leur exécution, et on n’a pas eu besoin de créer d’objet intermédiaire.

Si la liste des fonctions enchaînées est longue, on peut les répartir sur plusieurs lignes à condition que l’opérateur %>% soit en fin de ligne :

On appelle une suite d’instructions de ce type un pipeline.

Évidemment, il est naturel de vouloir récupérer le résultat final d’un pipeline pour le stocker dans un objet. Par exemple, on peut stocker le résultat du pipeline ci-dessus dans un nouveau tableau delay_la de la manière suivante :

Dans ce cas, delay_la contiendra le tableau final, obtenu après application des trois instructions filter, select et arrange.

Cette notation n’est pas forcément très intuitive au départ. Il faut bien comprendre que c’est le résultat final, une fois application de toutes les opérations du pipeline, qui est renvoyé et stocké dans l’objet en début de ligne.

Une manière de le comprendre peut être de voir que la notation suivante :

est équivalente à :

L’utilisation du pipe n’est pas obligatoire, mais elle rend les scripts plus lisibles et plus rapides à saisir. On l’utilisera donc dans ce qui suit.

Opérations groupées

group_by

Un élément très important de dplyr est la fonction group_by. Elle permet de définir des groupes de lignes à partir des valeurs d’une ou plusieurs colonnes. Par exemple, on peut grouper les vols selon leur mois :

# A tibble: 336,776 x 21
# Groups:   month [12]
    year month   day dep_time sched_dep_time dep_delay arr_time sched_arr_time
   <int> <int> <int>    <int>          <int>     <dbl>    <int>          <int>
 1  2013     1     1      517            515        2.      830            819
 2  2013     1     1      533            529        4.      850            830
 3  2013     1     1      542            540        2.      923            850
 4  2013     1     1      544            545       -1.     1004           1022
 5  2013     1     1      554            600       -6.      812            837
 6  2013     1     1      554            558       -4.      740            728
 7  2013     1     1      555            600       -5.      913            854
 8  2013     1     1      557            600       -3.      709            723
 9  2013     1     1      557            600       -3.      838            846
10  2013     1     1      558            600       -2.      753            745
# ... with 336,766 more rows, and 13 more variables: arr_delay <dbl>,
#   carrier <chr>, flight <int>, tailnum <chr>, origin <chr>, dest <chr>,
#   air_time <dbl>, distance <dbl>, hour <dbl>, minute <dbl>, time_hour <dttm>,
#   distance_km <dbl>, vitesse <dbl>

Par défaut ceci ne fait rien de visible, à part l’apparition d’une mention Groups dans l’affichage du résultat. Mais à partir du moment où des groupes ont été définis, les verbes comme slice, mutate ou summarise vont en tenir compte lors de leurs opérations.

Par exemple, si on applique slice à un tableau préalablement groupé, il va sélectionner les lignes aux positions indiquées pour chaque groupe. Ainsi la commande suivante affiche le premier vol de chaque mois, selon leur ordre d’apparition dans le tableau :

# A tibble: 12 x 21
# Groups:   month [12]
    year month   day dep_time sched_dep_time dep_delay arr_time sched_arr_time
   <int> <int> <int>    <int>          <int>     <dbl>    <int>          <int>
 1  2013     1     1      517            515        2.      830            819
 2  2013     2     1      456            500       -4.      652            648
 3  2013     3     1        4           2159      125.      318             56
 4  2013     4     1      454            500       -6.      636            640
 5  2013     5     1        9           1655      434.      308           2020
 6  2013     6     1        2           2359        3.      341            350
 7  2013     7     1        1           2029      212.      236           2359
 8  2013     8     1       12           2130      162.      257             14
 9  2013     9     1        9           2359       10.      343            340
10  2013    10     1      447            500      -13.      614            648
11  2013    11     1        5           2359        6.      352            345
12  2013    12     1       13           2359       14.      446            445
# ... with 13 more variables: arr_delay <dbl>, carrier <chr>, flight <int>,
#   tailnum <chr>, origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>,
#   hour <dbl>, minute <dbl>, time_hour <dttm>, distance_km <dbl>,
#   vitesse <dbl>

Idem pour mutate : les opérations appliquées lors du calcul des valeurs des nouvelles colonnes sont aplliquée groupe de lignes par groupe de lignes. Dans l’exemple suivant, on ajoute une nouvelle colonne qui contient le retard moyen du mois correspondant :

# A tibble: 336,776 x 3
# Groups:   month [12]
   dep_delay month mean_delay_month
       <dbl> <int>            <dbl>
 1        2.     1             10.0
 2        4.     1             10.0
 3        2.     1             10.0
 4       -1.     1             10.0
 5       -6.     1             10.0
 6       -4.     1             10.0
 7       -5.     1             10.0
 8       -3.     1             10.0
 9       -3.     1             10.0
10       -2.     1             10.0
# ... with 336,766 more rows

Ceci peut permettre, par exemple, de déterminer si un retard donné est supérieur ou inférieur au retard moyen du mois en cours.

group_by peut aussi être utile avec filter, par exemple pour sélectionner les vols avec le retard au départ le plus important pour chaque mois :

# A tibble: 12 x 21
# Groups:   month [12]
    year month   day dep_time sched_dep_time dep_delay arr_time sched_arr_time
   <int> <int> <int>    <int>          <int>     <dbl>    <int>          <int>
 1  2013     1     9      641            900     1301.     1242           1530
 2  2013    10    14     2042            900      702.     2255           1127
 3  2013    11     3      603           1645      798.      829           1913
 4  2013    12     5      756           1700      896.     1058           2020
 5  2013     2    10     2243            830      853.      100           1106
 6  2013     3    17     2321            810      911.      135           1020
 7  2013     4    10     1100           1900      960.     1342           2211
 8  2013     5     3     1133           2055      878.     1250           2215
 9  2013     6    15     1432           1935     1137.     1607           2120
10  2013     7    22      845           1600     1005.     1044           1815
11  2013     8     8     2334           1454      520.      120           1710
12  2013     9    20     1139           1845     1014.     1457           2210
# ... with 13 more variables: arr_delay <dbl>, carrier <chr>, flight <int>,
#   tailnum <chr>, origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>,
#   hour <dbl>, minute <dbl>, time_hour <dttm>, distance_km <dbl>,
#   vitesse <dbl>

Attention : la clause group_by marche pour les verbes déjà vus précédemment, sauf pour arrange, qui par défaut trie la table sans tenir compte des groupes. Pour obtenir un tri par groupe, il faut lui ajouter l’argument .by_group = TRUE.

On peut voir la différence en comparant les deux résultats suivants :

# A tibble: 336,776 x 21
# Groups:   month [12]
    year month   day dep_time sched_dep_time dep_delay arr_time sched_arr_time
   <int> <int> <int>    <int>          <int>     <dbl>    <int>          <int>
 1  2013     1     9      641            900     1301.     1242           1530
 2  2013     6    15     1432           1935     1137.     1607           2120
 3  2013     1    10     1121           1635     1126.     1239           1810
 4  2013     9    20     1139           1845     1014.     1457           2210
 5  2013     7    22      845           1600     1005.     1044           1815
 6  2013     4    10     1100           1900      960.     1342           2211
 7  2013     3    17     2321            810      911.      135           1020
 8  2013     6    27      959           1900      899.     1236           2226
 9  2013     7    22     2257            759      898.      121           1026
10  2013    12     5      756           1700      896.     1058           2020
# ... with 336,766 more rows, and 13 more variables: arr_delay <dbl>,
#   carrier <chr>, flight <int>, tailnum <chr>, origin <chr>, dest <chr>,
#   air_time <dbl>, distance <dbl>, hour <dbl>, minute <dbl>, time_hour <dttm>,
#   distance_km <dbl>, vitesse <dbl>
# A tibble: 336,776 x 21
# Groups:   month [12]
    year month   day dep_time sched_dep_time dep_delay arr_time sched_arr_time
   <int> <int> <int>    <int>          <int>     <dbl>    <int>          <int>
 1  2013     1     9      641            900     1301.     1242           1530
 2  2013     1    10     1121           1635     1126.     1239           1810
 3  2013     1     1      848           1835      853.     1001           1950
 4  2013     1    13     1809            810      599.     2054           1042
 5  2013     1    16     1622            800      502.     1911           1054
 6  2013     1    23     1551            753      478.     1812           1006
 7  2013     1    10     1525            900      385.     1713           1039
 8  2013     1     1     2343           1724      379.      314           1938
 9  2013     1     2     2131           1512      379.     2340           1741
10  2013     1     7     2021           1415      366.     2332           1724
# ... with 336,766 more rows, and 13 more variables: arr_delay <dbl>,
#   carrier <chr>, flight <int>, tailnum <chr>, origin <chr>, dest <chr>,
#   air_time <dbl>, distance <dbl>, hour <dbl>, minute <dbl>, time_hour <dttm>,
#   distance_km <dbl>, vitesse <dbl>

summarise et count

summarise permet d’agréger les lignes du tableau en effectuant une opération “résumée” sur une ou plusieurs colonnes. Par exemple, si on souhaite connaître les retards moyens au départ et à l’arrivée pour l’ensemble des vols du tableau flights :

# A tibble: 1 x 2
  retard_dep retard_arr
       <dbl>      <dbl>
1       12.6       6.90

Cette fonction est en général utilisée avec group_by, puisqu’elle permet du coup d’agréger et résumer les lignes du tableau groupe par groupe. Si on souhaite calculer le délai maximum, le délai minimum et le délai moyen au départ pour chaque mois, on pourra faire :

# A tibble: 12 x 4
   month max_delay min_delay mean_delay
   <int>     <dbl>     <dbl>      <dbl>
 1     1     1301.      -30.      10.0 
 2     2      853.      -33.      10.8 
 3     3      911.      -25.      13.2 
 4     4      960.      -21.      13.9 
 5     5      878.      -24.      13.0 
 6     6     1137.      -21.      20.8 
 7     7     1005.      -22.      21.7 
 8     8      520.      -26.      12.6 
 9     9     1014.      -24.       6.72
10    10      702.      -25.       6.24
11    11      798.      -32.       5.44
12    12      896.      -43.      16.6 

summarise dispose d’un opérateur spécial, n(), qui retourne le nombre de lignes du groupe. Ainsi si on veut le nombre de vols par destination, on peut utiliser :

# A tibble: 105 x 2
   dest     nb
   <chr> <int>
 1 ABQ     254
 2 ACK     265
 3 ALB     439
 4 ANC       8
 5 ATL   17215
 6 AUS    2439
 7 AVL     275
 8 BDL     443
 9 BGR     375
10 BHM     297
# ... with 95 more rows

n() peut aussi être utilisée avec filter et mutate.

À noter que quand on veut compter le nombre de lignes par groupe, on peut utiliser directement la fonction count. Ainsi le code suivant est identique au précédent :

# A tibble: 105 x 2
   dest      n
   <chr> <int>
 1 ABQ     254
 2 ACK     265
 3 ALB     439
 4 ANC       8
 5 ATL   17215
 6 AUS    2439
 7 AVL     275
 8 BDL     443
 9 BGR     375
10 BHM     297
# ... with 95 more rows

Grouper selon plusieurs variables

On peut grouper selon plusieurs variables à la fois, il suffit de les indiquer dans la clause du group_by :

# A tibble: 1,113 x 3
# Groups:   month [12]
   month dest     nb
   <int> <chr> <int>
 1     8 ORD    1604
 2    10 ORD    1604
 3     5 ORD    1582
 4     9 ORD    1582
 5     7 ORD    1573
 6     6 ORD    1547
 7     7 ATL    1511
 8     8 ATL    1507
 9     8 LAX    1505
10     7 LAX    1500
# ... with 1,103 more rows

On peut également compter selon plusieurs variables :

# A tibble: 224 x 3
   origin dest      n
   <chr>  <chr> <int>
 1 JFK    LAX   11262
 2 LGA    ATL   10263
 3 LGA    ORD    8857
 4 JFK    SFO    8204
 5 LGA    CLT    6168
 6 EWR    ORD    6100
 7 JFK    BOS    5898
 8 LGA    MIA    5781
 9 JFK    MCO    5464
10 EWR    BOS    5327
# ... with 214 more rows

On peut utiliser plusieurs opérations de groupage dans le même pipeline. Ainsi, si on souhaite déterminer le couple origine/destination ayant le plus grand nombre de vols selon le mois de l’année, on devra procéder en deux étapes :

  • d’abord grouper selon mois, origine et destination pour calculer le nombre de vols
  • puis grouper uniquement selon le mois pour sélectionner la ligne avec la valeur maximale.

Au final, on obtient le code suivant :

# A tibble: 12 x 4
# Groups:   month [12]
   month origin dest     nb
   <int> <chr>  <chr> <int>
 1     1 JFK    LAX     937
 2     2 JFK    LAX     834
 3     3 JFK    LAX     960
 4     4 JFK    LAX     935
 5     5 JFK    LAX     960
 6     6 JFK    LAX     928
 7     7 JFK    LAX     985
 8     8 JFK    LAX     979
 9     9 JFK    LAX     925
10    10 JFK    LAX     965
11    11 JFK    LAX     907
12    12 JFK    LAX     947

Lorsqu’on effectue un group_by suivi d’un summarise, le tableau résultat est automatiquement dégroupé de la dernière variable de regroupement. Ainsi le tableau généré par le code suivant est groupé par month et origin :

# A tibble: 2,313 x 4
# Groups:   month, origin [?]
   month origin dest     nb
   <int> <chr>  <chr> <int>
 1     1 EWR    ALB      64
 2     1 EWR    ATL     362
 3     1 EWR    AUS      51
 4     1 EWR    AVL       2
 5     1 EWR    BDL      37
 6     1 EWR    BNA     111
 7     1 EWR    BOS     430
 8     1 EWR    BQN      31
 9     1 EWR    BTV     100
10     1 EWR    BUF     119
# ... with 2,303 more rows

Cela peut permettre “d’enchaîner” les opérations groupées. Dans l’exemple suivant on calcule le pourcentage des trajets pour chaque destination par rapport à tous les trajets du mois :

# A tibble: 1,113 x 4
# Groups:   month [12]
   month dest     nb pourcentage
   <int> <chr> <int>       <dbl>
 1     1 ALB      64     0.237  
 2     1 ATL    1396     5.17   
 3     1 AUS     169     0.626  
 4     1 AVL       2     0.00741
 5     1 BDL      37     0.137  
 6     1 BHM      25     0.0926 
 7     1 BNA     399     1.48   
 8     1 BOS    1245     4.61   
 9     1 BQN      93     0.344  
10     1 BTV     223     0.826  
# ... with 1,103 more rows

On peut à tout moment “dégrouper” un tableau à l’aide de ungroup. Ce serait par exemple nécessaire, dans l’exemple précédent, si on voulait calculer le pourcentage sur le nombre total de vols plutôt que sur le nombre de vols par mois :

# A tibble: 1,113 x 4
   month dest     nb pourcentage
   <int> <chr> <int>       <dbl>
 1     1 ALB      64    0.0190  
 2     1 ATL    1396    0.415   
 3     1 AUS     169    0.0502  
 4     1 AVL       2    0.000594
 5     1 BDL      37    0.0110  
 6     1 BHM      25    0.00742 
 7     1 BNA     399    0.118   
 8     1 BOS    1245    0.370   
 9     1 BQN      93    0.0276  
10     1 BTV     223    0.0662  
# ... with 1,103 more rows

À noter que count, par contre, renvoit un tableau non groupé :

# A tibble: 1,113 x 3
   month dest      n
   <int> <chr> <int>
 1     1 ALB      64
 2     1 ATL    1396
 3     1 AUS     169
 4     1 AVL       2
 5     1 BDL      37
 6     1 BHM      25
 7     1 BNA     399
 8     1 BOS    1245
 9     1 BQN      93
10     1 BTV     223
# ... with 1,103 more rows

Autres fonctions utiles

dplyr contient beaucoup d’autres fonctions utiles pour la manipulation de données.

sample_n et sample_frac

sample_n et sample_frac permettent de sélectionner un nombre de lignes ou une fraction des lignes d’un tableau aléatoirement. Ainsi si on veut choisir 5 lignes au hasard dans le tableau airports :

# A tibble: 5 x 9
  faa   name                          lat    lon   alt    tz dst   tzone  alt_m
  <chr> <chr>                       <dbl>  <dbl> <int> <dbl> <chr> <chr>  <dbl>
1 BLI   Bellingham Intl              48.8 -123.    170   -8. A     Amer~  51.8 
2 SIK   Sikeston Memorial Municipal  36.9  -89.6   315   -6. A     Amer~  96.0 
3 ZRA   Atlantic City Rail Terminal  39.4  -74.4     8   -5. A     Amer~   2.44
4 ANI   Aniak Airport                61.6 -160.     88   -9. A     Amer~  26.8 
5 GGG   East Texas Rgnl              32.4  -94.7   365   -6. A     Amer~ 111.  

Si on veut tirer au hasard 10% des lignes de flights :

# A tibble: 33,678 x 21
    year month   day dep_time sched_dep_time dep_delay arr_time sched_arr_time
   <int> <int> <int>    <int>          <int>     <dbl>    <int>          <int>
 1  2013     8    25      650            700      -10.      918            947
 2  2013    10     8      654            700       -6.      928           1003
 3  2013    10    15     1455           1459       -4.     1722           1737
 4  2013     1     9     1403           1335       28.     1655           1633
 5  2013     7    12     1053           1100       -7.     1342           1349
 6  2013     7    19      957           1000       -3.     1248           1319
 7  2013     1     1     1305           1315      -10.     1523           1520
 8  2013    10    28     1505           1452       13.     1754           1757
 9  2013     7    27     1443           1450       -7.     1634           1633
10  2013    11    16      855            900       -5.     1155           1212
# ... with 33,668 more rows, and 13 more variables: arr_delay <dbl>,
#   carrier <chr>, flight <int>, tailnum <chr>, origin <chr>, dest <chr>,
#   air_time <dbl>, distance <dbl>, hour <dbl>, minute <dbl>, time_hour <dttm>,
#   distance_km <dbl>, vitesse <dbl>

Ces fonctions sont utiles notamment pour faire de “l’échantillonnage” en tirant au hasard un certain nombre d’observations du tableau.

lead et lag

lead et lag permettent de décaler les observations d’une variable d’un cran vers l’arrière (pour lead) ou vers l’avant (pour lag).

[1]  2  3  4  5 NA
[1] NA  1  2  3  4

Ceci peut être utile pour des données de type “séries temporelles”. Par exemple, on peut facilement calculer l’écart entre le retard au départ de chaque vol et celui du vol précédent :

# A tibble: 336,776 x 3
   dep_delay_prev dep_delay dep_delay_diff
            <dbl>     <dbl>          <dbl>
 1             4.        2.            -2.
 2             2.        4.             2.
 3            -1.        2.             3.
 4            -6.       -1.             5.
 5            -4.       -6.            -2.
 6            -5.       -4.             1.
 7            -3.       -5.            -2.
 8            -3.       -3.             0.
 9            -2.       -3.            -1.
10            -2.       -2.             0.
# ... with 336,766 more rows

tally

tally est une fonction qui permet de compter le nombre d’observations d’un groupe :

# A tibble: 2,313 x 4
# Groups:   month, origin [?]
   month origin dest      n
   <int> <chr>  <chr> <int>
 1     1 EWR    ALB      64
 2     1 EWR    ATL     362
 3     1 EWR    AUS      51
 4     1 EWR    AVL       2
 5     1 EWR    BDL      37
 6     1 EWR    BNA     111
 7     1 EWR    BOS     430
 8     1 EWR    BQN      31
 9     1 EWR    BTV     100
10     1 EWR    BUF     119
# ... with 2,303 more rows

Lors de son premier appel, elle sera équivalente à un summarise(n = n()) ou à un count(). Là où la fonction est intelligente, c’est que si on l’appelle plusieurs fois successivement, elle prendra en compte l’existence d’un n déjà calculé et effectuera automatiquement un summarise(n = sum(n)) :

Using `n` as weighting variable
# A tibble: 36 x 3
# Groups:   month [?]
   month origin    nn
   <int> <chr>  <int>
 1     1 EWR     9893
 2     1 JFK     9161
 3     1 LGA     7950
 4     2 EWR     9107
 5     2 JFK     8421
 6     2 LGA     7423
 7     3 EWR    10420
 8     3 JFK     9697
 9     3 LGA     8717
10     4 EWR    10531
# ... with 26 more rows

distinct

distinct filtre les lignes du tableau pour ne conserver que les lignes distinctes, en supprimant toutes les lignes en double.

# A tibble: 365 x 2
     day month
   <int> <int>
 1     1     1
 2     2     1
 3     3     1
 4     4     1
 5     5     1
 6     6     1
 7     7     1
 8     8     1
 9     9     1
10    10     1
# ... with 355 more rows

On peut lui spécifier une liste de variables : dans ce cas, pour toutes les observations ayant des valeurs identiques pour les variables en question, distinct ne conservera que la première d’entre elles.

# A tibble: 365 x 2
   month   day
   <int> <int>
 1     1     1
 2     1     2
 3     1     3
 4     1     4
 5     1     5
 6     1     6
 7     1     7
 8     1     8
 9     1     9
10     1    10
# ... with 355 more rows

L’option .keep_all permet, dans l’opération précédente, de conserver l’ensemble des colonnes du tableau :

# A tibble: 365 x 21
    year month   day dep_time sched_dep_time dep_delay arr_time sched_arr_time
   <int> <int> <int>    <int>          <int>     <dbl>    <int>          <int>
 1  2013     1     1      517            515        2.      830            819
 2  2013     1     2       42           2359       43.      518            442
 3  2013     1     3       32           2359       33.      504            442
 4  2013     1     4       25           2359       26.      505            442
 5  2013     1     5       14           2359       15.      503            445
 6  2013     1     6       16           2359       17.      451            442
 7  2013     1     7       49           2359       50.      531            444
 8  2013     1     8      454            500       -6.      625            648
 9  2013     1     9        2           2359        3.      432            444
10  2013     1    10        3           2359        4.      426            437
# ... with 355 more rows, and 13 more variables: arr_delay <dbl>,
#   carrier <chr>, flight <int>, tailnum <chr>, origin <chr>, dest <chr>,
#   air_time <dbl>, distance <dbl>, hour <dbl>, minute <dbl>, time_hour <dttm>,
#   distance_km <dbl>, vitesse <dbl>

Ressources

Toutes les ressources ci-dessous sont en anglais…

Le livre R for data science, librement accessible en ligne, contient plusieurs chapitres très complets sur la manipulation des données, notamment :

Le site de l’extension comprend une liste des fonctions et les pages d’aide associées, mais aussi une introduction au package et plusieurs articles dont un spécifiquement sur les jointures.

Une “antisèche” très synthétique est également accessible depuis RStudio, en allant dans le menu Help puis Cheatsheets et Data Transformation with dplyr.

Enfin, on trouvera des exercices dans l’Introduction à R et au tidyverse de Julien Barnier.

dplyr et data.table

Pour ceux travaillant également avec l’extension data.table, il est possible de concilier tibble et data.table avec l’extension dtplyr et sa fonction tbl_dt.

[1] "tbl_dt"     "tbl"        "data.table" "data.frame"

Le tableau de données est à la fois compatible avec data.table (et notamment sa syntaxe particulière des crochets) et les verbes de dplyr.

Pour décrouvrir data.table, voir le chapitre dédié.

dplyr et survey

L’extension srvyr vise à permettre d’utiliser les verbes de dplyr avec les plans d’échantillonnage complexe définis avec survey. Le fonctionnement de cette extension est expliqué dans une vignette dédiée : https://cran.r-project.org/web/packages/srvyr/vignettes/srvyr-vs-survey.html.


  1. Elles sont cependant moins rapides que les fonctions de data.table, voir le chapitre dédié

  2. À noter que cette opération est un peu plus “fragile” que les autres, car si l’ordre des colonnes change elle peut renvoyer un résultat différent.

  3. Il est également possible de renommer des colonnes directement avec select, avec la même syntaxe que pour rename.

  4. Le pipe a été introduit à l’origine par l’extension magrittr, et repris par dplyr