logo

Initiation à R avec le tidyverse – Exercices corrigés

Initiation à R avec les packages du {tidyverse} – Exercices corrigés

Les données

Commençons par récupérer les données.

load(url("https://github.com/olivierDecourt/livreR/blob/master/exemples.Rdata?raw=true"))

Normalement en exécutant le code ci-dessus vous devez avoir 4 objets dans votre environnement : calendar, flats, houses et other.

Partie 1 : statistiques descriptives

Commencez par récupérer le nombre de lignes de l’objet flats. Pour le moment on n’utilise pas de fonction du {tidyverse}.

nrow(flats)
## [1] 51494

Bien. Activons maintenant les packages {dplyr} et {tibble}. En votre âme et conscience, savez-vous bien à quoi servent ces deux packages ?

library(dplyr)  # requêtes
library(tibble) # visualisation des données

Vérifions rapidement le type des colonnes de flats.

glimpse(flats)
## Observations: 51,494
## Variables: 21
## $ id                     <int> 4867396, 7704653, 2725029, 9337509, 12928158...
## $ listing_url            <chr> "https://www.airbnb.com/rooms/4867396", "htt...
## $ last_scraped           <chr> "2016-07-03", "2016-07-04", "2016-07-04", "2...
## $ host_id                <int> 9703910, 35777602, 13945253, 5107123, 511956...
## $ host_name              <chr> "Matthieu", "Claire", "Vincent", "Julie", "D...
## $ host_since             <chr> "2013-10-29", "2015-06-14", "2014-04-06", "2...
## $ host_location          <chr> "Nantes, Pays de la Loire, France", "Paris, ...
## $ neighbourhood          <chr> "Batignolles", "Champs-Elysées", "Batignolle...
## $ neighbourhood_cleansed <chr> "Batignolles-Monceau", "Batignolles-Monceau"...
## $ zipcode                <chr> "75017", "75017", "75017", "75017", "75017",...
## $ property_type          <chr> "Apartment", "Apartment", "Apartment", "Apar...
## $ room_type              <chr> "Entire home/apt", "Entire home/apt", "Entir...
## $ accommodates           <int> 2, 4, 2, 2, 2, 3, 4, 2, 2, 2, 4, 4, 4, 4, 4,...
## $ bathrooms              <dbl> 1.0, 2.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.5,...
## $ bedrooms               <int> 1, 2, 1, 1, 1, 2, 2, 1, 1, 1, 0, 1, 1, 1, 1,...
## $ beds                   <int> 1, 3, 1, 1, 1, 2, 2, 1, 1, 1, 1, 2, 2, 2, 2,...
## $ price                  <dbl> 60, 200, 80, 60, 50, 100, 290, 99, 401, 80, ...
## $ number_of_reviews      <int> 1, 0, 1, 1, 2, 4, 14, 2, 0, 0, 1, 1, 0, 2, 9...
## $ review_scores_rating   <int> 100, NA, 80, 80, 100, 100, 97, NA, NA, NA, 1...
## $ cancellation_policy    <chr> "flexible", "flexible", "flexible", "flexibl...
## $ amenities              <chr> "TV|Cable TV|Internet|Wireless Internet|Kitc...

Avec les fonctions de {tidyverse}, sortons les statistiques suivantes.

flats %>% 
  group_by(room_type) %>% 
  summarise(nb_logements=n(),
            prix_moyen=mean(price),
            prix_median=median(price),
            note_moyenne=mean(review_scores_rating, na.rm=TRUE))
## # A tibble: 3 x 5
##   room_type       nb_logements prix_moyen prix_median note_moyenne
##   <chr>                  <int>      <dbl>       <dbl>        <dbl>
## 1 Entire home/apt        44558      102.           80         91.0
## 2 Private room            6441       54.3          49         91.3
## 3 Shared room              495       39.6          35         90.4

Sortons également des moyennes de toutes les variables numériques (même celles pour lesquelles c’est stupide).

flats %>% 
  summarise_if(is.numeric, mean, na.rm=TRUE)
##        id  host_id accommodates bathrooms bedrooms     beds    price
## 1 7067023 22378964     3.039791  1.083865 1.047329 1.671159 95.47648
##   number_of_reviews review_scores_rating
## 1          12.59052             91.00672

Un comptage des appartements selon le type de location et le quartier de Paris.

table(flats$neighbourhood_cleansed, flats$room_type)
##                      
##                       Entire home/apt Private room Shared room
##   Batignolles-Monceau            3087          412          25
##   Bourse                         1429          140           9
##   Buttes-Chaumont                2087          531          40
##   Buttes-Montmartre              5145          735          46
##   Élysée                         1328          100          10
##   Entrepôt                       2826          535          38
##   Gobelins                       1415          406          32
##   Hôtel-de-Ville                 1661          125           9
##   Louvre                         1015           73           5
##   Luxembourg                     1684          124           7
##   Ménilmontant                   2190          493          30
##   Observatoire                   1723          305          25
##   Opéra                          2012          249          12
##   Palais-Bourbon                 1442          119          16
##   Panthéon                       1777          146          25
##   Passy                          2663          310          25
##   Popincourt                     4136          599          72
##   Reuilly                        1822          331          22
##   Temple                         1896          160           6
##   Vaugirard                      3220          548          41

Et le même en pourcentages-lignes.

round(
  prop.table(
    table(flats$neighbourhood_cleansed, flats$room_type),
    1)*100,
  2)
##                      
##                       Entire home/apt Private room Shared room
##   Batignolles-Monceau           87.60        11.69        0.71
##   Bourse                        90.56         8.87        0.57
##   Buttes-Chaumont               78.52        19.98        1.50
##   Buttes-Montmartre             86.82        12.40        0.78
##   Élysée                        92.35         6.95        0.70
##   Entrepôt                      83.14        15.74        1.12
##   Gobelins                      76.36        21.91        1.73
##   Hôtel-de-Ville                92.53         6.96        0.50
##   Louvre                        92.86         6.68        0.46
##   Luxembourg                    92.78         6.83        0.39
##   Ménilmontant                  80.72        18.17        1.11
##   Observatoire                  83.93        14.86        1.22
##   Opéra                         88.52        10.95        0.53
##   Palais-Bourbon                91.44         7.55        1.01
##   Panthéon                      91.22         7.49        1.28
##   Passy                         88.83        10.34        0.83
##   Popincourt                    86.04        12.46        1.50
##   Reuilly                       83.77        15.22        1.01
##   Temple                        91.95         7.76        0.29
##   Vaugirard                     84.54        14.39        1.08

Attention, un peu plus compliqué… Les nombres d’appartements par tranche de prix, avec un pourcentage et un pourcentage cumulé !

flats %>% 
  group_by(tranche_prix = cut(price, 
                              breaks=c(-Inf,50,75,100,125,150,200,Inf))) %>% 
  count() %>% 
  ungroup() %>% 
  mutate(pct = round(n/sum(n)*100,2),
         pct_cumule = cumsum(pct))
## # A tibble: 7 x 4
##   tranche_prix     n   pct pct_cumule
##   <fct>        <int> <dbl>      <dbl>
## 1 (-Inf,50]    11915 23.1        23.1
## 2 (50,75]      14719 28.6        51.7
## 3 (75,100]     11602 22.5        74.2
## 4 (100,125]     3928  7.63       81.9
## 5 (125,150]     3589  6.97       88.8
## 6 (150,200]     2794  5.43       94.3
## 7 (200, Inf]    2947  5.72      100

Pour finir, on s’intéresse au nombre de loueurs différents de la base flats.

flats %>% 
  summarise(nb_loueurs = n_distinct(host_id))
##   nb_loueurs
## 1      43976

Et à son corrolaire logique, les 10 loueurs proposant le plus d’appartements.

flats %>% 
  group_by(host_id, host_name) %>% 
  count() %>% 
  ungroup() %>% 
  arrange(desc(n)) %>% 
  slice(1:10)
## # A tibble: 10 x 3
##     host_id host_name             n
##       <int> <chr>             <int>
##  1  2288803 Fabien              153
##  2  2667370 Parisian Home       139
##  3 12984381 Olivier              90
##  4  3972699 Hanane               80
##  5  3943828 Caroline             65
##  6 21630783 Pierre               65
##  7 39922748 Clara                64
##  8   789620 Charlotte            60
##  9 11593703 Rudy And Benjamin    56
## 10  3971743 Diane                55

Partie 2 : manipulation de données

Voici les types des variables de calendar.

glimpse(calendar)
## Observations: 9,461,700
## Variables: 3
## $ listing_id <int> 6159911, 6159911, 6159911, 6159911, 6159911, 6159911, 61...
## $ date       <chr> "2016-10-01", "2016-09-30", "2016-09-29", "2016-09-28", ...
## $ price      <dbl> 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, ...

A partir de la colonne date, créons date2 (de type Date), mois et annee.

library(lubridate) # manipulation de dates

calendar <- calendar %>% 
              mutate(date2 = as.Date(date),
                     mois  = month(date2),
                     mois_lettres = month(date2, label = TRUE, abbr = FALSE),
                     annee = year(date2))
glimpse(calendar)
## Observations: 9,461,700
## Variables: 7
## $ listing_id   <int> 6159911, 6159911, 6159911, 6159911, 6159911, 6159911, ...
## $ date         <chr> "2016-10-01", "2016-09-30", "2016-09-29", "2016-09-28"...
## $ price        <dbl> 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50...
## $ date2        <date> 2016-10-01, 2016-09-30, 2016-09-29, 2016-09-28, 2016-...
## $ mois         <dbl> 10, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,...
## $ mois_lettres <ord> octobre, septembre, septembre, septembre, septembre, s...
## $ annee        <dbl> 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, ...

On pourra alors utiliser ces nouvelles informations pour sortir les statistiques suivantes.

calendar %>% 
  group_by(annee, mois_lettres) %>% 
  summarise(prix_moyen = mean(price))
## # A tibble: 13 x 3
## # Groups:   annee [2]
##    annee mois_lettres prix_moyen
##    <dbl> <ord>             <dbl>
##  1  2016 juillet            123.
##  2  2016 août               117.
##  3  2016 septembre          116.
##  4  2016 octobre            121.
##  5  2016 novembre           119.
##  6  2016 décembre           119.
##  7  2017 janvier            117.
##  8  2017 février            117.
##  9  2017 mars               116.
## 10  2017 avril              117.
## 11  2017 mai                117.
## 12  2017 juin               116.
## 13  2017 juillet            115.

A partir des données flats, on voudrait créer deux nouvelles variables : internet et baignoire Elles correspondent respectivement à la présence dans la colonne amenities des mots-clés “internet” et “tub” (la casse n’est pas garantie). On vérifiera les statistiques d’appartements selon la présence ou l’absence de ces deux équipements.

library(stringr) # manipulation de textes

flats <- flats %>% 
  mutate(internet = str_detect(tolower(amenities),"internet"),
         baignoire = str_detect(tolower(amenities),"tub"))

flats %>% 
  group_by(internet, baignoire) %>% 
  count()
## # A tibble: 4 x 3
## # Groups:   internet, baignoire [4]
##   internet baignoire     n
##   <lgl>    <lgl>     <int>
## 1 FALSE    FALSE      2476
## 2 FALSE    TRUE          9
## 3 TRUE     FALSE     48321
## 4 TRUE     TRUE        688

Partie 3 : filtres

Vérifions que les appartements n’ayant pas de note moyenne n’ont reçu aucune appréciation.

flats %>% 
  filter(is.na(review_scores_rating)) %>% 
  select(number_of_reviews) %>% 
  summary()
##  number_of_reviews 
##  Min.   : 0.00000  
##  1st Qu.: 0.00000  
##  Median : 0.00000  
##  Mean   : 0.07931  
##  3rd Qu.: 0.00000  
##  Max.   :21.00000

Ah, ce n’est pas le cas ! Combien d’appartements ont au moins une appréciation mais pas de note moyenne ?

flats %>% 
  filter(is.na(review_scores_rating) & number_of_reviews >= 1) %>% 
  count()
## # A tibble: 1 x 1
##       n
##   <int>
## 1   935

Combien d’appartements ont un code postal qui est bien de la forme “750xx” avec xx un nombre entre 01 et 20 ?

flats %>% 
  filter(grepl("^750\\d{2}$", zipcode)
         & between(as.integer(substr(zipcode,4,5)),1,16)) %>% 
  count()
## Warning in between(as.integer(substr(zipcode, 4, 5)), 1, 16): NAs introduits
## lors de la conversion automatique
## # A tibble: 1 x 1
##       n
##   <int>
## 1 34488

Et dernière question toute simple : combien coûte en moyenne la nuit dans une maison pouvant accueillir au moins 6 personnes ?

houses %>% 
  filter(accommodates >= 6) %>% 
  summarise(prix_moyen_par_nuit = mean(price))
##   prix_moyen_par_nuit
## 1            351.3234

Partie 4 : jointures

Les identifiants de logement de calendar reprennent ceux qu’on trouve dans flats et houses. Seuls les logements disponibles à chaque date sont visibles dans calendar.

Parmi les appartements situés dans le quartier Bourse, combien sont disponibles pour la nuit du 15 août 2016 ? Quel pourcentage des appartements de ce quartier cela représente-t-il ?

calendar %>% 
  rename(prix_dispo = price) %>% 
  filter(date == "2016-08-15") %>% 
  right_join(flats %>% filter(neighbourhood_cleansed == "Bourse"),
             by=c("listing_id"="id")) %>% 
  summarise(nb_dispos = sum(!is.na(prix_dispo)),
            pct_dispos = mean(!is.na(prix_dispo))*100)
##   nb_dispos pct_dispos
## 1       849   53.80228

Combien de maisons ne sont pas disponibles le 31 décembre 2016 ?

houses %>% 
  anti_join(calendar %>% filter(date == "2016-12-31"),
            by=c("id"="listing_id")) %>% 
  nrow()
## [1] 334

Partie 5 : transposition

Nous allons surtout utiliser la transposition pour construire des tableaux croisés comme celui-ci, qui compte les appartements par quartier et type de location.

library(reshape2) # transpositions
flats %>% 
  group_by(neighbourhood_cleansed, room_type) %>% 
  count() %>% 
  dcast(neighbourhood_cleansed ~ room_type, fun.aggregate = sum)
##    neighbourhood_cleansed Entire home/apt Private room Shared room
## 1     Batignolles-Monceau            3087          412          25
## 2                  Bourse            1429          140           9
## 3         Buttes-Chaumont            2087          531          40
## 4       Buttes-Montmartre            5145          735          46
## 5                  Élysée            1328          100          10
## 6                Entrepôt            2826          535          38
## 7                Gobelins            1415          406          32
## 8          Hôtel-de-Ville            1661          125           9
## 9                  Louvre            1015           73           5
## 10             Luxembourg            1684          124           7
## 11           Ménilmontant            2190          493          30
## 12           Observatoire            1723          305          25
## 13                  Opéra            2012          249          12
## 14         Palais-Bourbon            1442          119          16
## 15               Panthéon            1777          146          25
## 16                  Passy            2663          310          25
## 17             Popincourt            4136          599          72
## 18                Reuilly            1822          331          22
## 19                 Temple            1896          160           6
## 20              Vaugirard            3220          548          41

Et nous terminerons par un tableau qui donne le prix moyen des appartements disponibles par mois selon le quartier.

library(lubridate)
calendar %>% 
  rename(prix_dispo = price) %>% 
  mutate(date2 = as.Date(date),
         mois  = month(date2),
         annee = year(date2)) %>% 
  right_join(flats,
             by=c("listing_id"="id")) %>% 
  dcast(neighbourhood_cleansed ~ annee + mois, fun.aggregate = mean, value.var="prix_dispo")
##    neighbourhood_cleansed    2016_7    2016_8    2016_9   2016_10   2016_11
## 1     Batignolles-Monceau 103.80293 101.36281 101.32420 106.04169 105.73107
## 2                  Bourse 131.24558 127.88840 132.69309 133.77050 130.81020
## 3         Buttes-Chaumont  68.55394  67.71375  66.46748  68.30896  68.44749
## 4       Buttes-Montmartre  81.38411  80.14531  80.56390  82.67545  82.64245
## 5                  Élysée 198.71429 190.10218 201.81291 203.60118 193.32613
## 6                Entrepôt  87.27470  86.85174  86.34913  88.65665  88.34212
## 7                Gobelins  92.14474 109.00940 114.81793  81.31708  79.66076
## 8          Hôtel-de-Ville 149.90187 145.77817 150.54911 153.35106 145.99065
## 9                  Louvre 147.45193 148.82210 157.93796 157.42321 155.71913
## 10             Luxembourg 156.80456 155.19483 160.70710 163.83336 161.52409
## 11           Ménilmontant 360.49222 264.47070 207.68205 259.28464 240.64275
## 12           Observatoire  88.53795  87.15131  88.20415  89.87562  88.46880
## 13                  Opéra 107.12876 104.98407 103.34522 107.65752 107.40605
## 14         Palais-Bourbon 160.50507 157.22974 162.59008 162.87301 162.22840
## 15               Panthéon 127.15660 125.35204 126.69686 130.68566 129.06279
## 16                  Passy 140.10765 135.81193 134.29068 138.57464 139.53468
## 17             Popincourt  85.48273  84.63856  84.30043  86.02607  85.62038
## 18                Reuilly  82.64887  82.62880  81.47959  83.80443  84.78133
## 19                 Temple 128.90304 126.35062 131.21688 134.47450 130.63206
## 20              Vaugirard  97.81199  98.28330  97.85704 100.36406 100.01738
##      2016_12    2017_1    2017_2    2017_3    2017_4    2017_5    2017_6
## 1  106.32000 103.70512 103.31961 103.15172 103.09352 103.40660 103.33403
## 2  133.52313 131.17633 130.71404 130.63110 131.82321 131.14095 130.92296
## 3   68.95229  68.22438  68.13420  67.97442  68.20620  68.23355  68.19499
## 4   82.76844  81.46362  81.22094  80.73636  80.82704  80.64747  80.60518
## 5  199.04955 189.05685 186.62809 188.29523 190.17085 193.66989 191.08498
## 6   89.61558  87.18223  87.60847  87.27754  87.54413  87.49324  87.35212
## 7   79.58858  79.32480  79.26508  78.97456  79.02867  78.98521  78.93212
## 8  149.27538 147.70511 147.42289 147.12588 147.83375 148.50127 147.47678
## 9  158.27421 157.25088 154.06358 154.95474 153.72713 154.21100 153.98550
## 10 164.03805 161.36627 161.78535 160.98847 161.57736 162.24018 161.84458
## 11 234.35091 227.56085 223.22656 221.58376 219.47189 216.73325 215.88281
## 12  89.20865  87.03032  87.71750  87.58403  87.53722  88.11149  86.00226
## 13 108.50944 106.23993 105.22694 105.36660 106.36516 106.43430 106.61110
## 14 163.49051 161.07830 160.76434 161.20774 161.86080 160.37891 156.51049
## 15 129.97988 127.31588 127.78044 129.15382 129.80399 129.60546 129.23521
## 16 141.49746 140.32403 140.61786 140.70022 141.50021 141.16113 139.92576
## 17  86.09009  84.37507  84.83688  84.60133  84.81283  84.68964  84.90648
## 18  84.10979  82.67919  83.31312  83.50547  84.23244  84.03252  84.18603
## 19 132.84073 131.02930 130.15176 129.54918 130.60389 130.19603 130.34180
## 20 100.97825  99.88663 100.11364  99.93930 100.18707  99.65177  99.53029
##       2017_7 NA_NA
## 1  104.95290    NA
## 2  132.75692    NA
## 3   68.15958    NA
## 4   80.49203    NA
## 5  190.24435    NA
## 6   87.84934    NA
## 7   78.87893    NA
## 8  148.93190    NA
## 9  152.74413    NA
## 10 161.89537    NA
## 11 188.67118    NA
## 12  86.46056    NA
## 13 105.99739    NA
## 14 155.22419    NA
## 15 129.07701    NA
## 16 140.48601    NA
## 17  85.08968    NA
## 18  85.20907    NA
## 19 130.44487    NA
## 20 100.10213    NA