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