%>% (pipe)select()filter()unite()separate()Nous chargeons les bases de données dans les variables jardin1 et jardin2 :
library(tidyverse)
# Chargement des données : attention, nous utilisons la fonction read_csv avec
# un underscore (tiret du bas) et non pas un point !
jardin1 <- read_csv('data/jardin1.csv')
jardin2 <- read_csv('data/jardin2.csv')
Les variables d’une base de données sont souvent inadaptées aux analyses qu’on souhaite pratiquer. Par exemple, nous aimerions connaître l’âge des membres des jardins dans la base jardin2.
age_tap <- table(jardin2$age)
age_tap_tri <- sort(age_tap, decreasing = TRUE)
age_tap_tri
##
## 50 - 59 60 - 69 40 - 49 30 - 39 20 - 29 '+ de 70 + de 70 Oct-19
## 52 39 33 30 8 6 5 1
Ce tri à plat ne nous aide pas vraiment à décrire la variable :
'+ de 70 et + de 70 sont en fait la même modalité, différentes en pratique à cause d’une erreur de frappe)Oct-19 ?)C’est pourquoi il serait plus utile de créer une nouvelle variable nettoyée et recodée à partir de la variable age.
Le nettoyage consiste à produire une base de données où l’information est présentée de manière claire, lisible et univoque, tout en perdant le moins d’information possible par rapport aux données originelles. Cela implique souvent :
jardin1, on a à la fois des informations sur la personne qui remplit le questionnaire et un jardin)L’étape du recodage se fait en fonction de la finalité du traitement statistique qu’on veut effectuer. En général, le recodage consiste à ajuster la variance d’une variable, c’est-à-dire sa diversité d’informations. Par exemple, on peut vouloir réduire l’information contenue dans une variable pour faciliter son agrégation (retirer ou combiner les modalités peu fréquentes d’une variable), ou au contraire augmenter cette diversité d’information en combinant différentes variables.
Le recodage implique souvent :
Nota Bene : en pratique, recodage et nettoyage sont souvent liés et combinés dans les mêmes commandes.
%>% (pipe)Nettoyage et recodage demandent souvent de combiner beaucoup de différentes commandes R pour arriver à ses fins. Il est tout à fait possible d’y parvenir en utilisant les fonctionnalités R de base, mais cela peut parfois s’avérer difficile.
Depuis quelques années, un opérateur a gagné en popularité pour faciliter l’enchaînement de commandes R : il s’agit du pipe, “tuyau” en anglais, noté %>%.
Pour cela, nous avons besoin du paquet tidyverse :
library(tidyverse)
Le symbole pipe (%>%) peut sembler compliqué, mais en fait il simplifie grandement l’écriture du code R en permettant d’écrire de haut en bas, comme en français.
Prenons par exemple la commande suivante :
head(sort(table(jardin2$age), decreasing = TRUE), 5)
##
## 50 - 59 60 - 69 40 - 49 30 - 39 20 - 29
## 52 39 33 30 8
Ce type de commandes peut être compliqué à lire et encore plus à écrire, puisqu’il faut partir des parenthèses à l’intérieur pour aller progressivement vers l’extérieur.
Nous avions vu qu’une manière de simplifier ces expressions est de créer des variables intermédiaires :
tableau <- table(jardin2$age) # Je fais un tri à plat de la variable age
tableau_tri_decroissant <- sort(tableau, decreasing = TRUE) # Je trie le tableau par ordre décroissant
tableau_top5 <- head(tableau_tri_decroissant, 5) # Je prends les 5 premiers éléments du tableau trié
tableau_top5 # J'affiche le top 5 des âges
##
## 50 - 59 60 - 69 40 - 49 30 - 39 20 - 29
## 52 39 33 30 8
Même si elle est plus lisible, cette démarche conserve certaines limites : ces variables intermédiaires viennent “polluer” la mémoire (onglet “Environnement” dans RStudio) en ajoutant un très grand nombre de variables. D’autre part, cette façon d’écrire est assez verbeuse et prend du temps, car il faut sans cesse créer et trouver de nouveaux noms de variables.
Le but du pipe %>% est de faciliter la lecture et l’écriture de ces commandes qui s’enchaînent.
jardin2 %>%
select(age) %>%
table() %>%
sort(decreasing = TRUE) %>%
head(5)
## .
## 50 - 59 60 - 69 40 - 49 30 - 39 20 - 29
## 52 39 33 30 8
Nous pouvons lire le symbole %>% comme voulant dire “PUIS”.
jardin2 %>% # Je prends la base de données jardin 2, PUIS
select(age) %>% # Je sélectionne la variable age, PUIS
table() %>% # J'en fait un tri à plat, PUIS
sort(decreasing = TRUE) %>% # Je le trie par ordre décroissant, PUIS
head(5) # Je prends les 5 premiers éléments
## .
## 50 - 59 60 - 69 40 - 49 30 - 39 20 - 29
## 52 39 33 30 8
Enfin, on peut stocker le résultat d’une commande enchaînée dans une variable en utilisant la syntaxe habituelle :
tableau_top5 <- jardin2 %>%
select(age) %>%
table() %>%
sort(decreasing = TRUE) %>%
head(5)
En plus de penser au %>% comme voulant dire “PUIS”, la métaphore du tuyau est également pertinente. Ce symbole nous permet de connecter plusieurs commandes entre elles, permettant à l’information de s’écouler de haut en bas : %>% est le tuyau qui connecte ces différentes commandes.
Exercice : écrire la commande suivante à l’aide de l’opérateur pipe.
ages <- c(18, 19, 20, 20, 20, 21)
round(mean(ages), 2)
## [1] 19.67
Reprenons notre exemple de l’âge. Créons une nouvelle variable, nommée age_r, dans la base de données jardin2.
NB : il est important d’effectuer les recodages en créant une nouvelle variable, avec un nom différent, pour ne pas perdre la variable de départ. On pourrait sinon faire une erreur et effacer les données de départ.
case_when()Évidemment, nous ne voulons pas juste dupliquer la variable age, mais en créer une nouvelle à partir de l’ancienne. Nous devons donc transformer l’ancienne variable à l’aide d’une fonction. Ici, nous allons utiliser case_when().
La fonction case_when() a une syntaxe un peu étrange au premier abord, mais elle est très puissante et permet d’effectuer presque tous les recodages et nettoyages imaginables. Il existe bien sûr de nombreuses autres fonctions, mais nous allons nous concentrer sur case_when() en raison de sa versatilité.
La syntaxe de case_when() est la suivante :
case_when(cas1 ~ valeur))
Du côté gauche de la tilde ~, nous écrivons le cas que nous voulons recoder. Du côté droit, nous écrivons sa nouvelle valeur.
Chaque cas va s’écrire sous la forme d’une condition, qui peut être vraie ou faux. Par exemple, écrivons une fonction qui écrit “un” si un nombre est égal à 1.
sequence <- c(2, 3, 1, 4, 1, 5)
case_when(sequence == 1 ~ 'vrai')
## [1] NA NA "vrai" NA "vrai" NA
Ça marche ! Mais comme case_when() ne savait pas quoi faire des nombres qui ne sont pas égaux à 1, il en a fait des valeurs NA (No Answer en anglais). Écrivons une commande qui écrit “un” quand un nombre est égal à 1, et qui écrit “autre” pour tout autre nombre.
sequence <- c(2, 3, 1, 4, 1, 5)
case_when(sequence == 1 ~ 'vrai',
TRUE ~ 'autre')
## [1] "autre" "autre" "vrai" "autre" "vrai" "autre"
Ça marche ! Nous pouvons donc enchaîner les instructions en les séparant par des virgules. La deuxième instruction comporte le cas TRUE (qui est tout le temps vrai par définition) et assigne la valeur “autre” dans ce cas-là.
Ajoutons une troisième instruction qui remplace les nombres plus grands que 4 par “quatre et plus” :
sequence <- c(2, 3, 1, 4, 1, 5)
case_when(sequence == 1 ~ 'vrai',
sequence >= 4 ~ 'quatre et plus',
TRUE ~ 'autre')
## [1] "autre" "autre" "vrai" "quatre et plus"
## [5] "vrai" "quatre et plus"
Attention : case_when() s’arrête au premier cas qui s’avère vrai. Qu’arrive-t-il avec le code suivant ? Pourquoi ?
sequence <- c(2, 3, 1, 4, 1, 5)
case_when(TRUE ~ 'autre',
sequence == 1 ~ 'vrai',
sequence >= 4 ~ 'quatre et plus')
## [1] "autre" "autre" "autre" "autre" "autre" "autre"
Pour récapituler, on peut lire les commandes case_when() de la manière suivante :
sequence <- c(2, 3, 1, 4, 1, 5)
case_when(sequence == 1 ~ 'vrai', # Si un élément est égal à 1, alors j'écris "vrai" ; sinon...
sequence >= 4 ~ 'quatre et plus', # Si un élément est supérieur ou égal à 4, alors j'écris "quatre et plus" ; sinon
TRUE ~ 'autre') # J'écris "autre" dans tous les autres cas
## [1] "autre" "autre" "vrai" "quatre et plus"
## [5] "vrai" "quatre et plus"
case_whenMettons en pratique notre connaissance de la fonction case_when() pour recoder et nettoyer les modalités de la variable age en créant une nouvelle variable age_r :
jardin2 <- jardin2 %>%
mutate(age_r = case_when(TRUE ~ age))
table(jardin2$age_r)
##
## '+ de 70 + de 70 20 - 29 30 - 39 40 - 49 50 - 59 60 - 69 Oct-19
## 6 5 8 30 33 52 39 1
Solution :
jardin2 <- jardin2 %>%
mutate(age_r = case_when(age == "'+ de 70" ~ "+ de 70",
age == "20 - 29" ~ "- de 39",
age == "30 - 39" ~ "- de 39",
age == "Oct-19" ~ NA_character_, # NA_character_ est équivalent à NA mais évite une erreur
TRUE ~ age))
table(jardin2$age_r)
##
## - de 39 + de 70 40 - 49 50 - 59 60 - 69
## 38 11 33 52 39
C’est déjà mieux ! Mais il nous reste à ordonner les modalités de cette variable dans un ordre qui a du sens.
On peut ordonner les modalités de la variable qualitative avec la fonction fct_relevel() :
jardin2 <- jardin2 %>%
mutate(age_r = fct_relevel(age_r, '- de 39', '40 - 49', '50 - 59', '60 - 69', '+ de 70'))
table(jardin2$age_r)
##
## - de 39 40 - 49 50 - 59 60 - 69 + de 70
## 38 33 52 39 11
Voici notre recodage et nettoyage en une seule commande :
jardin2 <- jardin2 %>%
mutate(age_r = case_when(age == "'+ de 70" ~ "+ de 70",
age == "20 - 29" ~ "- de 39",
age == "30 - 39" ~ "- de 39",
age == "Oct-19" ~ NA_character_,
TRUE ~ age)) %>%
mutate(age_r = fct_relevel(age_r, '- de 39', '40 - 49', '50 - 59', '60 - 69', '+ de 70'))
table(jardin2$age_r)
On peut vérifier qu’un recodage a correctement fonctionné en croisant les deux variables :
table(jardin2$age, jardin2$age_r, useNA = "always")
##
## - de 39 40 - 49 50 - 59 60 - 69 + de 70 <NA>
## '+ de 70 0 0 0 0 6 0
## + de 70 0 0 0 0 5 0
## 20 - 29 8 0 0 0 0 0
## 30 - 39 30 0 0 0 0 0
## 40 - 49 0 33 0 0 0 0
## 50 - 59 0 0 52 0 0 0
## 60 - 69 0 0 0 39 0 0
## Oct-19 0 0 0 0 0 1
## <NA> 0 0 0 0 0 0
Considérons la variable quantitative suivante, qui décrit l’âge des personnes ayant répondu à l’enquête Histoire De Vie 2003 :
# Chargement du paquet questionr
library(questionr)
# Chargement de la base hdv2003
data(hdv2003)
glimpse(hdv2003$age)
## int [1:2000] 28 23 59 34 71 35 60 47 20 28 ...
Regroupons les âges en classes d’âge qui seront plus pratiques (recodage d’une variable quantitative en qualitative) :
hdv2003 %>%
mutate(age_c = case_when(age <= 25 ~ '25 ou moins',
age <= 35 ~ '26-35',
age <= 45 ~ '36-45',
age <= 55 ~ '46-55',
age <= 65 ~ '56-65',
TRUE ~ '66 ou plus')) %>%
mutate(age_c = factor(age_c,
levels = c('25 ou moins', '26-35',
'36-45', '46-55', '56-65',
'66 ou plus'))) %>%
select(age_c) %>%
table()
## .
## 25 ou moins 26-35 36-45 46-55 56-65 66 ou plus
## 191 338 390 414 305 362
Pour une variable qualitative, comme la commune du jardin, on peut utiliser case_when() :
jardin1 %>%
mutate(commune_r = case_when(commune == "Paris 12" ~ "Paris 12ème",
commune == "Paris 18" ~ "Paris 18ème",
commune == "Paris 18e" ~ "Paris 18ème",
TRUE ~ commune)) %>%
select(commune_r) %>%
table()
Ou bien la fonction fct_collapse(), qui peut être parfois plus lisible :
jardin1 %>%
mutate(commune_r = fct_collapse(commune,
"Paris 12ème" = c("Paris 12"),
"Paris 18ème" = c("Paris 18", "Paris 18e", "Paris 18ème"))) %>%
select(commune_r) %>%
table()
Pour les variables quantitatives, on peut utiliser case_when() comme exposé dans la présentation de cette fonction.
select()On peut créer une base de données qui contienne uniquement les variables (recodées) qui nous intéressent. Par exemple, imaginons que je veuille seulement conserver l’âge recodé, le genre et la situation professionnelle des individus de la base jardin2.
nouvelle_base <- jardin2 %>%
select(age_r, genre, situation)
glimpse(nouvelle_base)
## Observations: 174
## Variables: 3
## $ age_r <fct> 50 - 59, 50 - 59, 50 - 59, 60 - 69, 60 - 69, 60 - 69, …
## $ genre <chr> "Une femme", "Une femme", "Une femme", "Une femme", "U…
## $ situation <chr> "A votre compte", NA, "Salarié", "Salarié", NA, "Salar…
Attention : lorsqu’on retire des variables, il faut absolument créer une base de données à part pour cela pour éviter de perdre des données.
filter()Par exemple, enlevons les personnes dont nous connaissons la situation professionnelle :
nouvelle_base %>%
filter(is.na(situation))
On pourrait aussi faire l’opération contraire avec ! devant notre filtre, pour garder seulement les personnes dont nous connaissons la situation professionnelle :
nouvelle_base %>%
filter(!is.na(situation)) # Attention au point d'exclamation qui change tout !
Attention : lorsqu’on retire des individus, il faut absolument créer une base de données à part pour cela, et bien indiquer que cette opération a changé l’échantillon.
unite()Créons une variable combinant le genre des individus avec leur âge :
nouvelle_base %>%
unite(age_genre, age_r, genre, sep = " ")
On peut aussi imaginer utiliser case_when() pour créer des catégories plus personnalisées.
separate()Certaines colonnes combinent plusieurs informations qui appartiennent en réalité à plusieurs variables (cas des questions à choix multiples). C’est le cas de la variable du type de jardin, où les modalités sont séparées par des virgules :
jardin1$jardin_type
## [1] "Partagé"
## [2] "Partagé"
## [3] "Partagé, Familial"
## [4] "Partagé"
## [5] "Ouvrier"
## [6] "Partagé"
## [7] "Partagé"
## [8] "Partagé"
## [9] "Partagé"
## [10] "Partagé"
## [11] "Pédagogique"
## [12] "Partagé, Familial, Lieu de vie, Havre de paix"
## [13] "Partagé"
## [14] "Partagé"
## [15] "Partagé"
## [16] "Partagé"
## [17] "Partagé, Familial"
## [18] "Commun, Collectif"
## [19] "Partagé"
## [20] "Partagé"
## [21] "Partagé"
## [22] "Partagé, Familial"
## [23] "Partagé"
## [24] "Partagé"
## [25] "Partagé"
## [26] "Partagé"
## [27] "Familial"
## [28] "Partagé"
## [29] "Partagé, Familial"
## [30] "Partagé"
## [31] "Partagé"
## [32] "Partagé"
## [33] "Partagé"
## [34] "Laboratoire urbain"
## [35] "Partagé, Familial"
## [36] "Partagé"
## [37] "Partagé"
## [38] "Partagé, d'insertion sociale"
## [39] "Partagé"
## [40] "Partagé"
## [41] "Partagé"
## [42] "Partagé, Familial"
## [43] "Partagé"
On peut utiliser la fonction separate() pour régler ce problème et créer trois colonnes, jardin_type1, jardin_type2 et jardin_type3.
jardin1 %>%
select(jardin_type) %>%
separate(jardin_type,
c('jardin_type1', 'jardin_type2', 'jardin_type3'),
extra = "drop")
## Warning: Expected 3 pieces. Missing pieces filled with `NA` in 41 rows [1,
## 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 15, 16, 17, 18, 19, 20, 21, ...].
Comment créer une nouvelle variable ?
Nous allons utiliser la fonction
mutate()pour créer une nouvelle variable :La syntaxe de
mutate()est la suivante :