10  Manipuler les données avec dplyr

dplyr est une extension facilitant le traitement et la manipulation de données contenues dans une ou plusieurs tables. Elle propose une syntaxe claire et cohérente, sous formes de verbes, pour la plupart des opérations de ce type.

dplyr part du principe que les données sont organisées selon le modèle des tidy data (voir Section 6.3). Les fonctions de l’extension peuvent s’appliquer à des tableaux de type data.frame ou tibble, et elles retournent systématiquement un tibble (voir Section 6.4).

Le code présent dans ce document nécessite d’avoir installé la version 1.0 de dplyr (ou plus récente).

10.1 Préparation

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

library(tidyverse)

On peut également la charger individuellement.

library(dplyr)

Dans ce qui suit on va utiliser le 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 :

library(nycflights13)
## Chargement des trois tables
data(flights)
data(airports)
data(airlines)

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

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

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

slice(airports, 345)
#> # A tibble: 1 × 8
#>   faa   name                lat   lon   alt    tz dst   tzone            
#>   <chr> <chr>             <dbl> <dbl> <dbl> <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 :

slice(airports, 1:5)
#> # A tibble: 5 × 8
#>   faa   name                            lat   lon   alt    tz dst   tzone       
#>   <chr> <chr>                         <dbl> <dbl> <dbl> <dbl> <chr> <chr>       
#> 1 04G   Lansdowne Airport              41.1 -80.6  1044    -5 A     America/New…
#> 2 06A   Moton Field Municipal Airport  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 Airport          31.1 -81.4    11    -5 A     America/New…

slice propose plusieurs variantes utiles, dont slice_head et slice_tail, qui permettent de sélectionner les premières ou les dernières lignes du tableau (on peut spécifier le nombre de lignes souhaitées avec n, ou la proportion avec prop).

slice_tail(airports, n = 3)
#> # A tibble: 3 × 8
#>   faa   name                        lat   lon   alt    tz dst   tzone           
#>   <chr> <chr>                     <dbl> <dbl> <dbl> <dbl> <chr> <chr>           
#> 1 ZWI   Wilmington Amtrak Station  39.7 -75.6     0    -5 A     America/New_York
#> 2 ZWU   Washington Union Station   38.9 -77.0    76    -5 A     America/New_York
#> 3 ZYP   Penn Station               40.8 -74.0    35    -5 A     America/New_York
slice_head(airlines, prop = 0.2)
#> # A tibble: 3 × 2
#>   carrier name                  
#>   <chr>   <chr>                 
#> 1 9E      Endeavor Air Inc.     
#> 2 AA      American Airlines Inc.
#> 3 AS      Alaska Airlines Inc.

Autres variantes utiles, slice_min et slice_max permettent de sélectionner les lignes avec les valeurs les plus grandes ou les plus petite d’une variable donnée. Ainsi, la commande suivante sélectionne le vol ayant le retard au départ le plus faible.

slice_min(flights, dep_delay)
#> # A tibble: 1 × 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
#> # ℹ 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 aussi spécifier le nombre de lignes souhaitées, par exemple la commande suivante retourne les 5 aéroports avec l’altitude la plus élevée (en cas de valeurs ex-aequo, il se peut que le nombre de lignes retournées soit plus élevé que celui demandé).

slice_max(airports, alt, n = 5)
#> # A tibble: 5 × 8
#>   faa   name                              lat   lon   alt    tz dst   tzone     
#>   <chr> <chr>                           <dbl> <dbl> <dbl> <dbl> <chr> <chr>     
#> 1 TEX   Telluride                        38.0 -108.  9078    -7 A     America/D…
#> 2 TVL   Lake Tahoe Airport               38.9 -120.  8544    -8 A     America/L…
#> 3 ASE   Aspen Pitkin County Sardy Field  39.2 -107.  7820    -7 A     America/D…
#> 4 GUC   Gunnison - Crested Butte         38.5 -107.  7678    -7 A     America/D…
#> 5 BCE   Bryce Canyon                     37.7 -112.  7590    -7 A     America/D…

10.2.2 filter

filter sélectionne des lignes d’une table selon une condition. On lui passe en paramètre un test, et seules les lignes pour lesquelles ce test renvoie TRUE (vrai) sont conservées. Pour plus d’informations sur les tests et leur syntaxe, voir Section 9.2.

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 :

filter(flights, month == 1)
#> # A tibble: 27,004 × 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
#> # ℹ 26,994 more rows
#> # ℹ 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 :

filter(flights, dep_delay >= 10 & dep_delay <= 15)
#> # A tibble: 14,919 × 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
#> # ℹ 14,909 more rows
#> # ℹ 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 commande précédente peut donc être écrite de la manière suivante, avec le même résultat :

filter(flights, dep_delay >= 10, dep_delay <= 15)

On peut également placer des fonctions dans les tests, qui nous permettent par exemple de sélectionner les vols ayant une distance supérieure à la distance médiane :

filter(flights, distance > median(distance))
#> # A tibble: 167,133 × 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      555            600        -5      913            854
#>  6  2013     1     1      557            600        -3      838            846
#>  7  2013     1     1      558            600        -2      849            851
#>  8  2013     1     1      558            600        -2      853            856
#>  9  2013     1     1      558            600        -2      924            917
#> 10  2013     1     1      558            600        -2      923            937
#> # ℹ 167,123 more rows
#> # ℹ 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>

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

select(airports, lat, lon)
#> # A tibble: 1,458 × 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. 
#> # ℹ 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 :

select(airports, -lat, -lon)
#> # A tibble: 1,458 × 6
#>    faa   name                             alt    tz dst   tzone              
#>    <chr> <chr>                          <dbl> <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
#> # ℹ 1,448 more rows

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

select(flights, starts_with("dep_"))
#> # A tibble: 336,776 × 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
#> # ℹ 336,766 more rows

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

select(flights, year:day)
#> # A tibble: 336,776 × 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
#> # ℹ 336,766 more rows

select propose de nombreuses autres possibilités de sélection qui sont décrites dans la documentation de l’extension tidyselect.

Une variante de select est rename2, qui permet de renommer 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 :

rename(airports, longitude = lon, latitude = lat)
#> # A tibble: 1,458 × 8
#>    faa   name                         latitude longitude   alt    tz dst   tzone
#>    <chr> <chr>                           <dbl>     <dbl> <dbl> <dbl> <chr> <chr>
#>  1 04G   Lansdowne Airport                41.1     -80.6  1044    -5 A     Amer…
#>  2 06A   Moton Field Municipal Airpo…     32.5     -85.7   264    -6 A     Amer…
#>  3 06C   Schaumburg Regional              42.0     -88.1   801    -6 A     Amer…
#>  4 06N   Randall Airport                  41.4     -74.4   523    -5 A     Amer…
#>  5 09J   Jekyll Island Airport            31.1     -81.4    11    -5 A     Amer…
#>  6 0A9   Elizabethton Municipal Airp…     36.4     -82.2  1593    -5 A     Amer…
#>  7 0G6   Williams County Airport          41.5     -84.5   730    -5 A     Amer…
#>  8 0G7   Finger Lakes Regional Airpo…     42.9     -76.8   492    -5 A     Amer…
#>  9 0P2   Shoestring Aviation Airfield     39.8     -76.6  1000    -5 U     Amer…
#> 10 0S9   Jefferson County Intl            48.1    -123.    108    -8 A     Amer…
#> # ℹ 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 (`) :

tmp <- rename(
    flights,
    "retard départ" = dep_delay,
    "retard arrivée" = arr_delay
)
select(tmp, `retard départ`, `retard arrivée`)
#> # A tibble: 336,776 × 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
#> # ℹ 336,766 more rows

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

arrange(flights, dep_delay)
#> # A tibble: 336,776 × 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
#> # ℹ 336,766 more rows
#> # ℹ 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 :

arrange(flights, month, dep_delay)
#> # A tibble: 336,776 × 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
#> # ℹ 336,766 more rows
#> # ℹ 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() :

arrange(flights, desc(dep_delay))
#> # A tibble: 336,776 × 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
#> # ℹ 336,766 more rows
#> # ℹ 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 :

tmp <- arrange(flights, desc(dep_delay))
slice(tmp, 1:3)
#> # A tibble: 3 × 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
#> # ℹ 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>

10.2.5 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 flights contient la durée du vol en minutes.. Si on veut créer une nouvelle variable duree_h avec cette durée en heures, on peut faire :

flights <- mutate(flights, duree_h = air_time / 60)

select(flights, air_time, duree_h)
#> # A tibble: 336,776 × 2
#>    air_time duree_h
#>       <dbl>   <dbl>
#>  1      227   3.78 
#>  2      227   3.78 
#>  3      160   2.67 
#>  4      183   3.05 
#>  5      116   1.93 
#>  6      150   2.5  
#>  7      158   2.63 
#>  8       53   0.883
#>  9      140   2.33 
#> 10      138   2.3  
#> # ℹ 336,766 more rows

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

flights <- mutate(
    flights,
    duree_h = air_time / 60,
    distance_km = distance / 0.62137,
    vitesse = distance_km / duree_h
)

select(flights, air_time, duree_h, distance, distance_km, vitesse)
#> # A tibble: 336,776 × 5
#>    air_time duree_h distance distance_km vitesse
#>       <dbl>   <dbl>    <dbl>       <dbl>   <dbl>
#>  1      227   3.78      1400       2253.    596.
#>  2      227   3.78      1416       2279.    602.
#>  3      160   2.67      1089       1753.    657.
#>  4      183   3.05      1576       2536.    832.
#>  5      116   1.93       762       1226.    634.
#>  6      150   2.5        719       1157.    463.
#>  7      158   2.63      1065       1714.    651.
#>  8       53   0.883      229        369.    417.
#>  9      140   2.33       944       1519.    651.
#> 10      138   2.3        733       1180.    513.
#> # ℹ 336,766 more rows

À noter que mutate est évidemment parfaitement compatible avec les fonctions vues Chapitre 9 sur les recodages : fct_recode, ifelse, 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 ifelse ou d’un case_when :

flights <- mutate(
    flights,
    type_retard = case_when(
        dep_delay > 0 & arr_delay > 0 ~ "Retard départ et arrivée",
        dep_delay > 0 & arr_delay <= 0 ~ "Retard départ",
        dep_delay <= 0 & arr_delay > 0 ~ "Retard arrivée",
        TRUE ~ "Aucun retard"
    )
)

Ensuite, il permet aussi d’intégrer ces recodages dans un pipeline de traitement de données, concept présenté dans la section suivante.

10.3 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 extraire une sous-population avec filter, sélectionner des colonnes avec select puis trier selon une variable avec arrange, etc.

Quand on veut enchaîner des opérations, on peut le faire de différentes manières. La première est d’effectuer toutes les opérations en une fois en les “emboîtant” :

arrange(select(filter(flights, dest == "LAX"), dep_delay, arr_delay), dep_delay)

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 :

tmp <- filter(flights, dest == "LAX")
tmp <- select(tmp, dep_delay, arr_delay)
arrange(tmp, dep_delay)

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é pipe3. 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 :

filter(flights, dest == "LAX")
flights %>% filter(dest == "LAX")

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

select(filter(flights, dest == "LAX"), dep_delay, arr_delay)

On va pouvoir faire :

flights %>% filter(dest == "LAX") %>% select(dep_delay, arr_delay)

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

flights %>%
  filter(dest == "LAX") %>%
  select(dep_delay, arr_delay) %>%
  arrange(dep_delay)
Note

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. On peut stocker le résultat du pipeline ci-dessus dans un nouveau tableau delay_la de la manière suivante :

delay_la <- flights %>%
  filter(dest == "LAX") %>%
  select(dep_delay, arr_delay) %>%
  arrange(dep_delay)

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 :

delay_la <- flights %>%
  filter(dest == "LAX") %>%
  select(dep_delay, arr_delay)

est équivalente à :

delay_la <- (flights %>% filter(dest == "LAX") %>% select(dep_delay, arr_delay))
Note

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.

Avertissement

Depuis la version 4.1, R propose un pipe “natif”, qui fonctionne partout, même si on n’utilise pas les extensions du tidyverse. Celui-ci est noté |>.

Il s’utilise de la même manière que %>% :

flights |> filter(dest == "LAX")

Ce pipe natif est à la fois un peu plus rapide et un peu moins souple. Par exemple, il est possible avec %>% d’appeler une fonction sans mettre de parenthèses :

df %>% View

Ce n’est pas possible d’omettre les parenthèses avec |>, on doit obligatoirement faire :

df |> View()

Dans la suite de ce document on privilégiera (pour l’instant) le pipe du tidyverse %>%, pour des raisons de compatibilité avec des versions de R moins récentes.

10.4 Opérations groupées

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

flights %>% group_by(month)
#> # A tibble: 336,776 × 22
#> # 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
#> # ℹ 336,766 more rows
#> # ℹ 14 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>, duree_h <dbl>,
#> #   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 :

flights %>% group_by(month) %>% slice(1)
#> # A tibble: 12 × 22
#> # 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
#> # ℹ 14 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>, duree_h <dbl>,
#> #   distance_km <dbl>, vitesse <dbl>

Plus utile, en utilisant une variante comme slice_min ou slice_max, on peut sélectionner les lignes ayant les valeurs les plus grandes ou les plus petites pour chaque groupe. Par exemple la commande suivant sélectionne, pour chaque mois de l’année, le vol ayant eu le retard le plus important.

flights %>% group_by(month) %>% slice_max(dep_delay)
#> # A tibble: 12 × 22
#> # 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     2    10     2243            830       853      100           1106
#>  3  2013     3    17     2321            810       911      135           1020
#>  4  2013     4    10     1100           1900       960     1342           2211
#>  5  2013     5     3     1133           2055       878     1250           2215
#>  6  2013     6    15     1432           1935      1137     1607           2120
#>  7  2013     7    22      845           1600      1005     1044           1815
#>  8  2013     8     8     2334           1454       520      120           1710
#>  9  2013     9    20     1139           1845      1014     1457           2210
#> 10  2013    10    14     2042            900       702     2255           1127
#> 11  2013    11     3      603           1645       798      829           1913
#> 12  2013    12     5      756           1700       896     1058           2020
#> # ℹ 14 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>, duree_h <dbl>,
#> #   distance_km <dbl>, vitesse <dbl>

Idem pour mutate : les opérations appliquées lors du calcul des valeurs des nouvelles colonnes sont appliquées groupe de lignes par groupe de lignes. Dans l’exemple suivant, on ajoute une nouvelle colonne qui contient le retard moyen pour chaque compagnie aérienne. Cette valeur est donc différente d’une compagnie à une autre, mais identique pour tous les vols d’une même compagnie :

flights %>%
  group_by(carrier) %>%
  mutate(mean_delay_carrier = mean(dep_delay, na.rm = TRUE)) %>%
  select(dep_delay, mean_delay_carrier)
#> Adding missing grouping variables: `carrier`
#> # A tibble: 336,776 × 3
#> # Groups:   carrier [16]
#>    carrier dep_delay mean_delay_carrier
#>    <chr>       <dbl>              <dbl>
#>  1 UA              2              12.1 
#>  2 UA              4              12.1 
#>  3 AA              2               8.59
#>  4 B6             -1              13.0 
#>  5 DL             -6               9.26
#>  6 UA             -4              12.1 
#>  7 B6             -5              13.0 
#>  8 EV             -3              20.0 
#>  9 B6             -3              13.0 
#> 10 AA             -2               8.59
#> # ℹ 336,766 more rows

Ceci peut permettre, par exemple, de déterminer si un retard donné est supérieur ou inférieur au retard médian de la compagnie :

flights %>%
  group_by(carrier) %>%
  mutate(
      median_delay = median(dep_delay, na.rm = TRUE),
      delay_carrier = ifelse(
          dep_delay > median_delay,
          "Supérieur",
          "Inférieur ou égal"
      )
  ) %>%
  select(dep_delay, median_delay, delay_carrier)
#> Adding missing grouping variables: `carrier`
#> # A tibble: 336,776 × 4
#> # Groups:   carrier [16]
#>    carrier dep_delay median_delay delay_carrier    
#>    <chr>       <dbl>        <dbl> <chr>            
#>  1 UA              2            0 Supérieur        
#>  2 UA              4            0 Supérieur        
#>  3 AA              2           -3 Supérieur        
#>  4 B6             -1           -1 Inférieur ou égal
#>  5 DL             -6           -2 Inférieur ou égal
#>  6 UA             -4            0 Inférieur ou égal
#>  7 B6             -5           -1 Inférieur ou égal
#>  8 EV             -3           -1 Inférieur ou égal
#>  9 B6             -3           -1 Inférieur ou égal
#> 10 AA             -2           -3 Supérieur        
#> # ℹ 336,766 more rows

group_by peut aussi être utile avec filter, par exemple pour sélectionner pour chaque mois les vols avec un retard au départ plus élevé que le retard moyen ce mois-ci.

flights %>%
  group_by(month) %>%
  filter(dep_delay >= mean(dep_delay, na.rm = TRUE))
#> # A tibble: 78,164 × 22
#> # 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      611            600        11      945            931
#>  2  2013     1     1      623            610        13      920            915
#>  3  2013     1     1      632            608        24      740            728
#>  4  2013     1     1      732            645        47     1011            941
#>  5  2013     1     1      743            730        13     1107           1100
#>  6  2013     1     1      743            730        13     1059           1056
#>  7  2013     1     1      749            710        39      939            850
#>  8  2013     1     1      811            630       101     1047            830
#>  9  2013     1     1      826            715        71     1136           1045
#> 10  2013     1     1      848           1835       853     1001           1950
#> # ℹ 78,154 more rows
#> # ℹ 14 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>, duree_h <dbl>,
#> #   distance_km <dbl>, vitesse <dbl>
Avertissement

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 :

flights %>%
  group_by(month) %>%
  arrange(desc(dep_delay))
#> # A tibble: 336,776 × 22
#> # 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
#> # ℹ 336,766 more rows
#> # ℹ 14 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>, duree_h <dbl>,
#> #   distance_km <dbl>, vitesse <dbl>
flights %>%
  group_by(month) %>%
  arrange(desc(dep_delay), .by_group = TRUE)
#> # A tibble: 336,776 × 22
#> # 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
#> # ℹ 336,766 more rows
#> # ℹ 14 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>, duree_h <dbl>,
#> #   distance_km <dbl>, vitesse <dbl>

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

flights %>%
  summarise(
      retard_dep = mean(dep_delay, na.rm = TRUE),
      retard_arr = mean(arr_delay, na.rm = TRUE)
  )
#> # A tibble: 1 × 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 :

flights %>%
  group_by(month) %>%
  summarise(
      max_delay = max(dep_delay, na.rm = TRUE),
      min_delay = min(dep_delay, na.rm = TRUE),
      mean_delay = mean(dep_delay, na.rm = TRUE)
  )
#> # A tibble: 12 × 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 :

flights %>%
  group_by(dest) %>%
  summarise(nb = n())
#> # A tibble: 105 × 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
#> # ℹ 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, il est plus simple d’utiliser directement la fonction count. Ainsi le code suivant est identique au précédent :

flights %>%
  count(dest)
#> # A tibble: 105 × 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
#> # ℹ 95 more rows

10.4.3 Grouper selon plusieurs variables

On peut grouper selon plusieurs variables à la fois, il suffit de les indiquer dans la clause du group_by. Le pipeline suivant calcule le retard moyen au départ pour chaque mois et pour chaque destination, et trie le résultat par retard décroissant :

flights %>%
  group_by(month, dest) %>%
  summarise(retard_moyen = mean(dep_delay, na.rm = TRUE)) %>%
  arrange(desc(retard_moyen))
#> `summarise()` has grouped output by 'month'. You can override using the
#> `.groups` argument.
#> # A tibble: 1,113 × 3
#> # Groups:   month [12]
#>    month dest  retard_moyen
#>    <int> <chr>        <dbl>
#>  1    12 BZN           75  
#>  2     7 TUL           72.6
#>  3     3 DSM           71.0
#>  4     7 CAE           69.4
#>  5    11 SBN           67.5
#>  6     7 BHM           64.6
#>  7     7 TYS           60.6
#>  8     6 BHM           57.2
#>  9     1 TUL           55.2
#> 10     1 SAV           54.8
#> # ℹ 1,103 more rows

On peut également utiliser count sur plusieurs variables. Les commandes suivantes comptent le nombre de vols pour chaque couple aéroport de départ / aéroport d’arrivée, et trie le résultat par nombre de vols décroissant. Ici la colonne qui contient le nombre de vols, créée par count, s’appelle n par défaut :

flights %>%
  count(origin, dest) %>%
  arrange(desc(n))
#> # A tibble: 224 × 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
#> # ℹ 214 more rows

On peut utiliser plusieurs opérations de groupage dans le même pipeline. Ainsi, si on souhaite déterminer le couple aéroport de départ / aéroport d’arrivée ayant le retard moyen au départ le plus élevé pour chaque mois de l’année, on devra procéder en deux étapes :

  • d’abord grouper selon mois, aéroports d’origine et d’arrivée pour calculer le retard moyen
  • puis grouper uniquement selon le mois pour sélectionner le mois avec le retard moyen maximal.

Au final, on obtient le code suivant :

flights %>%
  group_by(month, origin, dest) %>%
  summarise(retard_moyen = mean(dep_delay, na.rm = TRUE)) %>%
  group_by(month) %>%
  slice_max(retard_moyen)
#> `summarise()` has grouped output by 'month', 'origin'. You can override using
#> the `.groups` argument.
#> # A tibble: 12 × 4
#> # Groups:   month [12]
#>    month origin dest  retard_moyen
#>    <int> <chr>  <chr>        <dbl>
#>  1     1 EWR    TUL           55.2
#>  2     2 EWR    DSM           48.6
#>  3     3 EWR    DSM           71.0
#>  4     4 EWR    OKC           47.0
#>  5     5 EWR    TYS           60.6
#>  6     6 EWR    TYS           68.2
#>  7     7 EWR    CAE           81.5
#>  8     8 LGA    GSO           50.1
#>  9     9 LGA    MSN           24.7
#> 10    10 EWR    CAE           50.1
#> 11    11 LGA    SBN           67.5
#> 12    12 EWR    BZN           75

10.4.4 Dégroupage

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 seulement groupé par month et origin :

flights %>%
  group_by(month, origin, dest) %>%
  summarise(retard_moyen = mean(dep_delay, na.rm = TRUE))
#> `summarise()` has grouped output by 'month', 'origin'. You can override using
#> the `.groups` argument.
#> # A tibble: 2,313 × 4
#> # Groups:   month, origin [36]
#>    month origin dest  retard_moyen
#>    <int> <chr>  <chr>        <dbl>
#>  1     1 EWR    ALB          41.4 
#>  2     1 EWR    ATL           8.07
#>  3     1 EWR    AUS           6.67
#>  4     1 EWR    AVL          25.5 
#>  5     1 EWR    BDL          21.1 
#>  6     1 EWR    BNA          16.3 
#>  7     1 EWR    BOS           8.99
#>  8     1 EWR    BQN          12.3 
#>  9     1 EWR    BTV          20.5 
#> 10     1 EWR    BUF          23.1 
#> # ℹ 2,303 more rows

dplyr nous le signale d’ailleurs via un message d’avertissement : summarise() has grouped output by 'month', 'origin'.

Ce dégroupage progressif peut permettre “d’enchaîner” les opérations groupées. Dans l’exemple suivant on calcule le retard moyen au départ par destination et on conserve les trois retards les plus importants pour chaque mois.

flights %>%
  group_by(month, dest) %>%
  summarise(retard_moyen = mean(dep_delay, na.rm = TRUE)) %>%
  slice_max(retard_moyen, n = 3)
#> `summarise()` has grouped output by 'month'. You can override using the
#> `.groups` argument.
#> # A tibble: 36 × 3
#> # Groups:   month [12]
#>    month dest  retard_moyen
#>    <int> <chr>        <dbl>
#>  1     1 TUL           55.2
#>  2     1 SAV           54.8
#>  3     1 DSM           42.2
#>  4     2 DSM           48.6
#>  5     2 TUL           34.2
#>  6     2 GSP           32.4
#>  7     3 DSM           71.0
#>  8     3 PVD           47.5
#>  9     3 CAE           46.9
#> 10     4 OKC           47.0
#> # ℹ 26 more rows

On peut à tout moment “dégrouper” un tableau à l’aide de ungroup. C’est nécessaire, dans l’exemple précédent, si on veut seulement récupérer les trois retards les plus importants pour l’ensemble des couples mois / destination.

flights %>%
  group_by(month, dest) %>%
  summarise(retard_moyen = mean(dep_delay, na.rm = TRUE)) %>%
  ungroup() %>%
  slice_max(retard_moyen, n = 3)
#> `summarise()` has grouped output by 'month'. You can override using the
#> `.groups` argument.
#> # A tibble: 3 × 3
#>   month dest  retard_moyen
#>   <int> <chr>        <dbl>
#> 1    12 BZN           75  
#> 2     7 TUL           72.6
#> 3     3 DSM           71.0

On peut aussi spécifier précisément le comportement de dégroupage de summarise en lui fournissant un argument supplémentaire .groups qui peut prendre notamment les valeurs suivantes :

  • "drop_last" : dégroupe seulement de la dernière variable de groupage
  • "drop" : dégroupe totalement le tableau résultat (équivaut à l’application d’un ungroup)
  • "keep" : conserve toutes les variables de groupage

Ce concept de dégroupage successif peut être un peu déroutant de prime abord. Il est donc utile de faire attention aux avertissements affichés par ces opérations, et il ne faut pas hésiter à ajouter un ungroup en fin de pipeline si on sait qu’on ne souhaite pas utiliser les groupes encore existants par la suite.

À noter que la fonction count, de son côté, renvoie un tableau non groupé.

flights %>%
  count(month, dest)
#> # A tibble: 1,113 × 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
#> # ℹ 1,103 more rows

10.5 Autres fonctions utiles

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

10.5.1 slice_sample

Ce verbe permet de sélectionner aléatoirement un nombre de lignes (avec l’argument n) ou une fraction des lignes (avec l’argument prop) d’un tableau.

Ainsi si on veut choisir 5 lignes au hasard dans le tableau airports :

airports %>% slice_sample(n = 5)
#> # A tibble: 5 × 8
#>   faa   name                                 lat    lon   alt    tz dst   tzone 
#>   <chr> <chr>                              <dbl>  <dbl> <dbl> <dbl> <chr> <chr> 
#> 1 BIX   Keesler Afb                         30.4  -88.9    33    -6 A     Ameri…
#> 2 MSL   Northwest Alabama Regional Airport  34.7  -87.6   550    -6 A     Ameri…
#> 3 HTL   Roscommon Co                        44.4  -84.7  1150    -5 A     Ameri…
#> 4 IWD   Gogebic Iron County Airport         46.5  -90.1  1230    -6 U     Ameri…
#> 5 FST   Fort Stockton Pecos Co              30.9 -103.   3011    -6 A     Ameri…

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

flights %>% slice_sample(prop = 0.1)
#> # A tibble: 33,677 × 22
#>     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     5    15     1219           1221        -2     1521           1537
#>  2  2013     3     8     1535           1318       137     1815           1623
#>  3  2013     4    22     1952           2000        -8     2103           2113
#>  4  2013     5    21     1021           1030        -9     1156           1230
#>  5  2013     3    31     1332           1340        -8     1436           1459
#>  6  2013     6     8      850            854        -4     1055           1105
#>  7  2013     9    14     1311           1310         1     1436           1455
#>  8  2013     5    14     1950           1951        -1     2238           2300
#>  9  2013    11    25     1455           1455         0     1634           1650
#> 10  2013     2    19     1714           1650        24     1847           1855
#> # ℹ 33,667 more rows
#> # ℹ 14 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>, duree_h <dbl>,
#> #   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.

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

lead(1:5)
#> [1]  2  3  4  5 NA
lag(1:5)
#> [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 :

flights %>%
  mutate(
      dep_delay_prev = lag(dep_delay),
      dep_delay_diff = dep_delay - dep_delay_prev
  ) %>%
  select(dep_delay_prev, dep_delay, dep_delay_diff)
#> # A tibble: 336,776 × 3
#>    dep_delay_prev dep_delay dep_delay_diff
#>             <dbl>     <dbl>          <dbl>
#>  1             NA         2             NA
#>  2              2         4              2
#>  3              4         2             -2
#>  4              2        -1             -3
#>  5             -1        -6             -5
#>  6             -6        -4              2
#>  7             -4        -5             -1
#>  8             -5        -3              2
#>  9             -3        -3              0
#> 10             -3        -2              1
#> # ℹ 336,766 more rows

10.5.3 distinct et n_distinct

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

flights %>%
  select(day, month) %>%
  distinct()
#> # A tibble: 365 × 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
#> # ℹ 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.

flights %>%
  distinct(month, day)
#> # A tibble: 365 × 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
#> # ℹ 355 more rows

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

flights %>%
  distinct(month, day, .keep_all = TRUE)
#> # A tibble: 365 × 22
#>     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
#> # ℹ 355 more rows
#> # ℹ 14 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>, duree_h <dbl>,
#> #   distance_km <dbl>, vitesse <dbl>

La fonction n_distinct, elle, renvoie le nombre de valeurs distinctes d’un vecteur. On peut notamment l’utiliser dans un summarise.

Dans l’exemple qui suit on calcule, pour les trois aéroports de départ de la table flights le nombre de valeurs distinctes de l’aéroport d’arrivée :

flights %>%
  group_by(origin) %>%
  summarise(n_dest = n_distinct(dest))
#> # A tibble: 3 × 2
#>   origin n_dest
#>   <chr>   <int>
#> 1 EWR        86
#> 2 JFK        70
#> 3 LGA        68

10.5.4 relocate

relocate peut être utilisé pour réordonner les colonnes d’une table. Par défaut, si on lui passe un ou plusieurs noms de colonnes, relocate les place en début de tableau.

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

Les arguments supplémentaires .before et .after permettent de préciser à quel endroit déplacer la ou les colonnes indiquées.

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

10.6 Tables multiples

Le jeu de données nycflights13 est un exemple de données réparties en plusieurs tables. Ici on en a trois : les informations sur les vols dans flights, celles sur les aéroports dans airports et celles sur les compagnies aériennes dans airlines.

dplyr propose différentes fonctions permettant de travailler avec des données structurées de cette manière.

10.6.1 Concaténation : bind_rows et bind_cols

Les fonctions bind_rows et bind_cols permettent d’ajouter des lignes (respectivement des colonnes) à une table à partir d’une ou plusieurs autres tables.

L’exemple suivant (certes très artificiel) montre l’utilisation de bind_rows. On commence par créer trois tableaux t1, t2 et t3 :

t1 <- airports %>%
  select(faa, name, lat, lon) %>%
  slice(1:2)
t1
#> # A tibble: 2 × 4
#>   faa   name                            lat   lon
#>   <chr> <chr>                         <dbl> <dbl>
#> 1 04G   Lansdowne Airport              41.1 -80.6
#> 2 06A   Moton Field Municipal Airport  32.5 -85.7
t2 <- airports %>%
  select(faa, name, lat, lon) %>%
  slice(5:6)

t2
#> # A tibble: 2 × 4
#>   faa   name                             lat   lon
#>   <chr> <chr>                          <dbl> <dbl>
#> 1 09J   Jekyll Island Airport           31.1 -81.4
#> 2 0A9   Elizabethton Municipal Airport  36.4 -82.2
t3 <- airports %>%
  select(faa, name) %>%
  slice(100:101)
t3
#> # A tibble: 2 × 2
#>   faa   name             
#>   <chr> <chr>            
#> 1 ADW   Andrews Afb      
#> 2 AET   Allakaket Airport

On concaténe ensuite les trois tables avec bind_rows :

bind_rows(t1, t2, t3)
#> # A tibble: 6 × 4
#>   faa   name                             lat   lon
#>   <chr> <chr>                          <dbl> <dbl>
#> 1 04G   Lansdowne Airport               41.1 -80.6
#> 2 06A   Moton Field Municipal Airport   32.5 -85.7
#> 3 09J   Jekyll Island Airport           31.1 -81.4
#> 4 0A9   Elizabethton Municipal Airport  36.4 -82.2
#> 5 ADW   Andrews Afb                     NA    NA  
#> 6 AET   Allakaket Airport               NA    NA

On remarquera que si des colonnes sont manquantes pour certaines tables, comme les colonnes lat et lon de t3, des NA sont automatiquement insérées.

Il peut être utile, quand on concatène des lignes, de garder une trace du tableau d’origine de chacune des lignes dans le tableau final. C’est possible grâce à l’argument .id de bind_rows. On passe à cet argument le nom d’une colonne qui contiendra l’indicateur d’origine des lignes :

bind_rows(t1, t2, t3, .id = "source")
#> # A tibble: 6 × 5
#>   source faa   name                             lat   lon
#>   <chr>  <chr> <chr>                          <dbl> <dbl>
#> 1 1      04G   Lansdowne Airport               41.1 -80.6
#> 2 1      06A   Moton Field Municipal Airport   32.5 -85.7
#> 3 2      09J   Jekyll Island Airport           31.1 -81.4
#> 4 2      0A9   Elizabethton Municipal Airport  36.4 -82.2
#> 5 3      ADW   Andrews Afb                     NA    NA  
#> 6 3      AET   Allakaket Airport               NA    NA

Par défaut la colonne .id ne contient qu’un nombre, différent pour chaque tableau. On peut lui spécifier des valeurs plus explicites en “nommant” les tables dans bind_rows de la manière suivante :

bind_rows(table1 = t1, table2 = t2, table3 = t3, .id = "source")
#> # A tibble: 6 × 5
#>   source faa   name                             lat   lon
#>   <chr>  <chr> <chr>                          <dbl> <dbl>
#> 1 table1 04G   Lansdowne Airport               41.1 -80.6
#> 2 table1 06A   Moton Field Municipal Airport   32.5 -85.7
#> 3 table2 09J   Jekyll Island Airport           31.1 -81.4
#> 4 table2 0A9   Elizabethton Municipal Airport  36.4 -82.2
#> 5 table3 ADW   Andrews Afb                     NA    NA  
#> 6 table3 AET   Allakaket Airport               NA    NA

bind_cols permet de concaténer des colonnes et fonctionne de manière similaire :

t1 <- flights %>% slice(1:5) %>% select(dep_delay, dep_time)
t2 <- flights %>% slice(1:5) %>% select(origin, dest)
t3 <- flights %>% slice(1:5) %>% select(arr_delay, arr_time)
bind_cols(t1, t2, t3)
#> # A tibble: 5 × 6
#>   dep_delay dep_time origin dest  arr_delay arr_time
#>       <dbl>    <int> <chr>  <chr>     <dbl>    <int>
#> 1         2      517 EWR    IAH          11      830
#> 2         4      533 LGA    IAH          20      850
#> 3         2      542 JFK    MIA          33      923
#> 4        -1      544 JFK    BQN         -18     1004
#> 5        -6      554 LGA    ATL         -25      812

À noter que bind_cols associe les lignes uniquement par position. Les lignes des différents tableaux associés doivent donc correspondre (et leur nombre doit être identique). Pour associer des tables par valeur, on doit utiliser des jointures.

10.6.2 Jointures

10.6.2.1 Clés implicites

Très souvent, les données relatives à une analyse sont réparties dans plusieurs tables différentes. Dans notre exemple, on peut voir que la table flights contient le code de la compagnie aérienne du vol dans la variable carrier :

flights %>% select(carrier)
#> # A tibble: 336,776 × 1
#>    carrier
#>    <chr>  
#>  1 UA     
#>  2 UA     
#>  3 AA     
#>  4 B6     
#>  5 DL     
#>  6 UA     
#>  7 B6     
#>  8 EV     
#>  9 B6     
#> 10 AA     
#> # ℹ 336,766 more rows

Et que par ailleurs la table airlines contient une information supplémentaire relative à ces compagnies, à savoir le nom complet.

airlines
#> # A tibble: 16 × 2
#>    carrier name                       
#>    <chr>   <chr>                      
#>  1 9E      Endeavor Air Inc.          
#>  2 AA      American Airlines Inc.     
#>  3 AS      Alaska Airlines Inc.       
#>  4 B6      JetBlue Airways            
#>  5 DL      Delta Air Lines Inc.       
#>  6 EV      ExpressJet Airlines Inc.   
#>  7 F9      Frontier Airlines Inc.     
#>  8 FL      AirTran Airways Corporation
#>  9 HA      Hawaiian Airlines Inc.     
#> 10 MQ      Envoy Air                  
#> 11 OO      SkyWest Airlines Inc.      
#> 12 UA      United Air Lines Inc.      
#> 13 US      US Airways Inc.            
#> 14 VX      Virgin America             
#> 15 WN      Southwest Airlines Co.     
#> 16 YV      Mesa Airlines Inc.

Il est donc naturel de vouloir associer les deux, ici pour ajouter les noms complets des compagnies à la table flights. Pour cela on va effectuer une jointure : les lignes d’une table seront associées à une autre en se basant non pas sur leur position, mais sur les valeurs d’une ou plusieurs colonnes. Ces colonnes sont appelées des clés.

Pour faire une jointure de ce type, on va utiliser la fonction left_join :

left_join(flights, airlines)

Pour faciliter la lecture, on va afficher seulement certaines colonnes du résultat :

left_join(flights, airlines) %>%
  select(month, day, carrier, name)
#> Joining with `by = join_by(carrier)`
#> # A tibble: 336,776 × 4
#>    month   day carrier name                    
#>    <int> <int> <chr>   <chr>                   
#>  1     1     1 UA      United Air Lines Inc.   
#>  2     1     1 UA      United Air Lines Inc.   
#>  3     1     1 AA      American Airlines Inc.  
#>  4     1     1 B6      JetBlue Airways         
#>  5     1     1 DL      Delta Air Lines Inc.    
#>  6     1     1 UA      United Air Lines Inc.   
#>  7     1     1 B6      JetBlue Airways         
#>  8     1     1 EV      ExpressJet Airlines Inc.
#>  9     1     1 B6      JetBlue Airways         
#> 10     1     1 AA      American Airlines Inc.  
#> # ℹ 336,766 more rows

On voit que la table résultat est bien la fusion des deux tables d’origine selon les valeurs des deux colonnes clés carrier. On est parti de la table flights, et pour chaque ligne de celle-ci on a ajouté les colonnes de airlines pour lesquelles la valeur de carrier est la même. On a donc bien une nouvelle colonne name dans notre table résultat, avec le nom complet de la compagnie aérienne.

Note

À noter qu’on peut tout à fait utiliser le pipe avec les fonctions de jointure :

flights %>% left_join(airlines).

Nous sommes ici dans le cas le plus simple concernant les clés de jointure : les deux clés sont uniques et portent le même nom dans les deux tables. Par défaut, si on ne lui spécifie pas explicitement les clés, dplyr fusionne en utilisant l’ensemble des colonnes communes aux deux tables. On peut d’ailleurs voir dans cet exemple qu’un message a été affiché précisant que la jointure s’est bien faite sur la variable carrier.

10.6.2.2 Clés explicites

La table airports, contient des informations supplémentaires sur les aéroports : nom complet, altitude, position géographique, etc. Chaque aéroport est identifié par un code contenu dans la colonne faa.

Si on regarde la table flights, on voit que le code d’identification des aéroports apparaît à deux endroits différents : pour l’aéroport de départ dans la colonne origin, et pour celui d’arrivée dans la colonne dest. On a donc deux clés de jointure possibles, et qui portent un nom différent de la clé de airports.

On va commencer par fusionner les données concernant l’aéroport de départ. Pour simplifier l’affichage des résultats, on va se contenter d’un sous-ensemble des deux tables :

flights_ex <- flights %>% select(month, day, origin, dest)
airports_ex <- airports %>% select(faa, alt, name)

Si on se contente d’un left_join comme à l’étape précédente, on obtient un message d’erreur car aucune colonne commune ne peut être identifiée comme clé de jointure :

flights_ex %>% left_join(airports_ex)
#> Error in `left_join()`:
#> ! `by` must be supplied when `x` and `y` have no common variables.
#> ℹ Use `cross_join()` to perform a cross-join.

On doit donc spécifier explicitement les clés avec l’argument by de left_join. Ici la clé est nommée origin dans la première table, et faa dans la seconde. La syntaxe est donc la suivante :

flights_ex %>% 
  left_join(airports_ex, by = c("origin" = "faa"))
#> # A tibble: 336,776 × 6
#>    month   day origin dest    alt name               
#>    <int> <int> <chr>  <chr> <dbl> <chr>              
#>  1     1     1 EWR    IAH      18 Newark Liberty Intl
#>  2     1     1 LGA    IAH      22 La Guardia         
#>  3     1     1 JFK    MIA      13 John F Kennedy Intl
#>  4     1     1 JFK    BQN      13 John F Kennedy Intl
#>  5     1     1 LGA    ATL      22 La Guardia         
#>  6     1     1 EWR    ORD      18 Newark Liberty Intl
#>  7     1     1 EWR    FLL      18 Newark Liberty Intl
#>  8     1     1 LGA    IAD      22 La Guardia         
#>  9     1     1 JFK    MCO      13 John F Kennedy Intl
#> 10     1     1 LGA    ORD      22 La Guardia         
#> # ℹ 336,766 more rows

On constate que les deux nouvelles colonnes name et alt contiennent bien les données correspondant à l’aéroport de départ.

On va stocker le résultat de cette jointure dans la table flights_ex :

flights_ex <- flights_ex %>%
  left_join(airports_ex, by = c("origin" = "faa"))

Supposons qu’on souhaite maintenant fusionner à nouveau les informations de la table airports, mais cette fois pour les aéroports d’arrivée de notre nouvelle table flights_ex. Les deux clés sont donc désormais dest dans la première table, et faa dans la deuxième. La syntaxe est donc la suivante :

flights_ex %>%
  left_join(airports_ex, by = c("dest" = "faa"))
#> # A tibble: 336,776 × 8
#>    month   day origin dest  alt.x name.x              alt.y name.y              
#>    <int> <int> <chr>  <chr> <dbl> <chr>               <dbl> <chr>               
#>  1     1     1 EWR    IAH      18 Newark Liberty Intl    97 George Bush Interco…
#>  2     1     1 LGA    IAH      22 La Guardia             97 George Bush Interco…
#>  3     1     1 JFK    MIA      13 John F Kennedy Intl     8 Miami Intl          
#>  4     1     1 JFK    BQN      13 John F Kennedy Intl    NA <NA>                
#>  5     1     1 LGA    ATL      22 La Guardia           1026 Hartsfield Jackson …
#>  6     1     1 EWR    ORD      18 Newark Liberty Intl   668 Chicago Ohare Intl  
#>  7     1     1 EWR    FLL      18 Newark Liberty Intl     9 Fort Lauderdale Hol…
#>  8     1     1 LGA    IAD      22 La Guardia            313 Washington Dulles I…
#>  9     1     1 JFK    MCO      13 John F Kennedy Intl    96 Orlando Intl        
#> 10     1     1 LGA    ORD      22 La Guardia            668 Chicago Ohare Intl  
#> # ℹ 336,766 more rows

Cela fonctionne, les informations de l’aéroport d’arrivée ont bien été ajoutées, mais on constate que les colonnes ont été renommées. En effet, ici les deux tables fusionnées contenaient toutes les deux des colonnes name et alt. Comme on ne peut pas avoir deux colonnes avec le même nom dans un tableau, dplyr a renommé les colonnes de la première table en name.x et alt.x, et celles de la deuxième en name.y et alt.y.

C’est pratique, mais pas forcément très parlant. On pourrait renommer manuellement les colonnes avec rename avant de faire la jointure pour avoir des intitulés plus explicites, mais on peut aussi utiliser l’argument suffix de left_join, qui permet d’indiquer les suffixes à ajouter aux colonnes.

flights_ex %>%
  left_join(
    airports_ex,
    by = c("dest" = "faa"),
    suffix = c("_depart", "_arrivee")
  )
#> # A tibble: 336,776 × 8
#>    month   day origin dest  alt_depart name_depart      alt_arrivee name_arrivee
#>    <int> <int> <chr>  <chr>      <dbl> <chr>                  <dbl> <chr>       
#>  1     1     1 EWR    IAH           18 Newark Liberty …          97 George Bush…
#>  2     1     1 LGA    IAH           22 La Guardia                97 George Bush…
#>  3     1     1 JFK    MIA           13 John F Kennedy …           8 Miami Intl  
#>  4     1     1 JFK    BQN           13 John F Kennedy …          NA <NA>        
#>  5     1     1 LGA    ATL           22 La Guardia              1026 Hartsfield …
#>  6     1     1 EWR    ORD           18 Newark Liberty …         668 Chicago Oha…
#>  7     1     1 EWR    FLL           18 Newark Liberty …           9 Fort Lauder…
#>  8     1     1 LGA    IAD           22 La Guardia               313 Washington …
#>  9     1     1 JFK    MCO           13 John F Kennedy …          96 Orlando Intl
#> 10     1     1 LGA    ORD           22 La Guardia               668 Chicago Oha…
#> # ℹ 336,766 more rows

On obtient ainsi directement des noms de colonnes nettement plus clairs.

10.6.3 Types de jointures

Jusqu’à présent nous avons utilisé la fonction left_join, mais il existe plusieurs types de jointures.

Partons de deux tables d’exemple, personnes et voitures :

personnes <- tibble(
    nom = c("Sylvie", "Sylvie", "Monique", "Gunter", "Rayan", "Rayan"),
    voiture = c("Twingo", "Ferrari", "Scenic", "Lada", "Twingo", "Clio")
)
nom voiture
Sylvie Twingo
Sylvie Ferrari
Monique Scenic
Gunter Lada
Rayan Twingo
Rayan Clio
voitures <- tibble(
    voiture = c("Twingo", "Ferrari", "Clio", "Lada", "208"),
    vitesse = c("140", "280", "160", "85", "160")
)
voiture vitesse
Twingo 140
Ferrari 280
Clio 160
Lada 85
208 160

10.6.3.1 left_join

Si on fait un left_join de voitures sur personnes :

personnes %>% left_join(voitures)
#> Joining with `by = join_by(voiture)`
nom voiture vitesse
Sylvie Twingo 140
Sylvie Ferrari 280
Monique Scenic NA
Gunter Lada 85
Rayan Twingo 140
Rayan Clio 160

On voit que chaque ligne de personnes est bien présente, et qu’on lui a ajouté une ligne de voitures correspondante si elle existe. Dans le cas du Scenic, il n’y a avait pas de ligne dans voitures, donc vitesse a été mise à NA. Dans le cas de 208, présente dans voitures mais pas dans personnes, la ligne n’apparaît pas.

Si on fait un left_join cette fois de personnes sur voitures, c’est l’inverse :

voitures %>% left_join(personnes)
#> Joining with `by = join_by(voiture)`
voiture vitesse nom
Twingo 140 Sylvie
Twingo 140 Rayan
Ferrari 280 Sylvie
Clio 160 Rayan
Lada 85 Gunter
208 160 NA

La ligne 208 est là, mais nom est à NA. Par contre Monique est absente. Et on remarquera que la ligne Twingo, présente deux fois dans personnes, a été dupliquée pour être associée aux deux lignes de données de Sylvie et Rayan.

En résumé, quand on fait un left_join(x, y), toutes les lignes de x sont présentes, et dupliquées si nécessaire quand elles apparaissent plusieurs fois dans y. Les lignes de y non présentes dans x disparaissent. Les lignes de x non présentes dans y se voient attribuer des NA pour les nouvelles colonnes.

Intuitivement, on pourrait considérer que left_join(x, y) signifie “ramener l’information de la table y sur la table x”.

En général, left_join sera le type de jointures le plus fréquemment utilisé.

10.6.3.2 right_join

La jointure right_join est l’exacte symétrique de left_join, c’est-à dire que right_join(x, y) est équivalent à left_join(y, x) :

personnes %>% right_join(voitures)
#> Joining with `by = join_by(voiture)`
nom voiture vitesse
Sylvie Twingo 140
Sylvie Ferrari 280
Gunter Lada 85
Rayan Twingo 140
Rayan Clio 160
NA 208 160

10.6.3.3 inner_join

Dans le cas de inner_join(x, y), seules les lignes présentes à la fois dans x et y sont conservées (et si nécessaire dupliquées) dans la table résultat :

personnes %>% inner_join(voitures)
#> Joining with `by = join_by(voiture)`
nom voiture vitesse
Sylvie Twingo 140
Sylvie Ferrari 280
Gunter Lada 85
Rayan Twingo 140
Rayan Clio 160

Ici la ligne 208 est absente, ainsi que la ligne Monique, qui dans le cas d’un left_join avait été conservée et s’était vue attribuer une vitesse à NA.

10.6.3.4 full_join

Dans le cas de full_join(x, y), toutes les lignes de x et toutes les lignes de y sont conservées (avec des NA ajoutés si nécessaire) même si elles sont absentes de l’autre table :

personnes %>% full_join(voitures)
#> Joining with `by = join_by(voiture)`
nom voiture vitesse
Sylvie Twingo 140
Sylvie Ferrari 280
Monique Scenic NA
Gunter Lada 85
Rayan Twingo 140
Rayan Clio 160
NA 208 160

10.6.3.5 semi_join et anti_join

semi_join et anti_join sont des jointures filtrantes, c’est-à-dire qu’elles sélectionnent les lignes de x sans ajouter les colonnes de y.

Ainsi, semi_join ne conservera que les lignes de x pour lesquelles une ligne de y existe également, et supprimera les autres. Dans notre exemple, la ligne Monique est donc supprimée :

personnes %>% semi_join(voitures)
#> Joining with `by = join_by(voiture)`
nom voiture
Sylvie Twingo
Sylvie Ferrari
Gunter Lada
Rayan Twingo
Rayan Clio

Un anti_join fait l’inverse, il ne conserve que les lignes de x absentes de y. Dans notre exemple, on ne garde donc que la ligne Monique :

personnes %>% anti_join(voitures)
#> Joining with `by = join_by(voiture)`
nom voiture
Monique Scenic

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

Enfin, 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.

10.8 Exercices

On commence par charger les extensions et les données nécessaires.

library(tidyverse)
library(nycflights13)
data(flights)
data(airports)
data(airlines)

10.8.1 Les verbes de base de dplyr

Exercice 1.1

Sélectionner la dixième ligne du tableau des aéroports (airports).

#> # A tibble: 1 × 8
#>   faa   name                    lat   lon   alt    tz dst   tzone              
#>   <chr> <chr>                 <dbl> <dbl> <dbl> <dbl> <chr> <chr>              
#> 1 0S9   Jefferson County Intl  48.1 -123.   108    -8 A     America/Los_Angeles
slice(airports, 10)

Sélectionner les 5 premières lignes de la table airlines.

#> # A tibble: 5 × 2
#>   carrier name                  
#>   <chr>   <chr>                 
#> 1 9E      Endeavor Air Inc.     
#> 2 AA      American Airlines Inc.
#> 3 AS      Alaska Airlines Inc.  
#> 4 B6      JetBlue Airways       
#> 5 DL      Delta Air Lines Inc.
slice_head(airlines, n = 5)

Sélectionner l’aéroport avec l’altitude la plus basse.

#> # A tibble: 1 × 8
#>   faa   name          lat   lon   alt    tz dst   tzone              
#>   <chr> <chr>       <dbl> <dbl> <dbl> <dbl> <chr> <chr>              
#> 1 IPL   Imperial Co  32.8 -116.   -54    -8 A     America/Los_Angeles
slice_min(airports, alt)

Exercice 1.2

Sélectionnez les vols du mois de juillet (variable month).

#> # A tibble: 29,425 × 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     7     1        1           2029       212      236           2359
#>  2  2013     7     1        2           2359         3      344            344
#>  3  2013     7     1       29           2245       104      151              1
#>  4  2013     7     1       43           2130       193      322             14
#>  5  2013     7     1       44           2150       174      300            100
#>  6  2013     7     1       46           2051       235      304           2358
#>  7  2013     7     1       48           2001       287      308           2305
#>  8  2013     7     1       58           2155       183      335             43
#>  9  2013     7     1      100           2146       194      327             30
#> 10  2013     7     1      100           2245       135      337            135
#> # ℹ 29,415 more rows
#> # ℹ 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>
filter(flights, month == 7)

Sélectionnez les vols avec un retard à l’arrivée (variable arr_delay) compris entre 5 et 15 minutes.

#> # A tibble: 36,392 × 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      554            558        -4      740            728
#>  3  2013     1     1      558            600        -2      753            745
#>  4  2013     1     1      558            600        -2      924            917
#>  5  2013     1     1      600            600         0      837            825
#>  6  2013     1     1      611            600        11      945            931
#>  7  2013     1     1      623            610        13      920            915
#>  8  2013     1     1      624            630        -6      840            830
#>  9  2013     1     1      629            630        -1      824            810
#> 10  2013     1     1      632            608        24      740            728
#> # ℹ 36,382 more rows
#> # ℹ 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>
filter(flights, arr_delay >= 5 & arr_delay <= 15)

Sélectionnez les vols des compagnies Delta, United et American (codes DL, UA et AA de la variable carrier).

#> # A tibble: 139,504 × 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      554            600        -6      812            837
#>  5  2013     1     1      554            558        -4      740            728
#>  6  2013     1     1      558            600        -2      753            745
#>  7  2013     1     1      558            600        -2      924            917
#>  8  2013     1     1      558            600        -2      923            937
#>  9  2013     1     1      559            600        -1      941            910
#> 10  2013     1     1      559            600        -1      854            902
#> # ℹ 139,494 more rows
#> # ℹ 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>
filter(flights, carrier %in% c("DL", "UA", "AA"))

Exercice 1.3

Triez la table flights par retard au départ décroissant.

#> # A tibble: 336,776 × 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
#> # ℹ 336,766 more rows
#> # ℹ 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>
arrange(flights, desc(dep_delay))

Exercice 1.4

Sélectionnez les colonnes name, lat et lon de la table airports

#> # A tibble: 1,458 × 3
#>    name                             lat    lon
#>    <chr>                          <dbl>  <dbl>
#>  1 Lansdowne Airport               41.1  -80.6
#>  2 Moton Field Municipal Airport   32.5  -85.7
#>  3 Schaumburg Regional             42.0  -88.1
#>  4 Randall Airport                 41.4  -74.4
#>  5 Jekyll Island Airport           31.1  -81.4
#>  6 Elizabethton Municipal Airport  36.4  -82.2
#>  7 Williams County Airport         41.5  -84.5
#>  8 Finger Lakes Regional Airport   42.9  -76.8
#>  9 Shoestring Aviation Airfield    39.8  -76.6
#> 10 Jefferson County Intl           48.1 -123. 
#> # ℹ 1,448 more rows
select(airports, name, lat, lon)

Sélectionnez toutes les colonnes de la table airports sauf les colonnes tz et tzone

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

Sélectionnez toutes les colonnes de la table flights dont les noms se terminent par “delay”.

#> # A tibble: 336,776 × 2
#>    dep_delay arr_delay
#>        <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
#> # ℹ 336,766 more rows
select(flights, ends_with("delay"))

Dans la table airports, renommez la colonne alt en altitude et la colonne tzone en fuseau_horaire.

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

Exercice 1.5

Dans la table airports, la colonne alt contient l’altitude de l’aéroport en pieds. Créer une nouvelle variable alt_m contenant l’altitude en mètres (on convertit des pieds en mètres en les divisant par 3.2808). Sélectionner dans la table obtenue uniquement les deux colonnes alt et alt_m.

#> # A tibble: 1,458 × 2
#>      alt  alt_m
#>    <dbl>  <dbl>
#>  1  1044 318.  
#>  2   264  80.5 
#>  3   801 244.  
#>  4   523 159.  
#>  5    11   3.35
#>  6  1593 486.  
#>  7   730 223.  
#>  8   492 150.  
#>  9  1000 305.  
#> 10   108  32.9 
#> # ℹ 1,448 more rows
tmp <- mutate(airports, alt_m = alt / 3.2808)
select(tmp, alt, alt_m)

10.8.2 Enchaîner des opérations

Exercice 2.1

Réécrire le code de l’exercice précédent en utilisant le pipe %>%.

airports %>%
  mutate(alt_m = alt / 3.2808) %>%
  select(alt, alt_m)

Exercice 2.2

En utilisant le pipe, sélectionnez les vols à destination de San Francico (code SFO de la variable dest) et triez-les selon le retard au départ décroissant (variable dep_delay).

#> # A tibble: 13,331 × 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     9    20     1139           1845      1014     1457           2210
#>  2  2013     7     7     2123           1030       653       17           1345
#>  3  2013     7     7     2059           1030       629      106           1350
#>  4  2013     7     6      149           1600       589      456           1935
#>  5  2013     7    10      133           1800       453      455           2130
#>  6  2013     7    10     2342           1630       432      312           1959
#>  7  2013     7     7     2204           1525       399      107           1823
#>  8  2013     7     7     2306           1630       396      250           1959
#>  9  2013     6    23     1833           1200       393       NA           1507
#> 10  2013     7    10     2232           1609       383      138           1928
#> # ℹ 13,321 more rows
#> # ℹ 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>
flights %>%
  filter(dest == "SFO") %>%
  arrange(desc(dep_delay))

Exercice 2.3

Sélectionnez les vols des mois de septembre et octobre, conservez les colonnes dest et dep_delay, créez une nouvelle variable retard_h contenant le retard au départ en heures, et conservez uniquement les 5 lignes avec les plus grandes valeurs de retard_h.

#> # A tibble: 5 × 3
#>   dest  dep_delay retard_h
#>   <chr>     <dbl>    <dbl>
#> 1 SFO        1014    16.9 
#> 2 ATL         702    11.7 
#> 3 DTW         696    11.6 
#> 4 ATL         602    10.0 
#> 5 MSP         593     9.88
flights %>%
  filter(month %in% c(9, 10)) %>%
  select(dest, dep_delay) %>%
  mutate(retard_h = dep_delay / 60) %>%
  slice_max(retard_h, n = 5)

10.8.3 group_by et summarise

Exercice 3.1

Affichez le nombre de vols par mois.

#> # A tibble: 12 × 2
#>    month     n
#>    <int> <int>
#>  1     1 27004
#>  2     2 24951
#>  3     3 28834
#>  4     4 28330
#>  5     5 28796
#>  6     6 28243
#>  7     7 29425
#>  8     8 29327
#>  9     9 27574
#> 10    10 28889
#> 11    11 27268
#> 12    12 28135
flights %>%
  count(month)

Triez la table résultat selon le nombre de vols croissant.

#> # A tibble: 12 × 2
#>    month     n
#>    <int> <int>
#>  1     2 24951
#>  2     1 27004
#>  3    11 27268
#>  4     9 27574
#>  5    12 28135
#>  6     6 28243
#>  7     4 28330
#>  8     5 28796
#>  9     3 28834
#> 10    10 28889
#> 11     8 29327
#> 12     7 29425
flights %>%
  count(month) %>%
  arrange(n)

Exercice 3.2

Calculer la distance moyenne des vols selon l’aéroport de départ (variable origin).

#> # A tibble: 3 × 2
#>   origin distance_moyenne
#>   <chr>             <dbl>
#> 1 EWR               1057.
#> 2 JFK               1266.
#> 3 LGA                780.
flights %>%
  group_by(origin) %>%
  summarise(distance_moyenne = mean(distance))

Exercice 3.3

Calculer le nombre de vols à destination de Los Angeles (code LAX) pour chaque mois de l’année.

#> # A tibble: 12 × 2
#>    month     n
#>    <int> <int>
#>  1     1  1159
#>  2     2  1030
#>  3     3  1178
#>  4     4  1382
#>  5     5  1453
#>  6     6  1430
#>  7     7  1500
#>  8     8  1505
#>  9     9  1384
#> 10    10  1409
#> 11    11  1336
#> 12    12  1408
flights %>%
  filter(dest == "LAX") %>%
  count(month)

Exercice 3.4

Calculer le nombre de vols selon le mois et la destination.

#> # A tibble: 1,113 × 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
#> # ℹ 1,103 more rows
flights %>%
  count(month, dest)

Ne conserver, pour chaque mois, que la destination avec le nombre maximal de vols.

#> # A tibble: 12 × 3
#> # Groups:   month [12]
#>    month dest      n
#>    <int> <chr> <int>
#>  1     1 ATL    1396
#>  2     2 ATL    1267
#>  3     3 ATL    1448
#>  4     4 ATL    1490
#>  5     5 ORD    1582
#>  6     6 ORD    1547
#>  7     7 ORD    1573
#>  8     8 ORD    1604
#>  9     9 ORD    1582
#> 10    10 ORD    1604
#> 11    11 ATL    1384
#> 12    12 ATL    1463
flights %>%
  count(month, dest) %>%
  group_by(month) %>%
  slice_max(n)

Exercice 3.5

Calculer le nombre de vols selon le mois. Ajouter une colonne comportant le pourcentage de vols annuels réalisés par mois.

#> # A tibble: 12 × 3
#>    month     n pourcentage
#>    <int> <int>       <dbl>
#>  1     1 27004        8.02
#>  2     2 24951        7.41
#>  3     3 28834        8.56
#>  4     4 28330        8.41
#>  5     5 28796        8.55
#>  6     6 28243        8.39
#>  7     7 29425        8.74
#>  8     8 29327        8.71
#>  9     9 27574        8.19
#> 10    10 28889        8.58
#> 11    11 27268        8.10
#> 12    12 28135        8.35
flights %>%
  count(month) %>%
  mutate(pourcentage = n / sum(n) * 100)

Exercice 3.6

Calculer, pour chaque aéroport de départ et de destination, la durée moyenne des vols (variable air_time). Pour chaque aéroport de départ, ne conserver que la destination avec la durée moyenne la plus longue.

#> `summarise()` has grouped output by 'origin'. You can override using the
#> `.groups` argument.
#> # A tibble: 3 × 3
#> # Groups:   origin [3]
#>   origin dest  duree_moyenne
#>   <chr>  <chr>         <dbl>
#> 1 EWR    HNL            612.
#> 2 JFK    HNL            623.
#> 3 LGA    DEN            228.
flights %>%
  group_by(origin, dest) %>%
  summarise(duree_moyenne = mean(air_time, na.rm = TRUE)) %>%
  # Le group_by suivant n'est pas obligatoire
  group_by(origin) %>%
  slice_max(duree_moyenne)

10.8.4 Jointures

Exercice 4.1

Faire la jointure de la table airlines sur la table flights à l’aide de left_join.

#> # A tibble: 336,776 × 20
#>     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
#> # ℹ 336,766 more rows
#> # ℹ 12 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>, name <chr>
flights %>%
  left_join(airlines)

Exercice 4.2

À partir de la table résultat de l’exercice précédent, calculer le retard moyen au départ pour chaque compagnie, et trier selon ce retard décroissant.

#> # A tibble: 16 × 2
#>    name                        retard_moyen
#>    <chr>                              <dbl>
#>  1 Frontier Airlines Inc.             20.2 
#>  2 ExpressJet Airlines Inc.           20.0 
#>  3 Mesa Airlines Inc.                 19.0 
#>  4 AirTran Airways Corporation        18.7 
#>  5 Southwest Airlines Co.             17.7 
#>  6 Endeavor Air Inc.                  16.7 
#>  7 JetBlue Airways                    13.0 
#>  8 Virgin America                     12.9 
#>  9 SkyWest Airlines Inc.              12.6 
#> 10 United Air Lines Inc.              12.1 
#> 11 Envoy Air                          10.6 
#> 12 Delta Air Lines Inc.                9.26
#> 13 American Airlines Inc.              8.59
#> 14 Alaska Airlines Inc.                5.80
#> 15 Hawaiian Airlines Inc.              4.90
#> 16 US Airways Inc.                     3.78
flights %>%
  left_join(airlines) %>%
  group_by(name) %>%
  summarise(retard_moyen = mean(dep_delay, na.rm = TRUE)) %>%
  arrange(desc(retard_moyen))

Exercice 4.3

Faire la jointure de la table airports sur la table flights en utilisant comme clé le code de l’aéroport de destination.

#> # A tibble: 336,776 × 26
#>     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
#> # ℹ 336,766 more rows
#> # ℹ 18 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>, name <chr>, lat <dbl>,
#> #   lon <dbl>, alt <dbl>, tz <dbl>, dst <chr>, tzone <chr>
flights %>%
  left_join(airports, by = c("dest" = "faa"))

À partir de cette table, afficher pour chaque mois le nom de l’aéroport de destination ayant eu le plus petit nombre de vol.

#> # A tibble: 14 × 3
#> # Groups:   month [12]
#>    month name                      n
#>    <int> <chr>                 <int>
#>  1     1 Key West Intl             1
#>  2     2 Jackson Hole Airport      3
#>  3     3 Bangor Intl               2
#>  4     4 Key West Intl             1
#>  5     4 Myrtle Beach Intl         1
#>  6     5 Columbia Metropolitan     9
#>  7     6 Myrtle Beach Intl         1
#>  8     7 La Guardia                1
#>  9     8 South Bend Rgnl           1
#> 10     9 South Bend Rgnl           5
#> 11    10 Albany Intl               1
#> 12    10 South Bend Rgnl           1
#> 13    11 Blue Grass                1
#> 14    12 South Bend Rgnl           1
flights %>%
  left_join(airports, by = c("dest" = "faa")) %>%
  count(month, name) %>%
  group_by(month) %>%
  slice_min(n)

Exercice 4.4

Créer une table indiquant, pour chaque vol, uniquement le nom de l’aéroport de départ et celui de l’aéroport d’arrivée.

#> # A tibble: 336,776 × 2
#>    orig_name           dest_name                      
#>    <chr>               <chr>                          
#>  1 Newark Liberty Intl George Bush Intercontinental   
#>  2 La Guardia          George Bush Intercontinental   
#>  3 John F Kennedy Intl Miami Intl                     
#>  4 John F Kennedy Intl <NA>                           
#>  5 La Guardia          Hartsfield Jackson Atlanta Intl
#>  6 Newark Liberty Intl Chicago Ohare Intl             
#>  7 Newark Liberty Intl Fort Lauderdale Hollywood Intl 
#>  8 La Guardia          Washington Dulles Intl         
#>  9 John F Kennedy Intl Orlando Intl                   
#> 10 La Guardia          Chicago Ohare Intl             
#> # ℹ 336,766 more rows
flights %>%
  left_join(airports, by = c("dest" = "faa")) %>%
  rename(dest_name = name) %>%
  left_join(airports, by = c("origin" = "faa")) %>%
  rename(orig_name = name) %>%
  select(orig_name, dest_name)

10.8.5 Bonus

Exercice 5.1

Calculer le nombre de vols selon l’aéroport de destination, et fusionnez la table airports sur le résultat avec left_join. Stocker le résultat final dans un objet nommé flights_dest.

flights_dest <- flights %>%
  count(dest) %>%
  left_join(airports, by = c("dest"="faa"))

Créez une carte interactive des résultats avec le package leaflet et le code suivant :

library(leaflet)
leaflet(data = flights_dest) %>%
  addTiles %>%
  addCircles(lng = ~lon, lat = ~lat, radius = ~n * 10, popup = ~name)
#> Warning in validateCoords(lng, lat, funcName): Data contains 4 rows with either
#> missing or invalid lat/lon values and will be ignored

  1. À 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.↩︎

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

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