rbind
cbind
ou data.frame
merge
dplyr
rbindlist
et merge
du package data.table
Note préliminaire : Lors de leur dernière mise à jour, ces notes ont été révisées en utilisant R version 4.0.3, le package dplyr
version 1.0.3, le package tidyr
version 1.1.2, le package data.table
version 1.13.6, le package stringr
version 1.4.0 et le package lubridate
version 1.7.9.2. Pour d’autres versions, les informations peuvent différer.
Maintenant que nous savons comment créer un objet contenant les données que nous voulons analyser, il nous reste un volet à couvrir dans la manipulation de données en R : le prétraitement des données. Ce prétraitement est défini ici comme étant toutes les étapes nécessaires pour rendre des données prêtes à être fournies en entrée à une certaine fonction, par exemple une fonction pour produire un graphique ou ajuster un modèle. Un jeu de données brut doit, dans la grande majorité des cas, être adapté afin d’être analysable. Préparer des données peut impliquer :
Voici comment réaliser certains de ces prétraitements en R. La façon de procéder avec les packages chargés par défaut lors de l’ouverture d’une session R (principalement les packages base
et stats
) est présentée en détail. Des façons de réaliser certaines tâches avec des packages du tidyverse
(principalement les packages dplyr
et tidyr
), ainsi qu’avec le package data.table
, sont également mentionnées, sans être approfondies. Chargeons donc ces packages.
library(dplyr)
library(tidyr)
library(data.table)
De façon générale, les packages du tidyverse
présentent l’avantage d’être cohérents entre eux et abondamment documentés sur le web (en partie en raison de leur popularité). Pour sa part, la grande force du package data.table
est sa capacité à effectuer rapidement des traitements, même sur des jeux de données de grande taille. Les packages du tidyverse
tendent aussi à être un peu plus rapide que le R de base, mais pas aussi rapide que le package data.table
. Malgré des différences en ce qui concerne les temps d’exécution, les trois options présentées ici (R de base versus tidyverse
versus data.table
) produisent des résultats équivalents. Ainsi, dans un cas de manipulation de données dont la taille n’est pas trop grande, le choix entre ces outils est uniquement une question de goût.
Ces notes se terminent en présentant trois différentes avenues pour comparer des objets R. Lors de la réalisation de prétraitement de données, il est fréquemment de vouloir comparer des structures de données, dans leur entièreté ou en partie, afin de s’assurer d’avoir correctement effectué les manipulations requises.
Un grand nombre de transformations de variables usuelles peuvent être facilement réalisées en R, par exemple les transformations suivantes :
Les transformations de variables peuvent mener à des modifications des variables dans un jeu de données ou en la création de nouvelles variables à partir de celles existantes.
Supposons ici que nous travaillons avec un jeu de données stocké dans un data frame R. Les colonnes du data frame représentent des variables. Pour illustrer les premières transformations présentées, nous utiliserons le jeu de données cars
du package dataset
.
str(cars)
## 'data.frame': 50 obs. of 2 variables:
## $ speed: num 4 4 7 7 8 9 10 10 10 11 ...
## $ dist : num 2 10 4 22 16 10 18 26 34 17 ...
Il s’agit d’un petit jeu de données à 50 observations prises sur des voitures et 2 variables :
speed
: la vitesse de déplacement des voitures tout juste avant de freiner, mesurée en miles par heure;dist
: la distance parcourue par ces voitures entre le moment d’un freinage et l’atteinte d’une immobilisation complète, mesurée en pieds.transform
Pour remplacer une variable dans un data frame par une transformation de celle-ci, il suffit d’assigner le nouveau vecteur ou facteur contenant les observations de la variable transformée à la colonne contenant la variable à modifier.
Par exemple, dans le jeu de données cars
, la distance est exprimée en pieds. Supposons que nous voulons transformer l’échelle de mesure de cette variable pour des mètres. Cette transformation est effectuée par une simple opération mathématique : la multiplication par un facteur de conversion. Ce facteur de conversion est ici 0.3048, car un pied est l’équivalent de 0.3048 mètre.
$dist * 0.3048 cars
Pour modifier la variable sans en créer une nouvelle, il suffit d’assigner le vecteur contenant les données pour la variable transformée à la colonne qui contenait la variable d’origine.
$dist <- cars$dist * 0.3048 cars
Nous savons que l’instruction cars$dist
, servant à identifier la colonne à modifier, aurait pu être écrite de bien d’autres façons, notamment :
cars[["dist"]]
cars[, "dist"]
cars[, 2]
Je considère cependant que l’opérateur $
procure la syntaxe la plus simple pour un remplacement de variable dans un data frame. Le jeu de données cars
a été modifié comme suit.
str(cars)
## 'data.frame': 50 obs. of 2 variables:
## $ speed: num 4 4 7 7 8 9 10 10 10 11 ...
## $ dist : num 0.61 3.05 1.22 6.71 4.88 ...
La fonction transform
offre une autre façon d’écrire ce genre d’assignation. La commande suivante produit le même résultat que la dernière assignation.
<- transform(cars, dist = dist * 0.3048) cars
Il faut donner comme premier argument à la fonction transform
le data frame dans lequel nous souhaitons effectuer une transformation de variable. Les arguments suivants sont des assignations de nouvelles valeurs. Dans ces assignations, il est possible de référer aux colonnes du data frame fourni comme premier argument par leur nom sans les précéder de nom_du_data_frame$
et sans les encadrer de guillemets, comme avec la fonction subset
, vue dans le cours sur les structures de données en R.
cars
dans le chemin de rechercheNous avons maintenant deux objets nommés cars
dans notre chemin de recherche, parce que nous venons de modifier un data frame provenant d’un package chargé. Le data frame modifié, est situé dans notre environnement de travail. Alors que le data frame cars
original est toujours dans l’environnement du package datasets
. Celui-là n’a pas été modifié.
Le chemin de recherche de notre session R, pour une commande soumise dans la console, est le suivant.
search()
## [1] ".GlobalEnv" "package:data.table" "package:tidyr" "package:dplyr"
## [5] "package:stats" "package:graphics" "package:grDevices" "package:utils"
## [9] "package:datasets" "package:methods" "Autoloads" "package:base"
Lorsque nous demandons à R d’accéder à l’objet nommé cars
, il cherche ce nom dans les environnements de son chemin de recherche, un environnement à la fois, en respectant l’ordre des environnements dans la liste précédente. L’environnement ".GlobalEnv"
, soit notre environnement de travail, est le premier de la liste. Lorsque R trouve le nom, il cesse sa recherche. Ainsi, toute commande dans la console contenant le nom cars
, accédera maintenant à l’objet nommé cars
de notre environnement de travail.
str(cars)
## 'data.frame': 50 obs. of 2 variables:
## $ speed: num 4 4 7 7 8 9 10 10 10 11 ...
## $ dist : num 0.186 0.929 0.372 2.044 1.486 ...
Il est possible de forcer R à aller chercher l’objet cars
dans le package datasets
avec l’opérateur ::
.
str(datasets::cars)
## 'data.frame': 50 obs. of 2 variables:
## $ speed: num 4 4 7 7 8 9 10 10 10 11 ...
## $ dist : num 2 10 4 22 16 10 18 26 34 17 ...
Nous reviendrons sur le fonctionnement des évaluations en R dans un autre cours.
Au lieu d’écraser une colonne d’un data frame en la remplaçant par un autre objet, comme nous venons de le faire, il sera souvent préférable d’ajouter les nouvelles variables créées tout en conservant les anciennes. Dans ce cas, il suffit de concaténer les nouvelles variables au data frame avec la fonction cbind
ou data.frame
(vues plus loin), ou encore d’assigner les nouvelles variables à des colonnes portant des noms qui ne sont pas initialement présents dans le data frame. Par exemple, pour ajouter une variable contenant la distance exprimée en mètres au data frame cars
, nous pourrions procéder comme suit.
<- datasets::cars # pour repartir avec la version originale du jeu de données
cars $dist_m <- cars$dist * 0.3048
carshead(cars)
## speed dist dist_m
## 1 4 2 0.6096
## 2 4 10 3.0480
## 3 7 4 1.2192
## 4 7 22 6.7056
## 5 8 16 4.8768
## 6 9 10 3.0480
Étant donné que le jeu de données cars
ne contenait pas à l’origine de colonne nommée dist_m
, une nouvelle colonne portant ce nom a été créée et ajoutée après les colonnes déjà présentes dans le data frame.
La fonction transform
permet aussi d’ajouter de nouvelles variables par une assignation à un nouveau nom de colonne.
Remarque : Il y a souvent plusieurs façons de réaliser une même tâche en R. Personnellement, je tends à adopter la façon qui présente le meilleur compromis entre un code le plus facile à comprendre possible et un code le plus court possible. Ici, l’assignation à un nouveau nom de colonne avec l’opérateur $
me semble la meilleure option, mais à vous de choisir la méthode que vous préférez.
mutate
du package dplyr
Pour les adeptes du tidyverse
, le package dplyr
propose, entre autres, la fonction mutate
pour modifier ou créer de nouvelles variables. L’opération précédente aurait pu être effectuée comme suit avec cette fonction, dont l’utilisation ressemble à celle de la fonction transform
.
<- mutate(cars, dist_m = dist * 0.3048) cars
:=
du package data.table
Si le data frame contenant les données à manipuler est d’abord transformé en data table, déjà mentionné dans les notes sur les structures de données, l’opérateur d’assignation :=
offert par le package data.table
peut ensuite être utilisé pour ajouter, modifier ou même effacer des variables dans les données. Cet opérateur s’utilise en conjonction avec l’opérateur, [
comme dans l’exemple suivant.
<- as.data.table(datasets::cars)
cars_dt := dist * 0.3048]
cars_dt[, dist_m str(cars_dt)
## Classes 'data.table' and 'data.frame': 50 obs. of 3 variables:
## $ speed : num 4 4 7 7 8 9 10 10 10 11 ...
## $ dist : num 2 10 4 22 16 10 18 26 34 17 ...
## $ dist_m: num 0.61 3.05 1.22 6.71 4.88 ...
## - attr(*, ".internal.selfref")=<externalptr>
Avec :=
, aucune copie de l’objet n’est effectuée pour ensuite être réassignée. L’objet est modifié par référence (donc « sur place »), ce qui n’est pas le cas pour toutes les autres façons de faire présentées précédemment. La modification d’objets par référence est une des techniques utilisées par le package data.table
pour optimiser ses temps de traitement.
ifelse
La fonction ifelse
est souvent utile pour transformer des variables. Cette fonction prend comme premier argument un vecteur de valeurs logiques. Cet argument est nommé test
. Elle retourne en sortie un vecteur de même longueur que test
. Ce vecteur est formé des valeurs dans le deuxième argument (yes
) aux positions pour lesquelles test
comporte une valeur TRUE
, et des valeurs dans le troisième argument (no
) aux autres positions. Voici un petit exemple pour comprendre le fonctionnement de cette fonction.
ifelse(
test = c(TRUE, FALSE, FALSE, TRUE),
yes = c(1, 2, 3, 4),
no = c(-1, -2, -3 , -4)
)
## [1] 1 -2 -3 4
Par exemple, supposons que nous souhaitons créer une nouvelle variable dans le jeu de données cars
contenant la vitesse en km/heure plutôt qu’en miles/heure, mais dans laquelle les valeurs de distance en miles à l’heure inférieures à 10 sont ramenées à des valeurs manquantes. Nous pourrions créer cette variable comme suit (1.60934 est le facteur de conversion entre un mile à l’heure et un kilomètre à l’heure).
$speed_km <- ifelse(test = cars$speed < 10, yes = NA, no = cars$speed * 1.60934)
carshead(cars, n = 10)
## speed dist dist_m speed_km
## 1 4 2 0.6096 NA
## 2 4 10 3.0480 NA
## 3 7 4 1.2192 NA
## 4 7 22 6.7056 NA
## 5 8 16 4.8768 NA
## 6 9 10 3.0480 NA
## 7 10 18 5.4864 16.09340
## 8 10 26 7.9248 16.09340
## 9 10 34 10.3632 16.09340
## 10 11 17 5.1816 17.70274
Dans cet exemple, la valeur fournie au deuxième argument (yes
) n’est pas un vecteur de même longueur que les valeurs fournies aux arguments test
et no
. R applique donc la règle de recyclage pour transformer NA
, qui est en fait un vecteur de longueur 1, en un vecteur contenant length(cars$speed)=
50 valeurs NA
.
Il est même possible d’imbriquer plusieurs appels à la fonction ifelse
, comme dans cet exemple.
$speed_cat <- ifelse(
carstest = cars$speed < 10,
yes = "moins de 10",
no = ifelse(
test = cars$speed >= 20,
yes = "20 ou plus",
no = "entre 10 et 20"
)
)str(cars)
## 'data.frame': 50 obs. of 5 variables:
## $ speed : num 4 4 7 7 8 9 10 10 10 11 ...
## $ dist : num 2 10 4 22 16 10 18 26 34 17 ...
## $ dist_m : num 0.61 3.05 1.22 6.71 4.88 ...
## $ speed_km : num NA NA NA NA NA ...
## $ speed_cat: chr "moins de 10" "moins de 10" "moins de 10" "moins de 10" ...
Voici un extrait du jeu de données pour mieux comprendre de quoi a l’air la nouvelle variable créée.
c(5:7,38:40), c("speed", "speed_cat")] cars[
## speed speed_cat
## 5 8 moins de 10
## 6 9 moins de 10
## 7 10 entre 10 et 20
## 38 19 entre 10 et 20
## 39 20 20 ou plus
## 40 20 20 ou plus
Nous avons catégorisé la variable speed
en 3 catégories. Il existe cependant de meilleures façons de faire ce genre de tâche, présentées dans la sous-section suivante.
Notons, avant de clore cette sous-section, que le package dplyr
offre une fonction de remplacement à ifelse
, nommée if_else
, tout comme le package data.table
avec la fonction fifelse
. Ces fonctions ont le potentiel d’être plus rapides que ifelse
.
Parfois, nous avons besoin de rendre catégorique une variable numérique. Les agences de statistiques officielles, par exemple Statistique Canada, font souvent de telles catégorisations avant de rendre des données publiques afin qu’il ne soit plus possible d’identifier les individus auxquels réfèrent les données. Il s’agit d’une technique courante d’anonymisation de données qui contribue à permettre l’accès à des données tout en respectant des règlements concernant la confidentialité des données.
cut
La principale fonction pour catégoriser une variable numérique en R est la fonction cut
. En voici un exemple d’utilisation.
$speed_cat_2 <- cut(cars$speed, breaks = c(-Inf, 10, 20, Inf), right = FALSE)
carsstr(cars)
## 'data.frame': 50 obs. of 6 variables:
## $ speed : num 4 4 7 7 8 9 10 10 10 11 ...
## $ dist : num 2 10 4 22 16 10 18 26 34 17 ...
## $ dist_m : num 0.61 3.05 1.22 6.71 4.88 ...
## $ speed_km : num NA NA NA NA NA ...
## $ speed_cat : chr "moins de 10" "moins de 10" "moins de 10" "moins de 10" ...
## $ speed_cat_2: Factor w/ 3 levels "[-Inf,10)","[10,20)",..: 1 1 1 1 1 1 2 2 2 2 ...
L’argument right
sert à préciser si les limites de droite des intervalles de catégorisation sont incluses dans ceux-ci ou non. Ici, la valeur FALSE
fournie à cet argument a pour conséquence que les intervalles sont ouverts à droite : \([-\infty, 10), [10, 20), [20, \infty)\).
La nouvelle variable obtenue est un facteur, avec des libellés de niveaux différents de ceux de la variable speed_cat
.
c(5:7,38:40), c("speed", "speed_cat", "speed_cat_2")] cars[
## speed speed_cat speed_cat_2
## 5 8 moins de 10 [-Inf,10)
## 6 9 moins de 10 [-Inf,10)
## 7 10 entre 10 et 20 [10,20)
## 38 19 entre 10 et 20 [10,20)
## 39 20 20 ou plus [20, Inf)
## 40 20 20 ou plus [20, Inf)
Nous pouvons contrôler ces libellés avec l’argument labels
de la fonction cut
, comme dans cet exemple.
$speed_cat_3 <- cut(
cars$speed,
carsbreaks = c(-Inf, 10, 20, Inf),
labels = c("faible", "moyenne", "grande"),
right = FALSE
)c(5:7,38:40), c("speed", "speed_cat", "speed_cat_2", "speed_cat_3")] cars[
## speed speed_cat speed_cat_2 speed_cat_3
## 5 8 moins de 10 [-Inf,10) faible
## 6 9 moins de 10 [-Inf,10) faible
## 7 10 entre 10 et 20 [10,20) moyenne
## 38 19 entre 10 et 20 [10,20) moyenne
## 39 20 20 ou plus [20, Inf) grande
## 40 20 20 ou plus [20, Inf) grande
case_when
du package dplyr
L’équivalent de la fonction cut
dans l’univers du tidyverse
est la fonction case_when
du package dplyr
. À titre d’exemple, le dernier appel à cut
est reproduit comme suit avec case_when
.
$speed_cat_4 <- case_when(
cars$speed < 10 ~ "faible",
cars$speed >= 10 & cars$speed < 20 ~ "moyenne",
cars$speed >= 20 ~ "grande"
cars
)str(cars)
## 'data.frame': 50 obs. of 8 variables:
## $ speed : num 4 4 7 7 8 9 10 10 10 11 ...
## $ dist : num 2 10 4 22 16 10 18 26 34 17 ...
## $ dist_m : num 0.61 3.05 1.22 6.71 4.88 ...
## $ speed_km : num NA NA NA NA NA ...
## $ speed_cat : chr "moins de 10" "moins de 10" "moins de 10" "moins de 10" ...
## $ speed_cat_2: Factor w/ 3 levels "[-Inf,10)","[10,20)",..: 1 1 1 1 1 1 2 2 2 2 ...
## $ speed_cat_3: Factor w/ 3 levels "faible","moyenne",..: 1 1 1 1 1 1 2 2 2 2 ...
## $ speed_cat_4: chr "faible" "faible" "faible" "faible" ...
Contrairement à la fonction cut
, case_when
retourne un vecteur plutôt qu’un facteur.
ave
Si nous souhaitions remplacer les identifiants des nouvelles catégories par une statistique calculée sur les valeurs observées dans chaque catégorie, nous utiliserions la fonction ave
, comme dans cet exemple.
$speed_ave <- ave(cars$speed, cars$speed_cat, FUN = mean) cars
c(5:7,38:40),c("speed", "speed_cat", "speed_cat_2", "speed_ave")] cars[
## speed speed_cat speed_cat_2 speed_ave
## 5 8 moins de 10 [-Inf,10) 6.50000
## 6 9 moins de 10 [-Inf,10) 6.50000
## 7 10 entre 10 et 20 [10,20) 14.53125
## 38 19 entre 10 et 20 [10,20) 14.53125
## 39 20 20 ou plus [20, Inf) 22.16667
## 40 20 20 ou plus [20, Inf) 22.16667
Pour une observation, la valeur de la nouvelle variable speed_ave
est la moyenne de toutes les vitesses observées dans la même catégorie que celle de l’observation en question.
ave
et les fonctions tapply
, by
et aggregate
:La fonction ave
calcule une statistique selon des combinaisons de niveaux de facteurs, tout comme les fonctions tapply
, by
et aggregate
. Cependant, elle retourne un objet de même dimension que le premier argument qu’elle reçoit en entrée plutôt que de retourner une valeur par combinaison distincte de niveaux de facteurs. Par exemple, voici l’appel à la fonction aggregate
correspondant à l’appel à la fonction ave
précédent.
aggregate(x = cars$speed, by = list(catego = cars$speed_cat), FUN = mean)
## catego x
## 1 20 ou plus 22.16667
## 2 entre 10 et 20 14.53125
## 3 moins de 10 6.50000
Ici, le résultat produit possède seulement 3 lignes, pour les trois catégories distinctes. La sortie de l’appel à la fonction ave
précédent était plutôt de longueur 50, soit la longueur de cars$speed
.
La normalisation permet de ramener les mesures de différentes variables sur une même échelle.
scale
La fonction scale
permet de normaliser par colonne les valeurs numériques dans une matrice ou un data frame.
Par exemple, revenons au jeu de données cars
d’origine.
<- datasets::cars
cars str(cars)
## 'data.frame': 50 obs. of 2 variables:
## $ speed: num 4 4 7 7 8 9 10 10 10 11 ...
## $ dist : num 2 10 4 22 16 10 18 26 34 17 ...
Les valeurs pour les deux variables dans le jeu de données cars
ne sont pas sur la même échelle. En effet, leurs moyennes et écarts-types empiriques ne sont pas similaires. Utilisons la fonction apply
, vue dans les notes sur les calculs par sections d’un objet R, pour calculer ces statistiques par variable (= par colonne).
# moyennes par variable
apply(cars, MARGIN = 2, FUN = mean)
## speed dist
## 15.40 42.98
# écarts-types par variable
apply(cars, MARGIN = 2, FUN = sd)
## speed dist
## 5.287644 25.769377
Une normalisation couramment employée consiste à soustraire de valeurs leur moyenne, puis à diviser ces différences par l’écart-type des valeurs d’origine. Cette normalisation est parfois appelée standardisation. Les valeurs sont centrées et réduites. En notation statistique, la formule pour obtenir cette normalisation est la suivante :
\(z_i = \frac{x_i - \bar{x}}{\sigma_x}\)
où \(\bar{x}\) est la moyenne des observations \(x_i\) et \(\sigma_x\) leur écart-type. Standardisons les valeurs du jeu de donnes cars
avec la fonction scale
.
<- scale(cars) cars_standard
Les valeurs dans chacune des colonnes de cars_standard
ont une moyenne de 0 et un écart-type de 1. Elles sont donc sur la même échelle.
# moyennes par variable
round(apply(cars_standard, MARGIN = 2, FUN = mean), digits = 5)
## speed dist
## 0 0
# écarts-types par variable
apply(cars_standard, MARGIN = 2, FUN = sd)
## speed dist
## 1 1
Les arguments center
et scale
de la fonction scale
permettent de contrôler, respectivement, les valeurs soustraites par colonnes et les valeurs par lesquelles les colonnes sont divisées. Il est possible, par exemple, de centrer sans réduire avec center = TRUE
et scale = FALSE
.
Un autre exemple de normalisation possible avec scale
est de ramener les mesures de toutes les variables entre 0 et 1. En notation statistique, la formule pour obtenir cette normalisation est la suivante :
\(y_i = \frac{x_i - \min(x)}{\max(x) - \min(x)}.\)
Effectuons cette normalisation sur le jeu de données cars
.
<- apply(cars, MARGIN = 2, FUN = min) # obtention des minimums par variable
minimums <- apply(cars, MARGIN = 2, FUN = max) # obtention des maximums par variable
maximums <- scale(cars, center = minimums, scale = maximums - minimums)
cars_01norm summary(cars_01norm)
## speed dist
## Min. :0.0000 Min. :0.0000
## 1st Qu.:0.3810 1st Qu.:0.2034
## Median :0.5238 Median :0.2881
## Mean :0.5429 Mean :0.3473
## 3rd Qu.:0.7143 3rd Qu.:0.4576
## Max. :1.0000 Max. :1.0000
Note : La fonction sweep
permet aussi de faire simultanément une transformation pour chacune des colonnes d’une matrice. Elle généralise en fait scale
en permettant d’effectuer la transformation sur n’importe laquelle des dimensions d’un array. Cette fonction ne sera cependant pas approfondie ici.
D’un langage de programmation à l’autre, les chaînes de caractères ne sont pas manipulées de la même façon. Par exemple, si un objet R contient une chaîne de caractères telle la suivante,
<- "Bonjour" chaine
il n’est pas possible d’extraire disons la deuxième lettre de la chaîne avec la commande suivante.
2] chaine[
## [1] NA
Cette commande aurait fonctionné en langage Python. Elle ne fonctionne pas en R, parce que l’objet chaine
est un vecteur de longueur 1. L’opérateur d’indiçage [
réfère à des éléments d’un objet. L’objet chaine
ne contient qu’un seul élément, qui est une chaîne de caractères.
str(chaine)
## chr "Bonjour"
length(chaine)
## [1] 1
1] chaine[
## [1] "Bonjour"
Cette section présente quelques fonctions R de base pour manipuler des vecteurs dont les éléments sont des chaînes de caractères. Toutes ces fonctions travaillent de façon vectorielle. Elles effectuent d’un coup une opération sur tous les éléments d’un vecteur.
Ces fonctions seront illustrées en utilisant un data frame nommé boxoffice
créé le 25 janvier 2019 en important le contenu de la page web http://pro.boxoffice.com/numbers/all_time. Cette page web n’existe plus aujourd’hui, car le site web Boxoffice Pro a subi une refonte. Utilisons tout de même ces données, même si elles ne sont plus à jour, puisque leur format est parfait pour illustrer la manipulation de chaînes de caractères et de dates en R.
Une copie des données se trouve dans le haut de la page web des notes que vous êtes en train de lire. Le fichier contenant les données est en format .rds
, il se nomme boxoffice_20190125.rds
. Il se trouve dans le fichier compressé pretraitement_donnees_r_2021.zip
. Après avoir téléchargé ce fichier sur votre ordinateur, et après l’avoir décompressé pour en extraire boxoffice_20190125.rds
, vous pouvez charger les données en R avec le code suivant (en spécifiant le bon chemin d’accès) :
<- readRDS("C:/coursR/boxoffice_20190125.rds") boxoffice
Le data frame boxoffice
utilisé ici ressemble à celui créé dans les notes sur la lecture et l’écriture dans des fichiers externes à partir de R, mais il contient des données moins récentes et présentées sous un format un peu différent.
head(boxoffice, n = 3)
## X1 X2 X3 X4
## 1 1 Star Wars: The Force Awakens (Disney) Dec 18, 2015 $936,662,225
## 2 2 Avatar (Fox) Dec 18, 2009 $760,507,625
## 3 3 Black Panther (Disney) Feb 16, 2018 $700,059,566
Les noms des colonnes du data frame ne sont pas informatifs. Renommons les colonnes du data frame en reprenant les noms de colonnes qui apparaissaient dans la page web d’où ses données proviennent (en remplaçant les espaces par des tirets bas et en omettant les parenthèses).
colnames(boxoffice) <- c("Ranking", "Movie_Distributor", "Release_Date", "Gross")
Il s’agit de données concernant les recettes (Gross
) des films ayant généré le plus de revenus dans les cinémas américains.
paste
La fonction paste
sert à concaténer des éléments de vecteurs. Elle retourne toujours un résultat sous le format caractère. Elle n’est pas illustrée ici, car elle a été présentée dans les notes sur les structures de données en R.
Rappelons que la fonction paste0
est en quelque sorte une version de la fonction paste
avec l’argument sep = ""
(aucun caractère comme séparation entre les chaînes de caractères à concaténer).
nchar
La fonction nchar
permet de compter le nombre de caractères dans des chaînes de caractères. Elle a aussi été illustrée dans les notes sur les structures de données en R.
toupper
, tolower
et casefold
Les fonctions toupper
et tolower
transforment tous les caractères alphabétiques en majuscules et en minuscules respectivement.
<- head(boxoffice$Movie_Distributor, n = 2)
extrait extrait
## [1] "Star Wars: The Force Awakens (Disney)" "Avatar (Fox)"
toupper(extrait)
## [1] "STAR WARS: THE FORCE AWAKENS (DISNEY)" "AVATAR (FOX)"
tolower(extrait)
## [1] "star wars: the force awakens (disney)" "avatar (fox)"
La fonction casefold
, utilisée comme exemple de fonction dans les notes sur les concepts de base en R, permet de faire les deux transformations de casse, soit transformer tous les caractères alphabétiques en majuscules (avec l’argument upper = TRUE
) ou transformer tous les caractères alphabétiques en minuscules (avec l’argument upper = FALSE
).
casefold(extrait, upper = TRUE)
## [1] "STAR WARS: THE FORCE AWAKENS (DISNEY)" "AVATAR (FOX)"
casefold(extrait, upper = FALSE)
## [1] "star wars: the force awakens (disney)" "avatar (fox)"
strsplit
La fonction strsplit
sépare des chaînes de caractères en sous-chaînes de caractères en coupant lors de la rencontre d’une certaine série de caractères. Tentons d’utiliser cette fonction pour séparer le nom du film du nom du distributeur, qui se retrouvent tous deux dans la colonne Movie_Distributor
du data frame boxoffice
. Demandons à strsplit
de briser le contenu de cette colonne en deux parties : ce qui se trouve avant la parenthèse ouvrante et ce qui se trouve après. Étant donné que le nom du distributeur est toujours placé à la fin de la chaîne de caractères, entre parenthèses, nous réussirons ainsi à isoler les deux éléments.
<- strsplit(x = boxoffice$Movie_Distributor, split = "(", fixed = TRUE)
res_split head(res_split, n = 2)
## [[1]]
## [1] "Star Wars: The Force Awakens " "Disney)"
##
## [[2]]
## [1] "Avatar " "Fox)"
Par défaut, la fonction considère que la valeur donnée a l’argument split
est une expression régulière. Les expressions régulières ne seront pas vues ici (mais des références pour en apprendre davantage à ce sujet sont fournies dans les références). Elles constituent un moyen puissant pour effectuer des recherches dans des chaînes de caractères. L’argument fixed = TRUE
demande à strsplit
de ne pas considérer l’argument split
comme une expression régulière, mais bien de traiter la chaîne de caractères telle qu’elle a été fournie.
Le résultat de la fonction strsplit
est toujours une liste de la même longueur que le vecteur assigné à l’argument x
. Chaque élément de la liste est un vecteur comprenant les sous-chaînes de caractères obtenues de la séparation. Ici, ces vecteurs ont le plus souvent une longueur de 2. Par exemple pour le premier film du jeu de données, Star Wars: The Force Awakens (Disney), on obtient le vecteur ["Star Wars: The Force Awakens ", "Disney)"]
. Par contre, certains titres de film comprennent un élément entre parenthèses de plus, par exemple "The Lion King (1994) (Disney)"
. Pour eux, la longueur du vecteur est 3.
La commande suivante permet d’extraire le dernier élément de chacun des vecteurs se trouvant dans res_split
. Elle utilise la fonction sapply
vue dans le cours sur les calculs par sections d’un objet R.
<- sapply(res_split, tail, n = 1)
distributeur head(distributeur, n = 2)
## [1] "Disney)" "Fox)"
substr
Afin d’avoir vraiment isolé le nom du distributeur, il ne reste plus qu’à retirer la parenthèse fermante à la fin des chaînes de caractères dans distributeur
. La fonction substr
permet d’extraire une partie de chaînes de caractères en spécifiant les positions, dans les chaînes, des caractères à conserver.
<- substr(x = distributeur, start = 1, stop = nchar(distributeur) - 1)
distributeur head(distributeur, n = 2)
## [1] "Disney" "Fox"
Ici nous voulons extraire les caractères de la première position jusqu’à l’avant-dernière position. Celle-ci varie d’une observation à l’autre, c’est pourquoi nous utilisons l’expression nchar(distributeur) - 1
pour la calculer.
Notons que nous aurions aussi pu effectuer ce traitement avec la fonction strsplit
, en utilisant )
comme point de coupure, ou encore avec la fonction sub
, comme dans l’exemple suivant.
sub
et gsub
Voyons maintenant comment il est possible de remplacer une sous-chaîne de caractères par une autre. Dans les données boxoffice
, les recettes des films comprennent un signe de dollar au début, ainsi que des virgules pour séparer les milliers. À cause de ces caractères, cette colonne ne peut pas être transformée sous un format numérique directement. Nous allons donc retirer ces caractères en les remplaçant par des chaînes de caractères vides. Ensuite, nous pourrons convertir le vecteur au format numérique plutôt que caractère.
<- boxoffice$Gross
recettes str(recettes)
## chr [1:1000] "$936,662,225" "$760,507,625" "$700,059,566" "$678,815,482" ...
<- sub(pattern = "$", replacement = "", x = recettes, fixed = TRUE)
recettes str(recettes)
## chr [1:1000] "936,662,225" "760,507,625" "700,059,566" "678,815,482" "658,672,302" ...
<- gsub(pattern = ",", replacement = "", x = recettes, fixed = TRUE)
recettes str(recettes)
## chr [1:1000] "936662225" "760507625" "700059566" "678815482" "658672302" "652270625" ...
<- as.numeric(recettes)
recettes str(recettes)
## num [1:1000] 9.37e+08 7.61e+08 7.00e+08 6.79e+08 6.59e+08 ...
Il est maintenant possible de faire des calculs numériques sur les recettes des films, puisqu’elles sont stockées sous un format numérique.
Les fonctions sub
et gsub
servent donc à chercher les occurrences d’un « motif » (argument pattern
) dans des chaînes de caractères (argument x
), puis à remplacer ces occurrences par un autre motif (argument replacement
). La fonction sub
remplace seulement la première occurrence de pattern
, alors que la fonction gsub
remplace toutes les occurrences. Tout comme la fonction strsplit
, les fonctions sub
et gsub
travaillent par défaut avec des motifs exprimés sous forme d’expressions régulières. L’argument fixed = TRUE
empêche ce comportement et implique le traitement des motifs comme des chaînes de caractères.
grep
et grepl
Supposons que nous désirions avoir une variable logique prenant la valeur TRUE
si la compagnie Disney a distribué le film, FALSE
sinon. Nous pourrions utiliser la fonction grepl
comme suit pour obtenir cette variable.
<- grepl(pattern = "Disney", x = distributeur, fixed = TRUE)
Disney sum(Disney)
## [1] 161
Ainsi, 161 des 1000 films du jeu de données boxoffice
ont été distribués par Disney.
Les fonctions grep
et grepl
identifient les éléments d’un vecteur de chaînes de caractères (argument x
) contenant un certain « motif » (argument pattern
). Pour ne pas que ce motif soit traité comme une expression régulière, il faut fournir la valeur TRUE
à l’argument fixed
. La fonction grep
retourne un vecteur numérique contenant les positions dans x
des éléments possédant le motif. Comme nous l’avons constaté dans l’exemple précédent, la fonction grepl
retourne pour sa part un vecteur logique aussi long que x
, contenant la valeur TRUE
pour les éléments possédant le motif, FALSE
sinon.
Les fonctions sub
, gsub
, grep
et grepl
sont toutes documentées dans la même fiche d’aide : celle de la fonction grep
. Quelques autres fonctions de manipulation de chaînes de caractères sont documentées dans cette fiche, mais ne sont pas présentées ici.
chartr
Lorsque nous avons besoin de remplacer une série de caractères par d’autres (chaque caractère de la série ayant une valeur de remplacement spécifique), il est possible de procéder en appelant la fonction gsub
séparément pour chaque caractère de la série. Il existe cependant une façon de faire plus rapide : utiliser la fonction chartr
. Il faut fournir à cette fonction une chaîne de caractères comprenant tous les caractères à traduire (argument old
). Il faut aussi lui fournir une chaîne de caractères comprenant tous les caractères de remplacement (argument new
), en respectant l’ordre des caractères dans le premier vecteur. Finalement, il faut fournir à chartr
le vecteur des chaînes de caractères dans lesquelles effectuer les remplacements.
Par exemple, la commande suivante permet de retirer les accents français d’un vecteur de chaînes de caractères.
chartr(
old = "ÀÂÇÈÉÊËÎÏÔÙÛÜàâçèéêëîïôùûü",
new = "AACEEEEIIOUUUaaceeeeiiouuu",
x = c("François va à l'École des Pionniers.", "Où est située cette école?")
)
## [1] "Francois va a l'Ecole des Pionniers." "Ou est situee cette ecole?"
iconv
Pour la tâche spécifique de retirer des accents, il est en fait encore plus simple d’utiliser la fonction iconv
. Celle-ci permet de convertir l’encodage de chaîne de caractères. Un bon truc pour retirer des accents de chaînes de caractères, peu importe la langue utilisée dans ces chaînes, est de convertir vers l’encodage "ASCII//TRANSLIT"
. Voici un exemple.
iconv(
x = c("François va à l'École des Pionniers.", "Où est située cette école?"),
to = "ASCII//TRANSLIT"
)
## [1] "Francois va a l'Ecole des Pionniers." "Ou est situee cette ecole?"
La sortie de cette commande peut varier d’un système d’exploitation à l’autre à l’autre.
stringr
Un package du tidyverse
se spécialise en manipulation de chaînes de caractères. Il s’agit du package stringr
, qui propose des solutions de rechange à à peu près tout ce qui a été présenté dans cette sous-section. Les noms des fonctions de ce package, et de leurs arguments, ont été choisis de façon à être cohérents entre eux, ce qui n’est pas vraiment le cas pour les fonctions de manipulation de chaînes de caractères dans le R de base. En outre, quelques fonctions du package stringr
simplifient la réalisation de tâches courantes en manipulation de chaînes de caractères. Par exemple, la fonction str_trim
permet de retirer tout espace blanc en début et fin de chaînes de caractères. Les espaces blancs (en anglais whitespace) les plus courants sont les espaces, les tabulations (\t
), et les retours à la ligne (\n
).
library(stringr)
str_trim(
string = c("\tFrançois va à l'École des Pionniers. ", "Où est située cette école?\n"),
side = "both"
)
## [1] "François va à l'École des Pionniers." "Où est située cette école?"
Des références sont fournies à la fin de ce document pour en apprendre davantage sur le package stringr
.
Une des variables de boxoffice
est une date : la variable Release_Date
contenant la date de sortie des films. Elle est pour l’instant stockée sous format caractère.
str(boxoffice$Release_Date)
## chr [1:1000] "Dec 18, 2015" "Dec 18, 2009" "Feb 16, 2018" "Apr 27, 2018" ...
Nous pourrions vouloir compter le nombre d’années écoulées depuis la sortie des films. Pour ce faire, le plus simple est d’utiliser une classe dédiée à la manipulation de dates proposée par R.
as.Date
et Sys.getlocale
Nous allons convertir les données dans la colonne Release_Date
vers la classe "Date"
de R avec la fonction as.Date
. Cependant, cette conversion fonctionnera uniquement si les paramètres régionaux (en anglais locale) de notre session R sont en anglais, puisque les dates comportent des abréviations de noms de mois en anglais. Ainsi, commençons par vérifier quel paramètre régional pour le temps utilise notre session R avec la fonction Sys.getlocale
.
Sys.getlocale(category = "LC_TIME")
## [1] "English_Canada.1252"
Dans mon cas, j’utilise un système d’exploitation en anglais. En conséquence, les paramètres régionaux de mes sessions R sont par défaut en anglais. Si ce n’est pas votre cas, il faut d’abord changer le paramètre "LC_TIME"
de votre session R pour qu’il s’agisse de l’anglais. Sous Windows, vous pouvez procéder comme suit.
# Sous Windows
Sys.setlocale("LC_TIME", locale = "English")
Les valeurs comprises comme argument locale
dépendent du système d’exploitation. Sous un système d’exploitation macOS ou Linux, tentez une des valeurs suivantes : "en_CA"
, "en_CA.UTF-8"
ou "en_CA.utf8"
(ou la version US de ces valeurs, soit "en_US"
, "en_US.UTF-8"
ou "en_US.utf8"
).
# Sous macOS
Sys.setlocale("LC_TIME", locale = "en_CA.UTF-8")
Une fois s’être assuré d’avoir un paramètre "LC_TIME"
en anglais, nous pouvons appeler la fonction as.Date
comme suit.
$Date <- as.Date(boxoffice$Release_Date, format = "%b %d, %Y")
boxofficestr(boxoffice[, c("Release_Date", "Date")])
## 'data.frame': 1000 obs. of 2 variables:
## $ Release_Date: chr "Dec 18, 2015" "Dec 18, 2009" "Feb 16, 2018" "Apr 27, 2018" ...
## $ Date : Date, format: "2015-12-18" "2009-12-18" ...
head(subset(boxoffice, select = -c(Ranking, Gross)))
## Movie_Distributor Release_Date Date
## 1 Star Wars: The Force Awakens (Disney) Dec 18, 2015 2015-12-18
## 2 Avatar (Fox) Dec 18, 2009 2009-12-18
## 3 Black Panther (Disney) Feb 16, 2018 2018-02-16
## 4 Avengers: Infinity War (Disney) Apr 27, 2018 2018-04-27
## 5 Titanic (Paramount) Dec 19, 1997 1997-12-19
## 6 Jurassic World (Universal) Jun 12, 2015 2015-06-12
difftime
et Sys.Date
Pour calculer le nombre d’années entre aujourd’hui et la date de sortie des films, tentons d’utiliser la fonction difftime
. D’abord, obtenons la date courante avec la fonction Sys.Date
.
<- Sys.Date()
aujourdui aujourdui
## [1] "2020-01-27"
Ensuite, demandons à difftime
de calculer la différence, en années, entre aujourd’hui et les dates de sortie des films.
$Age <- difftime(aujourdui, boxoffice$Date, units = "years") boxoffice
## Error in match.arg(units)\ :
## 'arg' should be one of “auto”, “secs”, “mins”, “hours”, “days”, “weeks”
La fonction difftime
ne calcule pas de différence entre des dates en années, d’où l’erreur obtenue. Elle peut cependant calculer cette différence en jours ou en semaines (aussi en secondes, minutes ou heures si les dates comprennent aussi une heure). Pour faire un calcul approximatif, mais presque exact, du nombre d’années écoulées entre aujourd’hui et la sortie des films, nous pourrions d’abord calculer le temps écoulé en jours. Ensuite, nous pourrions diviser ce nombre de jours par 365.25 jours, soit le nombre moyen de jours dans une année.
$Age <- as.numeric(difftime(aujourdui, boxoffice$Date, units = "days") / 365.25)
boxofficehead(subset(boxoffice, select = -c(Ranking, Gross)))
## Movie_Distributor Release_Date Date Age
## 1 Star Wars: The Force Awakens (Disney) Dec 18, 2015 2015-12-18 4.109514
## 2 Avatar (Fox) Dec 18, 2009 2009-12-18 10.108145
## 3 Black Panther (Disney) Feb 16, 2018 2018-02-16 1.943874
## 4 Avengers: Infinity War (Disney) Apr 27, 2018 2018-04-27 1.752225
## 5 Titanic (Paramount) Dec 19, 1997 1997-12-19 22.105407
## 6 Jurassic World (Universal) Jun 12, 2015 2015-06-12 4.626968
Cet âge est presque exact. Cependant, il y a une petite erreur due au fait que nous ne savons pas combien il y a eu d’années bissextiles entre aujourd’hui et la date de sortie des films. Une année ne comporte pas 365.25 jours, mais bien 365 ou 366 jours.
format
Une date peut se présenter sous plusieurs formats, explicités dans la fiche d’aide nommée strptime
.
help(strptime)
Grâce à ces différents formats, nous pourrions par exemple extraire le mois de sortie d’un film grâce à la fonction format
comme suit.
$Month <- as.integer(format(boxoffice$Date, "%m")) boxoffice
Voyons ce que ça donne.
head(subset(boxoffice, select = -c(Ranking, Gross)))
## Movie_Distributor Release_Date Date Age Month
## 1 Star Wars: The Force Awakens (Disney) Dec 18, 2015 2015-12-18 4.109514 12
## 2 Avatar (Fox) Dec 18, 2009 2009-12-18 10.108145 12
## 3 Black Panther (Disney) Feb 16, 2018 2018-02-16 1.943874 2
## 4 Avengers: Infinity War (Disney) Apr 27, 2018 2018-04-27 1.752225 4
## 5 Titanic (Paramount) Dec 19, 1997 1997-12-19 22.105407 12
## 6 Jurassic World (Universal) Jun 12, 2015 2015-06-12 4.626968 6
Note : Il existe d’autres classes dédiées à la manipulation de dates en R qui permettent d’inclure l’heure dans la date (heure, minute, seconde) : "POSIXlt"
et "POSIXct"
. Avec ces classes, la prise en compte des fuseaux horaires est primordiale. Nous n’approfondirons pas l’utilisation de ces classes. Seule une petite partie des possibilités de manipulation de dates en R est abordée ici.
lubridate
Le tidyverse
offre aussi un outil pour manipuler des dates en R : le package lubridate
. Par exemple, le calcul approximatif de temps en années entre deux dates que nous avons effectué précédemment peut être fait de façon exacte avec lubridate
comme suit.
library(lubridate)
$Date_2 <- mdy(boxoffice$Release_Date, locale = "English")
boxoffice$Age_2 <- time_length(interval(start = boxoffice$Date_2, end = now()), "years")
boxofficehead(subset(boxoffice, select = c(Movie_Distributor, Date, Date_2, Age, Age_2)))
## Movie_Distributor Date Date_2 Age Age_2
## 1 Star Wars: The Force Awakens (Disney) 2015-12-18 2015-12-18 4.109514 4.109290
## 2 Avatar (Fox) 2009-12-18 2009-12-18 10.108145 10.109290
## 3 Black Panther (Disney) 2018-02-16 2018-02-16 1.943874 1.945205
## 4 Avengers: Infinity War (Disney) 2018-04-27 2018-04-27 1.752225 1.751366
## 5 Titanic (Paramount) 1997-12-19 1997-12-19 22.105407 22.106557
## 6 Jurassic World (Universal) 2015-06-12 2015-06-12 4.626968 4.625683
La fonction time_length
tient compte des années bissextiles. Elle peut aussi calculer des durées en mois tout en considérant les durées variables de ceux-ci. Remarquons que l’argument locale
de la fonction mdy
nous a permis de spécifier un paramètre régional s’appliquant uniquement à la conversion à effectuer. La modification d’un paramètre régional global de la session R n’est pas nécessaire avec les fonctions du packages lubridate
. Des références sont fournies à la fin de ce document pour en apprendre davantage sur le package lubridate
.
Nous venons de voir différentes façons de manipuler des variables dans un jeu de données sous forme de data frame. Voyons maintenant différentes manipulations usuelles de jeux de données entiers, soit :
Pour illustrer des manipulations, reprenons le data frame data_ex
créé dans le cours concernant la lecture et l’écriture dans des fichiers externes à partir de R, en modifiant une valeur afin d’introduire une observation dupliquée.
<- c(2, 3, 4, 1, 2, 3, 5, 6, 5, 4)
de_1 <- c(1, 4, 2, 3, 1, 4, 6, 2, 5, 3) # 5e valeur pas comme dans les notes précédentes
de_2 <- rep(c("Luc", "Kim"), each = 5)
lanceur <- data.frame(de1 = de_1, de2 = de_2, lanceur = lanceur)
data_ex # Introduction de valeurs manquantes
$de2[7] <- NA
data_ex$lanceur[3] <- NA
data_ex# Affichage du date frame
data_ex
## de1 de2 lanceur
## 1 2 1 Luc
## 2 3 4 Luc
## 3 4 2 <NA>
## 4 1 3 Luc
## 5 2 1 Luc
## 6 3 4 Kim
## 7 5 NA Kim
## 8 6 2 Kim
## 9 5 5 Kim
## 10 4 3 Kim
Il est parfois utile de gérer les observations dupliquées dans un jeu de données. En R, les fonctions suivantes sont utiles avec des observations dupliquées :
duplicated
,unique
.Une observation est ici définie par l’ensemble des valeurs observées de toutes les variables pour un individu (ou une unité) de la population statistique à l’étude. Donc une observation dupliquée est une ligne répétée (donc deux lignes ou plus complètement identiques) dans une matrice ou un data frame. Dans le cas d’une seule variable, stockée dans un vecteur, une observation dupliquée est une valeur présente plus d’une fois dans le vecteur.
Dans le jeu de données data_ex
, les lignes 1 et 5 de sont identiques. La fonction duplicated
identifie la 5e observation comme une duplication d’une autre observation.
duplicated(data_ex)
## [1] FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE
L’observation peut être retirée avec la fonction unique
comme suit.
unique(data_ex)
## de1 de2 lanceur
## 1 2 1 Luc
## 2 3 4 Luc
## 3 4 2 <NA>
## 4 1 3 Luc
## 6 3 4 Kim
## 7 5 NA Kim
## 8 6 2 Kim
## 9 5 5 Kim
## 10 4 3 Kim
Si elles reçoivent un vecteur en entrée, les fonctions duplicated
et unique
réagissent comme suit.
duplicated(c(1, 3, 2, 1, 2, 1))
## [1] FALSE FALSE FALSE TRUE TRUE TRUE
unique(c(1, 3, 2, 1, 2, 1))
## [1] 1 3 2
Comme pour les observations dupliquées, il existe des outils en R pour identifier ou retirer les observations contenant des données manquantes :
complete.cases
,na.omit
.Dans le jeu de données data_ex
, les lignes 3 et 7 contiennent chacune une donnée manquante. La fonction complete.cases
identifie ces lignes comme n’étant pas des cas complets (FALSE
retourné pour les lignes contenant des NA
).
complete.cases(data_ex)
## [1] TRUE TRUE FALSE TRUE TRUE TRUE FALSE TRUE TRUE TRUE
Ces deux observations peuvent facilement être retirées avec la fonction na.omit
comme suit.
na.omit(data_ex)
## de1 de2 lanceur
## 1 2 1 Luc
## 2 3 4 Luc
## 4 1 3 Luc
## 5 2 1 Luc
## 6 3 4 Kim
## 8 6 2 Kim
## 9 5 5 Kim
## 10 4 3 Kim
La fonction na.omit
a déjà été vue dans les notes sur les calculs statistiques en R). Cette fonction ajoute des attributs à l’objet retourné pour identifier les observations omises.
<- na.omit(data_ex)
data_ex_no_NA str(data_ex_no_NA)
## 'data.frame': 8 obs. of 3 variables:
## $ de1 : num 2 3 1 2 3 6 5 4
## $ de2 : num 1 4 3 1 4 2 5 3
## $ lanceur: chr "Luc" "Luc" "Luc" "Luc" ...
## - attr(*, "na.action")= 'omit' Named int [1:2] 3 7
## ..- attr(*, "names")= chr [1:2] "3" "7"
Les fonctions complete.cases
et na.omit
fonctionnent aussi avec des vecteurs, comme le montre ces exemples.
complete.cases(c(1, 3, 2, NA, 2, 1)) # équivalent à !is.na(c(1, 3, 2, NA, 2, 1))
## [1] TRUE TRUE TRUE FALSE TRUE TRUE
na.omit(c(1, 3, 2, NA, 2, 1))
## [1] 1 3 2 2 1
## attr(,"na.action")
## [1] 4
## attr(,"class")
## [1] "omit"
La fonction is.na
(vue dans les notes sur les calculs mathématiques en R), utilisée avec l’opérateur [
, permet aussi de retirer les données manquantes d’un vecteur.
<- c(1, 3, 2, NA, 2, 1)
x !is.na(x)] x[
## [1] 1 3 2 2 1
Remarquons que cette solution de rechange à na.omit
n’ajoute pas d’attributs au résultat.
Sélectionner un sous-ensemble de données signifie extraire d’un jeu de données une partie des observations (lignes) et/ou des variables (colonnes) qu’il contient. La sélection d’observations répondant à certains critères est aussi parfois appelée filtrage de données. La matière couverte dans les notes sur les calculs de base en R concernant les conditions logiques nous sera très utile pour tester des critères.
[
et fonction subset
Les principaux outils permettant d’extraire des sous-ensembles de données stockées dans un data frame ont déjà été présentés dans les notes sur les
structures de données en R. Il s’agit de l’opérateur d’indiçage [
et de la fonction d’extraction subset
. La fonction subset
a d’ailleurs été utilisée dans les derniers exemples pour sélectionner seulement certaines variables dans l’affichage des résultats.
Pour illustrer l’utilisation de ces outils dans un contexte de sélection de sous-ensembles de données, voyons comment conserver du jeu de données data_ex
seulement les résultats de lancers de dés effectués par Kim et ne comprenant aucune donnée manquante. Dans ce sous-ensemble, la variable lanceur
prendra toujours la même valeur. Elle ne sera donc plus vraiment pertinente et sera retirée.
Tout d’abord, voyons comment effectuer cette tâche avec l’opérateur [
, étape par étape. Pour limiter les observations à celles pour lesquelles la variable lanceur
prend la valeur Kim
, nous écrivons la condition logique représentant ce critère et la fournissons au premier argument de l’opérateur [
à plusieurs dimensions.
$lanceur == "Kim", ] data_ex[data_ex
## de1 de2 lanceur
## NA NA NA <NA>
## 6 3 4 Kim
## 7 5 NA Kim
## 8 6 2 Kim
## 9 5 5 Kim
## 10 4 3 Kim
Remarquons que l’observation pour laquelle la valeur de la variable lanceur
est manquante a été conservée. Ensuite, la façon la plus simple de retirer les lignes comprenant au moins une donnée manquante est d’utiliser la fonction na.omit
mentionnée précédemment.
na.omit(data_ex[data_ex$lanceur == "Kim", ])
## de1 de2 lanceur
## 6 3 4 Kim
## 8 6 2 Kim
## 9 5 5 Kim
## 10 4 3 Kim
Finalement, la variable lanceur peut être retirée en fournissant les noms des autres variables, celles à conserver, au deuxième argument de l’opérateur [
. Dans un cas de manipulation de data frame, l’identification des variables par leur nom plutôt que par leur position permet d’éviter des erreurs.
na.omit(data_ex[data_ex$lanceur == "Kim", c("de1", "de2")])
## de1 de2
## 6 3 4
## 8 6 2
## 9 5 5
## 10 4 3
Effectuons maintenant le même travail avec la fonction subset
, encore une fois étape par étape, pour bien comprendre les traitements réalisés.
subset(data_ex, subset = lanceur == "Kim")
## de1 de2 lanceur
## 6 3 4 Kim
## 7 5 NA Kim
## 8 6 2 Kim
## 9 5 5 Kim
## 10 4 3 Kim
Tout d’abord, remarquons que le comportement de la fonction subset
diffère un peu de celui de l’opérateur [
. L’observation pour laquelle la valeur de la variable lanceur
est manquante n’a pas été conservée. Nous pourrions retirer les observations contenant des valeurs manquantes pour les autres variables en combinant des conditions logiques comme suit.
subset(data_ex, subset = !is.na(de1) & !is.na(de2) & lanceur == "Kim")
## de1 de2 lanceur
## 6 3 4 Kim
## 8 6 2 Kim
## 9 5 5 Kim
## 10 4 3 Kim
Cependant, il est plus simple d’utiliser encore une fois la fonction na.omit
. Pour retirer la variable lanceur
, l’argument select
de la fonction subset
peut être spécifié comme dans la commande suivante, qui accompli la tâche souhaitée.
na.omit(subset(data_ex, subset = lanceur == "Kim", select = -lanceur))
## de1 de2
## 6 3 4
## 8 6 2
## 9 5 5
## 10 4 3
La possibilité de pouvoir retirer des variables en utilisant l’opérateur -
devant leur nom permet de simplifier des commandes. Pour conserver ou retirer plus d’une variable, il faut fournir leurs noms dans un vecteur, comme nous l’avons fait précédemment dans la commande suivante (non exécutée ici).
head(subset(boxoffice, select = -c(Ranking, Gross)))
filter
et select
du package dplyr
Le package dplyr
attribue les tâches de sélection d’observations et de variables à deux fonctions distinctes :
Par exemple, nous pouvons sélectionner les observations de data_ex
pour lesquelles la valeur de la variable lancer
est "Kim"
avec la fonction filter
comme suit.
filter(data_ex, lanceur == "Kim")
## de1 de2 lanceur
## 1 3 4 Kim
## 2 5 NA Kim
## 3 6 2 Kim
## 4 5 5 Kim
## 5 4 3 Kim
Tout comme le fait la fonction subset
, les lignes pour lesquelles la condition vaut NA
n’ont pas été conservées. Pour retirer les observations contenant des données manquantes, nous pouvons utiliser na.omit
, ou encore drop_na
, une fonction similaire contenue dans le package tidyr
.
drop_na(filter(data_ex, lanceur == "Kim"))
## de1 de2 lanceur
## 1 3 4 Kim
## 2 6 2 Kim
## 3 5 5 Kim
## 4 4 3 Kim
Notons que la fonction drop_na
permet de limiter la recherche de valeurs manquantes à un sous-ensemble de variables, ce qui n’est pas possible avec na.omit
.
Ensuite, la variable lanceur
peut être retirée à l’aide de la fonction select
comme suit.
select(drop_na(filter(data_ex, lanceur == "Kim")), -lanceur)
## de1 de2
## 1 3 4
## 2 6 2
## 3 5 5
## 4 4 3
Cette commande est un peu difficile à lire en raison du grand nombre d’appels de fonctions imbriqués qu’elle contient. Elle serait peut-être plus claire en ajoutant des retours à la ligne et des indentations comme suit.
select(
drop_na(
filter(
data_ex, == "Kim"
lanceur
)
), -lanceur
)
## de1 de2
## 1 3 4
## 2 6 2
## 3 5 5
## 4 4 3
La pratique courante avec les packages du tidyverse
est cependant d’utiliser l’opérateur « pipe » %>%
du package magrittr
pour rendre les commandes plus lisibles. En utilisant cet opérateur, la commande aurait l’allure suivante.
%>% filter(lanceur == "Kim") %>% drop_na() %>% select(-lanceur) data_ex
## de1 de2
## 1 3 4
## 2 6 2
## 3 5 5
## 4 4 3
Je ne fais que mentionner cet opérateur ici. Il est recommandé, mais pas obligatoire, de l’employer lors de l’utilisation de packages du tidyverse
. L’utilisation de cet opérateur est expliqué dans les notes sur les bonnes pratiques de programmation en R.
[
et fonction subset
du package data.table
Pour effectuer de la sélection de sous-ensembles de données avec la package data.table
, nous pouvons exploiter son opérateur [
et ses arguments i
et j
ou encore sa méthode pour la fonction subset
(qui est en fait une fonction générique) spécifique aux data tables. Illustrons ici l’utilisation de l’opérateur [
. La fonction subset
s’utilise à peu près de la même façon avec un data table qu’avec un data frame.
Après avoir transformé le data frame data_ex
en data table,
<- as.data.table(data_ex) data_ex_dt
nous pouvons l’indicer à l’aide de l’opérateur [
de data.table
en fournissant une condition logique à l’argument i
pour sélectionner des lignes.
= lanceur == "Kim"] data_ex_dt[i
## de1 de2 lanceur
## 1: 3 4 Kim
## 2: 5 NA Kim
## 3: 6 2 Kim
## 4: 5 5 Kim
## 5: 4 3 Kim
Dans les commandes suivantes, je vais omettre l’assignation explicite à l’argument i
puisque ce n’est vraiment pas une pratique courante d’inclure cette assignation dans un appel à [
. Selon l’ordre des arguments de l’opérateur dans sa définition (voir la section Usage de sa fiche d’aide), la première valeur fournie après [
est assignée à l’argument i
et la deuxième à l’argument j
si elles ne sont pas explicitement assignées à un nom d’argument (et si aucune valeur fournie en troisième position ou plus loin n’est assignée à i
ou j
, selon les règles d’assignation implicite de valeurs aux arguments présentée dans les notes sur les concepts de base en R).
Le package data.table
offre une méthode pour la fonction na.omit
spécifique aux data tables.
na.omit(data_ex_dt[lanceur == "Kim"])
## de1 de2 lanceur
## 1: 3 4 Kim
## 2: 6 2 Kim
## 3: 5 5 Kim
## 4: 4 3 Kim
La sélection de variables dans un data table s’effectue en fournissant une valeur à l’argument j
. Par exemple, nous pouvons sélectionner les variables de1
et de2
de data_ex_dt
comme suit (commande non évaluée),
na.omit(data_ex_dt[lanceur == "Kim", .(de1, de2)])
ou encore omettre la variable lanceur
, comme suit.
na.omit(data_ex_dt[lanceur == "Kim", !c("lanceur")])
## de1 de2
## 1: 3 4
## 2: 6 2
## 3: 5 5
## 4: 4 3
Remarquons que l’omission de variables s’indique avec l’opérateur !
avec data.table
, plutôt qu’avec -
.
Combiner des données signifie de mettre en commun deux jeux de données ou plus. Ils peuvent être mis en commun par une simple concaténation, c’est-à-dire une mise bout à bout, de lignes ou de colonnes. Ils peuvent aussi être fusionnés par association en tenant compte des valeurs prises par des variables communes aux jeux de données.
rbind
Supposons que nous avons en main deux jeux de données contenant différentes observations pour les mêmes variables. Par exemple, en plus du data frame data_ex
, disons que nous avons les observations supplémentaires suivantes.
<- data.frame(de1 = c(2, 5), lanceur = c("Luc", "Kim"), de2 = c(5, 6))
supp supp
## de1 lanceur de2
## 1 2 Luc 5
## 2 5 Kim 6
Pour concaténer les jeux de données data_ex
et supp
en un seul jeu de données, nous pourrions utiliser la fonction rbind
comme suit :
rbind(data_ex, supp)
## de1 de2 lanceur
## 1 2 1 Luc
## 2 3 4 Luc
## 3 4 2 <NA>
## 4 1 3 Luc
## 5 2 1 Luc
## 6 3 4 Kim
## 7 5 NA Kim
## 8 6 2 Kim
## 9 5 5 Kim
## 10 4 3 Kim
## 11 2 5 Luc
## 12 5 6 Kim
Remarquons que rbind
a su mettre dans les mêmes colonnes les observations des variables portant les mêmes noms, même si elles n’étaient pas dans le même ordre dans les deux jeux de données à combiner.
Il serait maintenant judicieux d’assigner cette concaténation à un nom afin de conserver le résultat obtenu dans un objet. Si nous assignions cet appel à rbind
au nom data_ex
, le jeu de données data_ex
d’origine serait remplacé par le résultat de la concaténation. Nous pourrions aussi créer un nouvel objet pour stocker le résultat.
cbind
ou data.frame
Lorsque nous avons ajouté une variable à un jeu de données à la section 1.1.1, nous avons réalisé une forme de concaténation de jeux de données en colonnes. Voici une copie d’un exemple réalisé.
$dist_m <- cars$dist * 0.3048 cars
La même tâche aurait pu être effectuée en utilisant en appelant la fonction cbind
ou data.frame
comme suit.
<- cbind(cars, dist_m = cars$dist * 0.3048)
cars # ou encore
<- data.frame(cars, dist_m = cars$dist * 0.3048) cars
Dans cette forme de concaténation, le deuxième jeu de données fusionné est constitué d’une seule variable, stockée dans un vecteur, et le résultat de la concaténation remplace le premier jeu de données fusionné.
La concaténation de variables peut être plus générale que ça. Nous pourrions concaténer deux jeux de données contenant chacun plus d’une variable et stocker le résultat de la concaténation dans un nouvel objet.
Par exemple, disons que nous avons un jeu de données contenant des informations sur les lanceurs de dés du jeu de données data_ex
.
<- data.frame(
data_ex_lanceurs lateralite = rep(c("gaucher", "droitier"), each = 5),
age = rep(c(22, 51), each = 5)
) data_ex_lanceurs
## lateralite age
## 1 gaucher 22
## 2 gaucher 22
## 3 gaucher 22
## 4 gaucher 22
## 5 gaucher 22
## 6 droitier 51
## 7 droitier 51
## 8 droitier 51
## 9 droitier 51
## 10 droitier 51
Pour concaténer en colonnes les jeux de données data_ex
et data_ex_lanceurs
, nous pourrions utiliser la fonction cbind
ou data.frame
comme suit.
cbind(data_ex, data_ex_lanceurs)
# ou encore
data.frame(data_ex, data_ex_lanceurs)
## de1 de2 lanceur lateralite age
## 1 2 1 Luc gaucher 22
## 2 3 4 Luc gaucher 22
## 3 4 2 <NA> gaucher 22
## 4 1 3 Luc gaucher 22
## 5 2 1 Luc gaucher 22
## 6 3 4 Kim droitier 51
## 7 5 NA Kim droitier 51
## 8 6 2 Kim droitier 51
## 9 5 5 Kim droitier 51
## 10 4 3 Kim droitier 51
Le résultat de la concaténation pourrait être assigné à un nom d’objet R existant ou à un nouveau nom, créant du coup un nouvel objet.
merge
Une concaténation de variables crée un jeu de données utile seulement si les valeurs sur une même ligne réfèrent toutes à un même individu (ou unité statistique) et les valeurs sur une même colonne à une même variable. Par exemple, le résultat que nous venons d’obtenir nous porte à croire que Luc est gaucher et qu’il a 22 ans, et que Kim est droitière et a 51 ans. Mais rien dans le jeu de données data_ex_lanceurs
ne dit à quels lanceurs correspondent les caractéristiques. Et la ligne 3 de ce jeu de données est surprenante. Le nom du lanceur est manquant, mais sa latéralité et son âge sont connus.
Avec une fusion par concaténation de variables, les risques d’erreurs sont présents. Il est plus prudent de fusionner des jeux de données en spécifiant par rapport à quelles variables établir les correspondances entre les lignes des deux jeux de données. Appelons ce type de combinaison de données jointure ou fusion par association. Une telle fusion peut s’effectuer en R avec la fonction merge
.
Dans l’exemple précédent, nous aurions préféré fusionner le jeu de données data_ex
au jeu de données suivant.
<- data.frame(
lanceurs nom = c("Luc", "Kim"),
lateralite = c("gaucher", "droitier"),
age = c(22, 51)
) lanceurs
## nom lateralite age
## 1 Luc gaucher 22
## 2 Kim droitier 51
Ce jeu de données indique clairement à quel lanceur sont associées les différentes caractéristiques.
Une fusion par association selon la valeur de la variable identifiant le lanceur entre les data frames data_ex
et lanceurs
peut s’effectuer comme suit.
merge(
x = data_ex, y = lanceurs,
by.x = "lanceur", by.y = "nom",
all = TRUE, sort = FALSE
)
## lanceur de1 de2 lateralite age
## 1 Luc 2 1 gaucher 22
## 2 Luc 3 4 gaucher 22
## 3 Luc 1 3 gaucher 22
## 4 Luc 2 1 gaucher 22
## 5 Kim 3 4 droitier 51
## 6 Kim 5 NA droitier 51
## 7 Kim 6 2 droitier 51
## 8 Kim 5 5 droitier 51
## 9 Kim 4 3 droitier 51
## 10 <NA> 4 2 <NA> NA
Ici, étant donné que la variable selon laquelle établir les correspondances ne porte pas le même nom dans les deux jeux de données, nous avons dû spécifier les arguments by.x
et by.y
. Ces arguments prennent comme valeur un vecteur contenant les noms des variables d’association dans le jeu de données fourni en entrée à l’argument x
pour by.x
et y
pour by.y
. Lorsque les variables d’association portent le même nom partout, seul l’argument by
a besoin d’être spécifié. Si aucune valeur n’est fournie aux arguments by.x
et by.y
ou by
, les variables utilisées pour associer les lignes sont celles portant le même nom dans les deux jeux de données.
L’argument sort
permet d’indiquer à merge
si les observations doivent être ordonnées dans la sortie (par défaut elles le sont) selon les valeurs des variables d’association. Si l’utilisateur demande à ce que les observations ne soient pas ordonnées, comme dans l’exemple précédent, les observations contenant des valeurs manquantes pour les variables d’association sont tout de même placées à la fin du data frame retourné en sortie.
L’argument all
permet quant à lui de spécifier à merge
quelles observations doivent être conservées dans la sortie. Par défaut, seules les observations ayant pu être associées à une observation de l’autre jeu de données sont conservées. Selon la terminologie des bases de données relationnelles, la fonction merge
effectue par défaut une jointure interne (en anglais inner join). Dans l’exemple, nous avons plutôt spécifié all = TRUE
afin de conserver toutes les observations des deux jeux de données. Nous avons donc effectué une jointure externe complète (en anglais full outer join). Avec les arguments all.x
et all.y
, nous aurions aussi pu effectuer des jointures externes gauches ou droites (en anglais left or right outer join).
dplyr
Le package dplyr
propose la fonction bind_rows
en remplacement de rbind
et bind_cols
en remplacement de cbind
. Une capacité intéressante de bind_rows
est d’arriver à concaténer en lignes des jeux de données ne comportant pas toutes les mêmes variables. Des valeurs manquantes sont ajoutées dans les colonnes des variables non communes aux deux jeux de données.
<- data.frame(supp, lateralite = c("gaucher", "droitier"))
supp_2 supp_2
## de1 lanceur de2 lateralite
## 1 2 Luc 5 gaucher
## 2 5 Kim 6 droitier
bind_rows(data_ex, supp_2)
## de1 de2 lanceur lateralite
## 1 2 1 Luc <NA>
## 2 3 4 Luc <NA>
## 3 4 2 <NA> <NA>
## 4 1 3 Luc <NA>
## 5 2 1 Luc <NA>
## 6 3 4 Kim <NA>
## 7 5 NA Kim <NA>
## 8 6 2 Kim <NA>
## 9 5 5 Kim <NA>
## 10 4 3 Kim <NA>
## 11 2 5 Luc gaucher
## 12 5 6 Kim droitier
Le fonction bind_rows
peut aussi ajouter une colonne dans le résultat de la concaténation pour identifier la provenance des lignes grâce à son argument .id
.
bind_rows(jeu1 = data_ex, jeu2 = supp_2, .id = "origine")
## origine de1 de2 lanceur lateralite
## 1 jeu1 2 1 Luc <NA>
## 2 jeu1 3 4 Luc <NA>
## 3 jeu1 4 2 <NA> <NA>
## 4 jeu1 1 3 Luc <NA>
## 5 jeu1 2 1 Luc <NA>
## 6 jeu1 3 4 Kim <NA>
## 7 jeu1 5 NA Kim <NA>
## 8 jeu1 6 2 Kim <NA>
## 9 jeu1 5 5 Kim <NA>
## 10 jeu1 4 3 Kim <NA>
## 11 jeu2 2 5 Luc gaucher
## 12 jeu2 5 6 Kim droitier
De plus, au lieu d’offrir une seule fonction capable d’effectuer plusieurs types de fusions par association, le package dplyr
comporte plusieurs fonctions de jointure effectuant chacune un seul type de jointure, identifié par le nom de la fonction : inner_join
, full_join
, left_join
, right_join
, etc. L’exemple de fusion par association effectué avec merge
se reproduit comme suit avec une fonction de dplyr
.
full_join(x = data_ex, y = lanceurs, by = c("lanceur" = "nom"))
## de1 de2 lanceur lateralite age
## 1 2 1 Luc gaucher 22
## 2 3 4 Luc gaucher 22
## 3 4 2 <NA> <NA> NA
## 4 1 3 Luc gaucher 22
## 5 2 1 Luc gaucher 22
## 6 3 4 Kim droitier 51
## 7 5 NA Kim droitier 51
## 8 6 2 Kim droitier 51
## 9 5 5 Kim droitier 51
## 10 4 3 Kim droitier 51
Contrairement à merge
, les fonctions de jointure de dplyr
ne modifient aucunement l’ordre des variables et l’ordre des observations. Ainsi, la ou les variables d’association ne sont pas placées au début et les observations avec des valeurs d’association manquantes ne sont pas placées à la fin dans le jeu de données produit en sortie.
rbindlist
et merge
du package data.table
Le package data.table
contient la fonction rbindlist
, qui est une solution de rechange à rbind
du R de base ou bind_rows
de dplyr
. Elle a les mêmes capacités que cette dernière.
rbindlist(list(jeu1 = data_ex, jeu2 = supp_2), fill = TRUE, idcol = "origine")
## origine de1 de2 lanceur lateralite
## 1: jeu1 2 1 Luc <NA>
## 2: jeu1 3 4 Luc <NA>
## 3: jeu1 4 2 <NA> <NA>
## 4: jeu1 1 3 Luc <NA>
## 5: jeu1 2 1 Luc <NA>
## 6: jeu1 3 4 Kim <NA>
## 7: jeu1 5 NA Kim <NA>
## 8: jeu1 6 2 Kim <NA>
## 9: jeu1 5 5 Kim <NA>
## 10: jeu1 4 3 Kim <NA>
## 11: jeu2 2 5 Luc gaucher
## 12: jeu2 5 6 Kim droitier
Le package data.table
comporte aussi une méthode pour la fonction merge
spécifique aux data tables qui s’utilise de la même façon que la méthode merge
pour data frames (celle dont nous avons parlé précédemment).
<- as.data.table(lanceurs)
lanceurs_dt merge(
x = data_ex_dt, y = lanceurs_dt,
by.x = "lanceur", by.y = "nom",
all = TRUE, sort = FALSE
)
## lanceur de1 de2 lateralite age
## 1: Luc 2 1 gaucher 22
## 2: Luc 3 4 gaucher 22
## 3: <NA> 4 2 <NA> NA
## 4: Luc 1 3 gaucher 22
## 5: Luc 2 1 gaucher 22
## 6: Kim 3 4 droitier 51
## 7: Kim 5 NA droitier 51
## 8: Kim 6 2 droitier 51
## 9: Kim 5 5 droitier 51
## 10: Kim 4 3 droitier 51
Il est aussi possible de fusionner des données avec l’opérateur [
du package data.table
et son argument on
, mais cette fonctionnalité ne sera pas illustrée ici.
Il existe différentes fonctions en R pour ordonner les éléments d’un objet.
rev
Une des fonctions les plus simples pour effectuer un ordonnancement d’éléments est la fonction rev
, qui renverse l’ordre des éléments d’un vecteur (ou un facteur).
$de1 data_ex
## [1] 2 3 4 1 2 3 5 6 5 4
rev(data_ex$de1)
## [1] 4 5 6 5 3 2 1 4 3 2
sort
Pour effectuer l’ordonnancement des éléments d’un vecteur (ou un facteur) selon les valeurs des données en éléments, de façon à replacer les éléments en ordre croissant ou décroissant de valeur, il faut plutôt utiliser la fonction sort
. Par exemple, ordonnons des valeurs numériques en ordre décroissant.
$de1 data_ex
## [1] 2 3 4 1 2 3 5 6 5 4
sort(data_ex$de1, decreasing = TRUE)
## [1] 6 5 5 4 4 3 3 2 2 1
Par défaut, l’ordre croissant est retourné. Alors pour obtenir un ordre décroissant, il faut spécifier decreasing = TRUE
.
Nous avons vu dans les notes sur les calculs mathématiques en R comment connaître l’ordre utilisé par notre session R pour classer des caractères. Rappelons qu’il suffit de fournir à la fonction sort
un vecteur contenant tous les caractères potentiels, par exemple.
<-
caracteres_speciaux c("!", "\"", "#", "$", "%", "&", "'", "(", ")", "*", "+", ",", "-", ".", "/", ":", ";",
"<", "=",">", "?", "@", "[", "\\", "]", "^", "_", "{", "|", "}", "~")
<- c("à", "â", "é", "è", "ê", "ë", "ï", "î", "ô", "ù", "ü", "û", "ç")
lettres_accentuees <- sort(c(caracteres_speciaux, 0:9, letters, LETTERS,
catacteres_ordonnes toupper(lettres_accentuees)))
lettres_accentuees, paste(catacteres_ordonnes, collapse = "")
Dans cette commande, l’appel à la fonction paste
sert uniquement à raccourcir la sortie en mettant bout à bout les caractères ordonnés dans une seule chaîne de caractères. J’obtiens le résultat suivant, qui sera peut-être différent sur votre ordinateur si vous n’avez pas les mêmes paramètres régionaux que moi.
"'-!\"#$%&()*,./:;?@[\]^_{|}~+<=>0123456789aAàÀâÂbBcCçÇdDeEéÉèÈêÊëËfFgGhHiIîÎïÏjJkKlLmM
nNoOôÔpPqQrRsStTuUùÙûÛüÜvVwWxXyYzZ"
Ordonnons en ordre alphabétique croissant les noms des lanceurs dans le jeu de données data_ex
.
$lanceur data_ex
## [1] "Luc" "Luc" NA "Luc" "Luc" "Kim" "Kim" "Kim" "Kim" "Kim"
sort(data_ex$lanceur)
## [1] "Kim" "Kim" "Kim" "Kim" "Kim" "Luc" "Luc" "Luc" "Luc"
Remarquons que la valeur manquante est omise dans la sortie. C’est le comportement par défaut de la fonction sort
. Pour forcer sort
à inclure les valeurs manquantes dans la sortie, il faut lui dire où les placer avec l’argument na.last
comme suit.
sort(data_ex$lanceur, na.last = TRUE)
## [1] "Kim" "Kim" "Kim" "Kim" "Kim" "Luc" "Luc" "Luc" "Luc" NA
[
L’opérateur d’indiçage [
a pratiquement toujours été utilisé jusqu’à maintenant comme un outil d’extraction d’éléments. Dans les notes sur les structures de données, il a été mentionné que cet opérateur acceptait différents types de données en entrée pour identifier les éléments à extraire : entiers positifs, entiers négatifs, chaînes de caractères ou valeurs logiques. Deux de ces types de données permettent également de réordonner les éléments : les entiers positifs et les chaînes de caractères.
Par exemple, pour modifier l’ordre des colonnes du data frame data_ex
afin de placer la colonne nommée lanceur
en première position plutôt qu’en dernière, il suffirait d’utiliser l’opérateur [
et de donner comme valeur au deuxième argument un vecteur contenant les noms des colonnes dans l’ordre désiré, comme dans cet exemple.
# Data frame d'origine
data_ex
## de1 de2 lanceur
## 1 2 1 Luc
## 2 3 4 Luc
## 3 4 2 <NA>
## 4 1 3 Luc
## 5 2 1 Luc
## 6 3 4 Kim
## 7 5 NA Kim
## 8 6 2 Kim
## 9 5 5 Kim
## 10 4 3 Kim
# Data frame avec colonnes réordonnées
c("lanceur", "de1", "de2")] data_ex[,
## lanceur de1 de2
## 1 Luc 2 1
## 2 Luc 3 4
## 3 <NA> 4 2
## 4 Luc 1 3
## 5 Luc 2 1
## 6 Kim 3 4
## 7 Kim 5 NA
## 8 Kim 6 2
## 9 Kim 5 5
## 10 Kim 4 3
Ce réordonnancement des colonnes aurait aussi pu être effectué en fournissant un vecteur de nombres entiers, comme suit.
c(3, 1, 2)] data_ex[,
## lanceur de1 de2
## 1 Luc 2 1
## 2 Luc 3 4
## 3 <NA> 4 2
## 4 Luc 1 3
## 5 Luc 2 1
## 6 Kim 3 4
## 7 Kim 5 NA
## 8 Kim 6 2
## 9 Kim 5 5
## 10 Kim 4 3
Cette instruction a dit à R de placer la colonne qui est à l’origine en position 3 à la position 1 (car le nombre 3 est en position 1 dans le vecteur fourni au deuxième argument de [
), la colonne à l’origine en position 1 à la position 2 et finalement celle à l’origine en position 2 à la position 3. Nous pouvons appeler le vecteur c(3, 1, 2)
de la commande précédente une permutation des données.
Une permutation fournie comme premier argument à [
(avant la virgule) ordonne les lignes et une permutation fournie comme deuxième argument à [
(après la virgule, comme dans l’exemple précédent) ordonne les colonnes. Les lignes et les colonnes peuvent être ordonner simultanément. Par exemple, pour déplacer en une commande la dernière observation du data frame data_ex
en première position et la variable lanceur
en première position, nous pouvons procéder ainsi.
c(nrow(data_ex), 1:(nrow(data_ex) - 1)), c("lanceur", "de1", "de2")] data_ex[
## lanceur de1 de2
## 10 Kim 4 3
## 1 Luc 2 1
## 2 Luc 3 4
## 3 <NA> 4 2
## 4 Luc 1 3
## 5 Luc 2 1
## 6 Kim 3 4
## 7 Kim 5 NA
## 8 Kim 6 2
## 9 Kim 5 5
Notons que l’opérateur [
permet aussi de réordonner les éléments d’un vecteur ou d’une liste, ou encore les lignes ou les colonnes d’une matrice. Pour terminer, notons également que les opérateurs d’indiçage [[
et $
ne permettent pas de réordonner des éléments, car ils servent à extraire un seul élément.
order
Pour modifier l’ordre des observations ou des variables dans un jeu de données R, la fonction order
est utile. Elle permet d’obtenir un vecteur d’indice des observations dans l’ordre désiré.
Par exemple, pour ordonner les observations du jeu de données data_ex
en ordre croissant de la valeur de la variable de1
,
$de1 data_ex
## [1] 2 3 4 1 2 3 5 6 5 4
nous devons d’abord obtenir le vecteur d’indices suivant avec la fonction order
:
<- order(data_ex$de1)
permutation permutation
## [1] 4 1 5 2 6 3 10 7 9 8
Ce vecteur s’interprète comme suit : la plus petite valeur de data_ex$de1
se trouve en position 4 (valeur 1), les deuxièmes plus petites (valeur 2 qui revient 2 fois) se trouvent dans les positions 1 et 5, …, la plus grande valeur (6) se trouve en position 8. Ensuite, il suffit de fournir ce vecteur au premier argument de l’opérateur d’indiçage [
pour ordonner les lignes de data_ex
.
data_ex[permutation, ]
## de1 de2 lanceur
## 4 1 3 Luc
## 1 2 1 Luc
## 5 2 1 Luc
## 2 3 4 Luc
## 6 3 4 Kim
## 3 4 2 <NA>
## 10 4 3 Kim
## 7 5 NA Kim
## 9 5 5 Kim
## 8 6 2 Kim
Notons que nous aurions pu jumeler l’appel à order
à l’appel à [
comme suit :
order(data_ex$de1), ] data_ex[
La fonction order
accepte plus d’un vecteur de valeurs pour déterminer le nouvel ordre. Par exemple, la commande suivante permet d’ordonner les observations de data_ex
en ordre croissant de valeur de la variable de1
, en résolvant les égalités en de1
en considérant l’ordre alphabétique croissant de la variable lanceur
.
order(data_ex$de1, data_ex$lanceur), ] data_ex[
## de1 de2 lanceur
## 4 1 3 Luc
## 1 2 1 Luc
## 5 2 1 Luc
## 6 3 4 Kim
## 2 3 4 Luc
## 10 4 3 Kim
## 3 4 2 <NA>
## 7 5 NA Kim
## 9 5 5 Kim
## 8 6 2 Kim
La fonction order
a un argument decreasing
, tout comme sort
, pour demander un ordre décroissant plutôt que croissant. Par exemple, pour modifier l’ordre des colonnes de data_ex
selon l’ordre alphabétique décroissant des noms de colonnes, nous pourrions procéder comme suit.
order(names(data_ex), decreasing = TRUE)] data_ex[,
## lanceur de2 de1
## 1 Luc 1 2
## 2 Luc 4 3
## 3 <NA> 2 4
## 4 Luc 3 1
## 5 Luc 1 2
## 6 Kim 4 3
## 7 Kim NA 5
## 8 Kim 2 6
## 9 Kim 5 5
## 10 Kim 3 4
Ainsi, la fonction order
fournie la permutation selon l’ordre voulue, mais c’est l’opérateur [
qui effectue la tâche de réordonner des lignes ou des colonnes.
arrange
du package dplyr
Si l’utilisation de la fonction order
vous paraît peu conviviale, vous pouvez vous tourner vers la fonction arrange
du package dplyr
pour l’ordonnancement d’observations dans un data frame. Voici un exemple d’utilisation de cette fonction, produisant presque le même résultat que la commande suivante.
order(data_ex$de1, data_ex$lanceur), ] data_ex[
## de1 de2 lanceur
## 4 1 3 Luc
## 1 2 1 Luc
## 5 2 1 Luc
## 6 3 4 Kim
## 2 3 4 Luc
## 10 4 3 Kim
## 3 4 2 <NA>
## 7 5 NA Kim
## 9 5 5 Kim
## 8 6 2 Kim
arrange(data_ex, de1, lanceur)
## de1 de2 lanceur
## 1 1 3 Luc
## 2 2 1 Luc
## 3 2 1 Luc
## 4 3 4 Kim
## 5 3 4 Luc
## 6 4 3 Kim
## 7 4 2 <NA>
## 8 5 NA Kim
## 9 5 5 Kim
## 10 6 2 Kim
La seule différence entre les deux sorties est les noms des lignes. La fonction arrange
renomme les lignes 1 au nombre total de lignes.
Dans l’appel à la fonction arrange
, il suffit de fournir d’abord le data frame à traiter, puis les noms des variables selon lesquelles définir l’ordre. Par défaut, ces variables sont prises dans le data frame fourni comme premier argument. Si elles proviennent bien de celui-ci, nul besoin de précéder leur nom de nom_du_data_frame$
ou de l’encadrer de guillemets.
Avec arrange
, pour demander un ordre décroissant par rapport à une certaine variable, il faut envelopper le nom de la variable par un appel à la fonction desc
comme dans cet exemple.
arrange(data_ex, desc(de1), lanceur)
## de1 de2 lanceur
## 1 6 2 Kim
## 2 5 NA Kim
## 3 5 5 Kim
## 4 4 3 Kim
## 5 4 2 <NA>
## 6 3 4 Kim
## 7 3 4 Luc
## 8 2 1 Luc
## 9 2 1 Luc
## 10 1 3 Luc
La fonction arrange
s’avère donc plus souple que order
, qui applique le même type d’ordre (ascendant ou descendant) à toutes les variables. Toutefois, pour des variables numériques, le type d’ordre peut toujours être inversé en transformant la variable par elle-même multipliée par -1.
order(-data_ex$de1, data_ex$lanceur), ] data_ex[
## de1 de2 lanceur
## 8 6 2 Kim
## 7 5 NA Kim
## 9 5 5 Kim
## 10 4 3 Kim
## 3 4 2 <NA>
## 6 3 4 Kim
## 2 3 4 Luc
## 1 2 1 Luc
## 5 2 1 Luc
## 4 1 3 Luc
Notons que la fonction arrange
permet seulement d’ordonner les lignes d’un data frame (ou d’un tibble), alors que la fonction order
utilisée avec l’opérateur [
permet en fait d’ordonner les éléments de n’importe quel type de structure de données et selon n’importe laquelle de ses dimensions.
setorder
et setcolorder
du package data.table
Les fonctions setorder
et setcolorder
du package data.table
permettent d’ordonner, respectivement, les lignes et les colonnes d’un data table. Tout comme l’opérateur :=
, ces fonctions modifient un data table par référence (donc « sur place »), sans créer de copie.
Par exemple, reprenons la version data table du jeu de données data_ex
.
data_ex_dt
## de1 de2 lanceur
## 1: 2 1 Luc
## 2: 3 4 Luc
## 3: 4 2 <NA>
## 4: 1 3 Luc
## 5: 2 1 Luc
## 6: 3 4 Kim
## 7: 5 NA Kim
## 8: 6 2 Kim
## 9: 5 5 Kim
## 10: 4 3 Kim
Réordonnons ses lignes en ordre décroissant de valeur de la variable de1
et en ordre alphabétique ascendant de valeur de la variable lanceur
en cas d’égalité. Un type d’ordre descendant pour une variable s’obtient en précédent son nom de l’opérateur -
dans l’appel à la fonction setorder
. Mis à part la façon de demander un ordre descendant qui diffère, la fonction setorder
s’utilise comme la fonction arrange
de dplyr
.
setorder(data_ex_dt, -de1, lanceur)
Remarquons que cette commande ne retourne rien. Elle a toutefois eu un effet (que nous pourrions qualifier d’effet de bord). Elle a modifié le data table data_ex_dt
.
data_ex_dt
## de1 de2 lanceur
## 1: 6 2 Kim
## 2: 5 NA Kim
## 3: 5 5 Kim
## 4: 4 2 <NA>
## 5: 4 3 Kim
## 6: 3 4 Kim
## 7: 3 4 Luc
## 8: 2 1 Luc
## 9: 2 1 Luc
## 10: 1 3 Luc
Pour changer l’ordre des variables d’un data table par référence, il suffit de fournir à la fonction setcolorder
ce data table, puis un vecteur des noms de variables dans l’ordre souhaité.
setcolorder(data_ex_dt, c("lanceur", "de1", "de2"))
data_ex_dt
## lanceur de1 de2
## 1: Kim 6 2
## 2: Kim 5 NA
## 3: Kim 5 5
## 4: <NA> 4 2
## 5: Kim 4 3
## 6: Kim 3 4
## 7: Luc 3 4
## 8: Luc 2 1
## 9: Luc 2 1
## 10: Luc 1 3
La mise en forme de jeux de données est définie ici, de façon générale, par le choix de positionnement des données dans un tableau de données.
Par exemple, en statistique, les variables sont habituellement positionnées en colonnes et les observations en lignes dans un tableau de données. Que faire avec un tableau de données dans lequel les variables sont en lignes et les observations en colonnes ? Il serait judicieux de transposer le tableau (inverser les lignes et les colonnes), afin de retomber sur une mise en forme plus standard.
Toutefois, l’expression mise en forme de jeux de données réfère le plus souvent en statistique à la distinction entre les mises en forme « large » et « longue », qui sont définies un peu plus bas.
t
La fonction t
sert à transposer (inverser les lignes et les colonnes) une matrice ou un data frame.
data_ex
## de1 de2 lanceur
## 1 2 1 Luc
## 2 3 4 Luc
## 3 4 2 <NA>
## 4 1 3 Luc
## 5 2 1 Luc
## 6 3 4 Kim
## 7 5 NA Kim
## 8 6 2 Kim
## 9 5 5 Kim
## 10 4 3 Kim
<- t(data_ex)
data_ex_transpo data_ex_transpo
## [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
## de1 "2" "3" "4" "1" "2" "3" "5" "6" "5" "4"
## de2 " 1" " 4" " 2" " 3" " 1" " 4" NA " 2" " 5" " 3"
## lanceur "Luc" "Luc" NA "Luc" "Luc" "Kim" "Kim" "Kim" "Kim" "Kim"
str(data_ex_transpo)
## chr [1:3, 1:10] "2" " 1" "Luc" "3" " 4" "Luc" "4" " 2" NA "1" " 3" "Luc" "2" " 1" ...
## - attr(*, "dimnames")=List of 2
## ..$ : chr [1:3] "de1" "de2" "lanceur"
## ..$ : NULL
Cependant, la fonction t
est en fait conçue pour effectuer des calculs d’algèbre linéaire et elle retourne toujours une matrice.
Tout d’abord, introduisons un peu de terminologie informelle qui nous aidera à décrire les mises en forme.
Lorsqu’un jeu de données comprend une seule ligne par individu et que les différentes variables mesurées se trouvent dans différentes colonnes, la mise en forme du jeu de données est dite « large ».
TP1
et TP2
<- data.frame(
notes_large etudiant = c("a", "b", "c", "d"),
TP1 = c(90, 80, 70, 60),
TP2 = c(95, 74, 89, 85)
)
notes_large
## etudiant TP1 TP2
## 1 a 90 95
## 2 b 80 74
## 3 c 70 89
## 4 d 60 85
Si un jeu de données comporte plutôt plus d’une ligne par individu, sa mise en forme est dite « longue ».
<- data.frame(
notes_long etudiant = c("a", "b", "c", "d", "a", "b", "c", "d"),
note = c(90, 80, 70, 60, 95, 74, 89, 85),
evaluation = rep(c("TP1", "TP2"), each = 4)
)
notes_long
## etudiant note evaluation
## 1 a 90 TP1
## 2 b 80 TP1
## 3 c 70 TP1
## 4 d 60 TP1
## 5 a 95 TP2
## 6 b 74 TP2
## 7 c 89 TP2
## 8 d 85 TP2
Le data frame notes_long
contient exactement les mêmes données que celles dans le data frame notes_large
, mais disposées différemment. Dans notes_long
, il y a deux lignes par individu. Le tableau de mise en forme longue contient plus de lignes, donc est plus long, que le tableau de mise en forme large, d’où les qualificatifs « large » et « longue » pour les mises en forme.
Dans un jeu de données avec une mise en forme longue, les mesures pour plus d’une variable sont combinées dans une même colonne. Cependant, il doit aussi nécessairement y avoir une ou des colonnes servant à identifier à quelle variable est associée chaque valeur (colonne evaluation
dans l’exemple).
Lors d’une analyse statistique de données stockées sous une mise en forme longue, l’analyste doit être vigilant. Il est important d’être conscient que certaines lignes concernent les mêmes individus, donc qu’il s’agit de mesures répétées sur les mêmes individus et non d’observations indépendantes.
Il peut arriver qu’un jeu de données ait une mise en forme mi-large, mi-longue. Voici un exemple pour illustrer cette mise en forme intermédiaire.
Hauteur_Temps1
, Hauteur_Temps2
, Diametre_Temps1
, Diametre_Temps2
Dimension de la sous-matrice contenant les valeurs : 3 x 4
<- data.frame(
plants_large Plant = c("a", "b", "c"),
Hauteur_Temps1 = c(67, 59, 62),
Hauteur_Temps2 = c(69, 65, 66),
Diametre_Temps1 = c(15, 16, 13),
Diametre_Temps2 = c(15, 17, 14),
stringsAsFactors = FALSE
)
plants_large
## Plant Hauteur_Temps1 Hauteur_Temps2 Diametre_Temps1 Diametre_Temps2
## 1 a 67 69 15 15
## 2 b 59 65 16 17
## 3 c 62 66 13 14
Dimension de la sous-matrice contenant les valeurs : 6 x 2
<- data.frame(
plants_inter Plant = rep(c("a", "b", "c"), times = 2),
Temps = rep(1:2, each = 3),
Hauteur = c(67, 59, 62, 69, 65, 66),
Diametre = c(15, 16, 13, 15, 17, 14),
stringsAsFactors = FALSE
)
plants_inter
## Plant Temps Hauteur Diametre
## 1 a 1 67 15
## 2 b 1 59 16
## 3 c 1 62 13
## 4 a 2 69 15
## 5 b 2 65 17
## 6 c 2 66 14
Dimension de la sous-matrice contenant les valeurs : 12 x 1
<- data.frame(
plants_long Plant = rep(c("a", "b", "c"), times = 4),
Temps = rep(rep(1:2, each = 3), times = 2),
Variable = rep(c("Hauteur", "Diametre"), each = 6),
Valeur = c(67, 59, 62, 69, 65, 66, 15, 16, 13, 15, 17, 14),
stringsAsFactors = FALSE
)
plants_long
## Plant Temps Variable Valeur
## 1 a 1 Hauteur 67
## 2 b 1 Hauteur 59
## 3 c 1 Hauteur 62
## 4 a 2 Hauteur 69
## 5 b 2 Hauteur 65
## 6 c 2 Hauteur 66
## 7 a 1 Diametre 15
## 8 b 1 Diametre 16
## 9 c 1 Diametre 13
## 10 a 2 Diametre 15
## 11 b 2 Diametre 17
## 12 c 2 Diametre 14
ou encore
<- data.frame(
plants_long_2 Plant = rep(c("a", "b", "c"), times = 4),
Variable = rep(
c("Hauteur_Temps1", "Hauteur_Temps2", "Diametre_Temps1", "Diametre_Temps2"),
each = 3
),Valeur = c(67, 59, 62, 69, 65, 66, 15, 16, 13, 15, 17, 14),
stringsAsFactors = FALSE
)
plants_long_2
## Plant Variable Valeur
## 1 a Hauteur_Temps1 67
## 2 b Hauteur_Temps1 59
## 3 c Hauteur_Temps1 62
## 4 a Hauteur_Temps2 69
## 5 b Hauteur_Temps2 65
## 6 c Hauteur_Temps2 66
## 7 a Diametre_Temps1 15
## 8 b Diametre_Temps1 16
## 9 c Diametre_Temps1 13
## 10 a Diametre_Temps2 15
## 11 b Diametre_Temps2 17
## 12 c Diametre_Temps2 14
Certaines fonctions R requièrent un type particulier de mise en forme du jeu de données.
Dans l’exemple des notes d’étudiants, nous pourrions vouloir comparer les notes des deux travaux. Étant donné que nous sommes en présence de données prises sur les mêmes individus (qualifiées en statistique de mesures répétées ou encore de données pairées dans le cas particulier de deux variables), un test approprié à réaliser serait un test de comparaison de moyennes pour données pairées. La fonction t.test
réalise ce type de test. Elle accepte les jeux de données de mise en forme large,
t.test(notes_large$TP1, notes_large$TP2, paired = TRUE)
##
## Paired t-test
##
## data: notes_large$TP1 and notes_large$TP2
## t = -1.54, df = 3, p-value = 0.2212
## alternative hypothesis: true difference in means is not equal to 0
## 95 percent confidence interval:
## -32.96547 11.46547
## sample estimates:
## mean of the differences
## -10.75
ou de mise en forme longue (qui permet l’utilisation d’une formule R).
t.test(note ~ evaluation, data = notes_long, paired = TRUE)
##
## Paired t-test
##
## data: note by evaluation
## t = -1.54, df = 3, p-value = 0.2212
## alternative hypothesis: true difference in means is not equal to 0
## 95 percent confidence interval:
## -32.96547 11.46547
## sample estimates:
## mean of the differences
## -10.75
Nous pourrions aussi ajuster un modèle linéaire mixte pour réaliser un test équivalent. La fonction lme
du package nlme
ajuste des modèles linéaires mixtes. Elle accepte cependant seulement des jeux de données de mise en forme longue.
library(nlme)
<- lme(fixed = note ~ evaluation, data = notes_long, random = ~ 1 | etudiant)
mixed summary(mixed)
## Linear mixed-effects model fit by REML
## Data: notes_long
## AIC BIC logLik
## 56.51876 55.6858 -24.25938
##
## Random effects:
## Formula: ~1 | etudiant
## (Intercept) Residual
## StdDev: 5.000001 9.872098
##
## Fixed effects: note ~ evaluation
## Value Std.Error DF t-value p-value
## (Intercept) 75.00 5.533045 3 13.554924 0.0009
## evaluationTP2 10.75 6.980628 3 1.539976 0.2212
## Correlation:
## (Intr)
## evaluationTP2 -0.631
##
## Standardized Within-Group Residuals:
## Min Q1 Med Q3 Max
## -1.2489496 -0.6258934 0.2768885 0.5459932 1.1029740
##
## Number of Observations: 8
## Number of Groups: 4
Nous avons utilisé ici un modèle statistique puissant pour réaliser en fait un test simple. Le test sur le terme evaluationTP2
dans le modèle linéaire mixte revient au test de comparaison de moyennes pour données pairées réalisé avec la fonction t.test
. Nous pouvons constater que le seuil observé des deux tests est le même, soit 0.2212.
Le but de cet exemple était de montrer qu’il est utile en R de savoir convertir un jeu de données d’une mise à forme à l’autre puisque certaines fonctions exigent une mise en forme spécifique.
stack
et unstack
Revenons maintenant au jeu de données data_ex
. Celui-ci est sous une mise en forme large, car il contient une seule ligne par individu. Dans ce jeu de données, un individu est un lancer de dés.
data_ex
## de1 de2 lanceur
## 1 2 1 Luc
## 2 3 4 Luc
## 3 4 2 <NA>
## 4 1 3 Luc
## 5 2 1 Luc
## 6 3 4 Kim
## 7 5 NA Kim
## 8 6 2 Kim
## 9 5 5 Kim
## 10 4 3 Kim
Il est possible de faire passer ce jeu de données à une mise en forme longue avec la fonction stack
comme suit.
<- stack(data_ex)
data_ex_long data_ex_long
## values ind
## 1 2 de1
## 2 3 de1
## 3 4 de1
## 4 1 de1
## 5 2 de1
## 6 3 de1
## 7 5 de1
## 8 6 de1
## 9 5 de1
## 10 4 de1
## 11 1 de2
## 12 4 de2
## 13 2 de2
## 14 3 de2
## 15 1 de2
## 16 4 de2
## 17 <NA> de2
## 18 2 de2
## 19 5 de2
## 20 3 de2
## 21 Luc lanceur
## 22 Luc lanceur
## 23 <NA> lanceur
## 24 Luc lanceur
## 25 Luc lanceur
## 26 Kim lanceur
## 27 Kim lanceur
## 28 Kim lanceur
## 29 Kim lanceur
## 30 Kim lanceur
Cette fonction n’est pas très puissante. Elle n’a pas su traiter la colonne lanceur
et l’a laissé tomber.
Nous pourrions réaliser la transformation inverse avec la fonction unstack
, pour revenir à la mise en forme originale.
<- unstack(data_ex_long)
data_ex_large data_ex_large
## de1 de2 lanceur
## 1 2 1 Luc
## 2 3 4 Luc
## 3 4 2 <NA>
## 4 1 3 Luc
## 5 2 1 Luc
## 6 3 4 Kim
## 7 5 <NA> Kim
## 8 6 2 Kim
## 9 5 5 Kim
## 10 4 3 Kim
Les noms stack
et unstack
viennent du fait que pour passer d’une mise en forme large vers une mise en forme longue, des valeurs doivent être empilées (en anglais stacked) dans une même colonne, alors que pour réaliser la transformation inverse elles doivent être « désempilées » (en anglais unstacked).
Voyons maintenant une autre fonction qui nous permettra de réaliser les mêmes transformations, sans mettre de côté la variable lanceur
.
reshape
Le package stats
, chargé en R par défaut, comprend une fonction nommée reshape
dont l’utilité est de faire passer un jeu de donnée d’une mise en forme large vers une mise en forme longue, et aussi de réaliser la transformation inverse, d’une mise en forme longue vers une mise en forme large. Cette fonction est puissante, mais un peu difficile à utiliser, il faut l’avouer. Il est facile de se perdre dans ses nombreux arguments.
Pour faire passer le data frame data_ex
vers une mise en forme longue avec reshape
, nous pouvons procéder comme suit.
<- reshape(
data_ex_long_2 data = data_ex,
direction = "long",
varying = c("de1", "de2"),
v.names = "resultat"
) data_ex_long_2
## lanceur time resultat id
## 1.1 Luc 1 2 1
## 2.1 Luc 1 3 2
## 3.1 <NA> 1 4 3
## 4.1 Luc 1 1 4
## 5.1 Luc 1 2 5
## 6.1 Kim 1 3 6
## 7.1 Kim 1 5 7
## 8.1 Kim 1 6 8
## 9.1 Kim 1 5 9
## 10.1 Kim 1 4 10
## 1.2 Luc 2 1 1
## 2.2 Luc 2 4 2
## 3.2 <NA> 2 2 3
## 4.2 Luc 2 3 4
## 5.2 Luc 2 1 5
## 6.2 Kim 2 4 6
## 7.2 Kim 2 NA 7
## 8.2 Kim 2 2 8
## 9.2 Kim 2 5 9
## 10.2 Kim 2 3 10
Il faut toujours fournir à la fonction reshape
les arguments :
data
: le data frame dont la mise en forme est à transformer;direction
: la direction de la transformation, "long"
pour une transformation vers une mise en forme longue, "wide"
pour une transformation vers une mise en forme large.Lors d’une transformation vers une mise en forme longue, il faut aussi spécifier l’argument :
varying
: les noms ou les indices des colonnes dans le jeu de données d’origine (data
) sous la mise en forme large contenant les valeurs à combiner dans des colonnes uniques sous la mise en forme longue.La fonction reshape
va alors tenter de deviner le ou les noms à donner à cette ou ces nouvelles colonnes, mais il est préférable de lui spécifier ce ou ces noms avec l’argument :
v.names
: nom(s) de la ou des colonnes dans la mise en forme longue où sont empilées les valeurs provenant de plusieurs colonnes dans la mise en forme large.Par défaut, reshape
a nommé time
la colonne contenant les identifiants des variables dont les valeurs ont été empilées. Aussi, reshape
a utilisé comme identifiants dans cette colonne les entiers allant de 1 jusqu’au nombre de variables spécifiées dans varying
. Il est possible de contrôler cette colonne avec les arguments :
timevar
: nom de la colonne dans la mise en forme longue contenant, pour chaque ligne, l’identifiant de la variable de laquelle la valeur qui se trouve dans la colonne identifiée par v.names
est une mesure (ou de laquelle les valeurs qui se trouvent dans les colonnes identifiées par v.names
sont des mesures).times
: les valeurs d’identifiants à utiliser dans la colonne spécifiée par timevar
.Les noms de ces arguments contiennent le mot time
en référence à un cas classique de données pour lesquelles nous sommes susceptibles de vouloir modifier la mise en forme : des mesures répétées dans le temps.
<- reshape(
data_ex_long_2 data = data_ex,
direction = "long",
varying = c("de1", "de2"),
v.names = "resultat",
timevar = "de",
times = c("de1", "de2")
) data_ex_long_2
## lanceur de resultat id
## 1.de1 Luc de1 2 1
## 2.de1 Luc de1 3 2
## 3.de1 <NA> de1 4 3
## 4.de1 Luc de1 1 4
## 5.de1 Luc de1 2 5
## 6.de1 Kim de1 3 6
## 7.de1 Kim de1 5 7
## 8.de1 Kim de1 6 8
## 9.de1 Kim de1 5 9
## 10.de1 Kim de1 4 10
## 1.de2 Luc de2 1 1
## 2.de2 Luc de2 4 2
## 3.de2 <NA> de2 2 3
## 4.de2 Luc de2 3 4
## 5.de2 Luc de2 1 5
## 6.de2 Kim de2 4 6
## 7.de2 Kim de2 NA 7
## 8.de2 Kim de2 2 8
## 9.de2 Kim de2 5 9
## 10.de2 Kim de2 3 10
Sous une mise en forme longue, reshape
inclut aussi toujours au moins une colonne pour contenir un identifiant des individus. Il nomme par défaut cette colonne id
et utilise comme identifiants les entiers allant de 1 jusqu’au nombre de lignes dans le jeu de données d’origine (data
) sous la mise en forme large. Pour contrôler cette colonne, il faut utiliser les arguments :
idvar
: nom(s) de la ou des colonnes pour identifier les individus;ids
: les valeurs d’identifiants à utiliser dans la ou les colonnes spécifiées par idvar
.<- reshape(
data_ex_long_2 data = data_ex,
direction = "long",
varying = c("de1", "de2"),
v.names = "resultat",
timevar = "de",
times = c("de1", "de2"),
idvar = "IDlancer",
ids = paste0("l", 1:10)
) data_ex_long_2
## lanceur de resultat IDlancer
## l1.de1 Luc de1 2 l1
## l2.de1 Luc de1 3 l2
## l3.de1 <NA> de1 4 l3
## l4.de1 Luc de1 1 l4
## l5.de1 Luc de1 2 l5
## l6.de1 Kim de1 3 l6
## l7.de1 Kim de1 5 l7
## l8.de1 Kim de1 6 l8
## l9.de1 Kim de1 5 l9
## l10.de1 Kim de1 4 l10
## l1.de2 Luc de2 1 l1
## l2.de2 Luc de2 4 l2
## l3.de2 <NA> de2 2 l3
## l4.de2 Luc de2 3 l4
## l5.de2 Luc de2 1 l5
## l6.de2 Kim de2 4 l6
## l7.de2 Kim de2 NA l7
## l8.de2 Kim de2 2 l8
## l9.de2 Kim de2 5 l9
## l10.de2 Kim de2 3 l10
Dans le jeu de données de mise en forme longue obtenu en sortie de reshape
, les données pour les variables (colonnes) ne contenant pas de mesures répétées ont simplement été recopiées autant de fois que nécessaire. Il serait possible de laisser tomber ces variables grâce à l’argument drop
. Il serait aussi possible de contrôler les noms des lignes du data frame obtenu avec l’argument new.row.names
.
Lorsque nous avons obtenu un jeu de données avec la fonction reshape
, il est toujours possible de retourner à la mise en forme d’origine simplement avec la commande
reshape(data_ex_long_2)
## lanceur IDlancer de1 de2
## l1.de1 Luc l1 2 1
## l2.de1 Luc l2 3 4
## l3.de1 <NA> l3 4 2
## l4.de1 Luc l4 1 3
## l5.de1 Luc l5 2 1
## l6.de1 Kim l6 3 4
## l7.de1 Kim l7 5 NA
## l8.de1 Kim l8 6 2
## l9.de1 Kim l9 5 5
## l10.de1 Kim l10 4 3
en raison d’un attribut inséré par reshape
dans sa sortie.
str(data_ex_long_2)
## 'data.frame': 20 obs. of 4 variables:
## $ lanceur : chr "Luc" "Luc" NA "Luc" ...
## $ de : chr "de1" "de1" "de1" "de1" ...
## $ resultat: num 2 3 4 1 2 3 5 6 5 4 ...
## $ IDlancer: chr "l1" "l2" "l3" "l4" ...
## - attr(*, "reshapeLong")=List of 4
## ..$ varying:List of 1
## .. ..$ resultat: chr [1:2] "de1" "de2"
## .. ..- attr(*, "v.names")= chr "resultat"
## .. ..- attr(*, "times")= chr [1:2] "de1" "de2"
## ..$ v.names: chr "resultat"
## ..$ idvar : chr "IDlancer"
## ..$ timevar: chr "de"
Mais supposons que nous n’avons pas créé data_ex_long_2
avec la fonction reshape
et que nous voulons transformer ce jeu de données vers une mise en forme large. Afin de réellement simuler une telle situation, nous allons effacer de data_ex_long_2
son attribut nommé reshapeLong
comme suit.
attr(data_ex_long_2, "reshapeLong") <- NULL
str(data_ex_long_2)
## 'data.frame': 20 obs. of 4 variables:
## $ lanceur : chr "Luc" "Luc" NA "Luc" ...
## $ de : chr "de1" "de1" "de1" "de1" ...
## $ resultat: num 2 3 4 1 2 3 5 6 5 4 ...
## $ IDlancer: chr "l1" "l2" "l3" "l4" ...
Nous pourrions procéder ainsi pour effectuer la transformation de mise en forme.
<- reshape(
data_ex_large2 data = data_ex_long_2,
direction = "wide",
timevar = "de",
idvar = "IDlancer"
) data_ex_large2
## IDlancer lanceur.de1 resultat.de1 lanceur.de2 resultat.de2
## l1.de1 l1 Luc 2 Luc 1
## l2.de1 l2 Luc 3 Luc 4
## l3.de1 l3 <NA> 4 <NA> 2
## l4.de1 l4 Luc 1 Luc 3
## l5.de1 l5 Luc 2 Luc 1
## l6.de1 l6 Kim 3 Kim 4
## l7.de1 l7 Kim 5 Kim NA
## l8.de1 l8 Kim 6 Kim 2
## l9.de1 l9 Kim 5 Kim 5
## l10.de1 l10 Kim 4 Kim 3
Les seuls arguments obligatoires pour réaliser cette transformation vers une mise en forme large sont data
, direction
, timevar
et idvar
, dont les descriptions ont été fournies ci-dessus. Cependant, reshape
a par défaut considéré que toutes les variables autres que celles identifiées avec timevar
et idvar
contiennent des mesures répétées. Nous nous retrouvons donc maintenant avec deux colonnes identiques contenant le nom du lanceur. Pour spécifier à reshape
la ou les colonnes pour lesquelles les valeurs doivent être réparties en plusieurs colonnes, il faut aussi spécifier l’argument v.names
.
<- reshape(
data_ex_large2
data_ex_long_2,direction = "wide",
timevar = "de",
idvar = "IDlancer",
v.names = "resultat"
) data_ex_large2
## lanceur IDlancer resultat.de1 resultat.de2
## l1.de1 Luc l1 2 1
## l2.de1 Luc l2 3 4
## l3.de1 <NA> l3 4 2
## l4.de1 Luc l4 1 3
## l5.de1 Luc l5 2 1
## l6.de1 Kim l6 3 4
## l7.de1 Kim l7 5 NA
## l8.de1 Kim l8 6 2
## l9.de1 Kim l9 5 5
## l10.de1 Kim l10 4 3
Et voilà!
Afin de reconstruire un data frame vraiment identique à data_ex
,
data_ex
## de1 de2 lanceur
## 1 2 1 Luc
## 2 3 4 Luc
## 3 4 2 <NA>
## 4 1 3 Luc
## 5 2 1 Luc
## 6 3 4 Kim
## 7 5 NA Kim
## 8 6 2 Kim
## 9 5 5 Kim
## 10 4 3 Kim
il suffirait de retirer la colonne IDlancer
, renommer les colonnes contenant les résultats des lancers de dés, réordonner les colonnes et renommer les lignes.
$IDlancer <- NULL # retirer une colonne
data_ex_large2colnames(data_ex_large2)[2:3] <- c("de1", "de2") # renommer des colonnes
<- data_ex_large2[, c("de1", "de2", "lanceur")] # réordonner les colonnes
data_ex_large2 rownames(data_ex_large2) <- 1:10 # renommer les lignes
data_ex_large2
## de1 de2 lanceur
## 1 2 1 Luc
## 2 3 4 Luc
## 3 4 2 <NA>
## 4 1 3 Luc
## 5 2 1 Luc
## 6 3 4 Kim
## 7 5 NA Kim
## 8 6 2 Kim
## 9 5 5 Kim
## 10 4 3 Kim
reshape
En résumé, les arguments de reshape
sont les suivants :
data
: data frame à remettre en forme;direction
: direction de la remise en forme ("wide"
ou "long"
).idvar
: nom(s) colonne(s) contenant les identifiants des individus;ids
: identifiants des individus.timevar
: nom colonne contenant les identifiants des variables;times
: identifiants des variables.v.names
: nom(s) colonne(s) contenant les valeurs empilées dans le mise en forme longue ou intermédiaire;varying
: noms colonnes contenant les valeurs dans le mise en forme large ou intermédiaire.pivot_longer
et pivot_wider
du package tidyr
Le package tidyr
offre les fonctions suivantes, permettant d’effectuer des transformations de mise en forme :
pivot_longer
,pivot_wider
.Ces fonctions remplacent les fonctions gather
et spread
depuis septembre 2019. Illustrons l’utilisation des fonctions pivot_longer
et pivot_wider
.
Voici une commande pour transformer le data frame data_ex
de sa mise en forme d’origine, large, vers une mise en forme longue avec la fonction pivot_longer
.
<- pivot_longer(
data_ex_long_3 data = data_ex,
cols = c(de1, de2),
names_to = "de",
values_to = "resultat"
) data_ex_long_3
## # A tibble: 20 x 3
## lanceur de resultat
## <chr> <chr> <dbl>
## 1 Luc de1 2
## 2 Luc de2 1
## 3 Luc de1 3
## 4 Luc de2 4
## 5 <NA> de1 4
## 6 <NA> de2 2
## 7 Luc de1 1
## 8 Luc de2 3
## 9 Luc de1 2
## 10 Luc de2 1
## 11 Kim de1 3
## 12 Kim de2 4
## 13 Kim de1 5
## 14 Kim de2 NA
## 15 Kim de1 6
## 16 Kim de2 2
## 17 Kim de1 5
## 18 Kim de2 5
## 19 Kim de1 4
## 20 Kim de2 3
Il faut fournir à pivot_longer
les arguments :
data
: le data frame (ou tibble) dont la mise en forme est à transformer;cols
: les noms des colonnes dans le jeu de données d’origine (data
) sous la mise en forme large contenant les données à empiler dans une seule colonne sous la mise en forme longue.Pour rendre le résultat plus facilement interprétable, il est aussi recommandé de fournir une valeur aux arguments suivants :
names_to
: le nom de la colonne dans la mise en forme longue servant à identifier les variables dont les valeurs dans la nouvelle colonne de valeurs empilées sont des mesures;values_to
: le nom de la colonne dans la mise en forme longue où seront empilées les valeurs provenant de plusieurs colonnes sous la mise en forme large.Remarquons que la fonction pivot_longer
retourne un tibble et n’empile pas les valeurs dans le même ordre que reshape
. De plus, pivot_longer
n’ajoute pas une colonne pour identifier les individus comme le fait la fonction reshape
. Il aurait fallu que le jeu de données d’origine contienne déjà une colonne avec ces identifiants. Dans data_ex
, ce sont plutôt les noms de lignes qui identifient en quelque sorte les individus.
Afin d’effectuer une transformation d’une mise en forme longue vers une mise en forme large avec la fonction pivot_wider
de tidyr
, le jeu de données fourni en entrée à la fonction doit contenir une colonne pour identifier les individus. Voici donc un exemple d’utilisation de la fonction pivot_wider
, réalisé en utilisant comme jeu de données d’origine data_ex_long_2
.
<- pivot_wider(
data_ex_large_3 data = data_ex_long_2,
names_from = de,
values_from = resultat
) data_ex_large_3
## # A tibble: 10 x 4
## lanceur IDlancer de1 de2
## <chr> <chr> <dbl> <dbl>
## 1 Luc l1 2 1
## 2 Luc l2 3 4
## 3 <NA> l3 4 2
## 4 Luc l4 1 3
## 5 Luc l5 2 1
## 6 Kim l6 3 4
## 7 Kim l7 5 NA
## 8 Kim l8 6 2
## 9 Kim l9 5 5
## 10 Kim l10 4 3
La fonction a réussi à réaliser la tâche souhaitée en fournissant seulement des valeurs aux arguments data
, names_from
et values_from
, définis comme suit :
data
: le data frame (ou tibble) dont la mise en forme est à transformer;names_from
: le nom de la colonne dans la mise en forme longue contenant les identifiants des variables desquelles les valeurs empilées sont des mesures;values_from
: le nom de la ou des colonnes dans la mise en forme longue où sont empilées les valeurs qui doivent maintenant être réparties dans plusieurs colonnes.Les références proposent de bonnes sources d’information pour en apprendre davantage à propos des fonctions pivot_longer
et pivot_wider
.
melt
et dcast
du package data.table
Le package data.table
contient les fonctions suivantes, permettant d’effectuer des transformations de mise en forme :
Ces fonctions sont en fait des extensions aux data tables des fonctions portant les mêmes noms dans le package reshape2
, qui n’est maintenant plus mis à jour. Illustrons l’utilisation des fonctions melt
et dcast
.
Voici une commande pour transformer le data table data_ex_dt
de sa mise en forme d’origine, large, vers une mise en forme longue avec la fonction melt
du package data.table
.
<- melt(
data_ex_long_4 data = data_ex_dt,
measure.vars = c("de1", "de2"),
variable.name = "de",
value.name = "resultat"
) data_ex_long_4
## lanceur de resultat
## 1: Kim de1 6
## 2: Kim de1 5
## 3: Kim de1 5
## 4: <NA> de1 4
## 5: Kim de1 4
## 6: Kim de1 3
## 7: Luc de1 3
## 8: Luc de1 2
## 9: Luc de1 2
## 10: Luc de1 1
## 11: Kim de2 2
## 12: Kim de2 NA
## 13: Kim de2 5
## 14: <NA> de2 2
## 15: Kim de2 3
## 16: Kim de2 4
## 17: Luc de2 4
## 18: Luc de2 1
## 19: Luc de2 1
## 20: Luc de2 3
Les arguments de melt
portent des noms différents de ceux de pivot_longer
(mis à part data
), mais ils peuvent être définis de façon similaire :
data
: le data table dont la mise en forme est à transformer;measure.vars
: les noms des colonnes dans le jeu de données d’origine (data
) sous la mise en forme large contenant les données à empiler dans une seule colonne sous la mise en forme longue;variable.name
: le nom de la colonne dans la mise en forme longue servant à identifier les variables dont les valeurs dans la nouvelle colonne de valeurs empilées sont des valeurs;value.name
: le nom de la colonne dans la mise en forme longue où seront empilées les valeurs provenant de plusieurs colonnes sous la mise en forme large.Comme pour la fonction pivot_wider
, la fonction dcast
a besoin que le jeu de données à transformer contienne une colonne pour identifier les individus. Voici donc un exemple d’utilisation de la fonction dcast
, réalisé en utilisant comme jeu de données d’origine le data frame data_ex_long_2
converti en data table.
<- as.data.table(data_ex_long_2)
data_ex_long_2_dt <- dcast(
data_ex_large_4 data = data_ex_long_2_dt,
formula = lanceur + IDlancer ~ de,
value.var = "resultat"
)
data_ex_large_4
## lanceur IDlancer de1 de2
## 1: <NA> l3 4 2
## 2: Kim l10 4 3
## 3: Kim l6 3 4
## 4: Kim l7 5 NA
## 5: Kim l8 6 2
## 6: Kim l9 5 5
## 7: Luc l1 2 1
## 8: Luc l2 3 4
## 9: Luc l4 1 3
## 10: Luc l5 2 1
Les arguments de dcast
sont différents des arguments de toutes les fonctions de modification de mise en forme vues jusqu’à maintenant.
data
: le data table dont la mise en forme est à transformer;formula
: formule de la forme LHS
~ RHS
, où
LHS
= noms des colonnes identifiant les individus ou à conserver telles quelles dans la sortie (séparées par +
s’il y en a plus d’une),RHS
= nom de la colonne identifiant les variables desquelles les valeurs empilées sont des mesures;value.var
= le nom de la colonne dans la mise en forme longue où sont empilées les valeurs qui doivent maintenant être réparties dans plusieurs colonnes.La fonction dcast
va plus loin que la modification de mise en forme de jeux de données. Elle peut aussi agréger les données référant à un même individu en appliquant la fonction fournie à son argument fun.aggregate
. Pour plus d’informations, voir les références.
base
et stats
)transform
,cbind
ou data.frame
;ifelse
;cut
, ave
;scale
;paste
: concatène des chaînes de caractères,nchar
: calcule le nombre de caractères,toupper
: transforme toutes les lettres en majuscules,tolower
: transforme toutes les lettres en minuscules,strsplit
: brise des chaînes de caractères, en coupant lors de la rencontre d’une certaine sous-chaîne de caractères,substr
: extrait les caractères entre deux positions,sub
ou gsub
: remplacent la première ou toutes les occurrences d’une certaine sous-chaîne de caractères par une autre,grep
et grepl
: testent la présence d’une certaine sous-chaîne de caractères,chartr
: remplace des caractères par d’autres,iconv
: convertit l’encodage de chaîne de caractères;as.Date
: convertit une chaîne de caractère en format date,Sys.setlocale
: permet de modifier les paramètres régionaux d’une session R (utile lorsque nous voulons convertir en format Date
des chaînes de caractères contenant des noms de mois dans une langue autre que celle du système d’exploitation de notre ordinateur),difftime
: calcule l’intervalle de temps entre deux dates,Sys.Date
: retourne la date courante,format
: permet de modifier le format d’une date.duplicated
,unique
.complete.cases
,na.omit
.[
, fonction subset
.rbind
.cbind
, data.frame
.merge
by
, ou arguments by.x
et by.y
, pour spécifier les variables d’association (celles à partir desquelles établir les correspondances entre les lignes);all
, ou arguments all.x
et all.y
, pour spécifier les lignes à conserver (donc le type de jointure à réaliser).rev
: renverse l’ordre des éléments;sort
: ordonne les éléments d’un vecteur atomique
decreasing = TRUE
pour ordre décroissant;[
: permet de réordonner les lignes ou les colonnes d’un objet à deux dimensions dans un ordre quelconque;order
: retourne la permutation des données requise pour ordonner les données selon l’ordre spécifié \(\rightarrow\) pour effectuer l’ordonnancement, il faut fournir le résultat retourné par order
en argument à l’opérateur [
decreasing = TRUE
pour ordre décroissant;t
: transpose des matricesstack
et unstack
: modifient la mise en forme, fonctions simplistesreshape
: modifie la mise en forme; les arguments de reshape
sont les suivants
data
: data frame à remettre en forme;direction
: direction de la remise en forme ("wide"
ou "long"
).idvar
: nom(s) colonne(s) contenant les identifiants des individus;ids
: identifiants des individus.timevar
: nom colonne contenant les identifiants des variables;times
: identifiants des variables.v.names
: nom(s) colonne(s) contenant les valeurs empilées dans la mise en forme longue ou intermédiaire;varying
: noms colonnes contenant les valeurs dans la mise en forme large ou intermédiaire.tidyverse
(principalement dplyr
et tidyr
)mutate
du package dplyr
.if_else
du package dplyr
.stringr
.lubridate
.filter
du package dplyr
;select
du package dplyr
;drop_na
du package tidyr
.bind_rows
;bind_cols
du package dplyr
;inner_join
, full_join
, left_join
, right_join
, etc., du package dplyr
.arrange
du package dplyr
,
desc
du package dplyr
pour obtenir un ordre décroissant.pivot_longer
du package tidyr
;pivot_wider
du package tidyr
.data.table
:=
(modification par référence).fifelse
.[
, méthode subset
;na.omit
.rbindlist
;merge
.setorder
;setcolorder
.melt
;dcast
.Transformation | Package utils |
Package stats |
Package tidyr |
Package data.table |
---|---|---|---|---|
large vers long | stack |
reshape (direction = "long" ) |
pivot_longer |
melt |
long vers large | unstack |
reshape (direction = "wide" ) |
pivot_wider |
dcast |
Sites web abordant le prétraitement de données en R :
Package dplyr
:
Package tidyr
:
Package data.table
:
[
du package data.tablePackage stringr
:
Expressions régulières :
help(regexp)
Autres références en manipulation de chaînes de caractères en R :
help(strptime)
,Package lubridate
:
sqldf
: https://CRAN.R-project.org/package=sqldfVoici quelques exemples supplémentaires de changements de mise en forme effectués avec la fonction reshape
ou avec le package tidyr
.
Données utilisées :
plants_large
## Plant Hauteur_Temps1 Hauteur_Temps2 Diametre_Temps1 Diametre_Temps2
## 1 a 67 69 15 15
## 2 b 59 65 16 17
## 3 c 62 66 13 14
# Avec reshape :
reshape(
data = plants_large,
direction = "long",
idvar = "Plant",
timevar = "Temps",
times = 1:2,
v.names = c("Hauteur", "Diametre"),
varying = c("Diametre_Temps1", "Hauteur_Temps1", "Diametre_Temps2", "Hauteur_Temps2")
)
## Plant Temps Hauteur Diametre
## a.1 a 1 67 15
## b.1 b 1 59 16
## c.1 c 1 62 13
## a.2 a 2 69 15
## b.2 b 2 65 17
## c.2 c 2 66 14
# Avec pivot_longer :
# Il faut réaliser la transformation en deux temps,
# parce que l'argument values_to accepte seulement un nom.
<- pivot_longer(
hauteur_long data = plants_large,
cols = c(Hauteur_Temps1, Hauteur_Temps2),
names_to = "Temps",
names_prefix = "Hauteur_Temps",
values_to = "Hauteur"
)<- pivot_longer(
diametre_long data = plants_large,
cols = c(Diametre_Temps1, Diametre_Temps2),
names_to = "Temps",
names_prefix = "Diametre_Temps",
values_to = "Diametre",
)# Il ne reste qu'à fusionner les deux `data frames tibbles
# et à éliminer des variables.
full_join(
x = select(hauteur_long, Plant, Temps, Hauteur),
y = select(diametre_long, Plant, Temps, Diametre)
)
## Joining, by = c("Plant", "Temps")
## # A tibble: 6 x 4
## Plant Temps Hauteur Diametre
## <chr> <chr> <dbl> <dbl>
## 1 a 1 67 15
## 2 a 2 69 15
## 3 b 1 59 16
## 4 b 2 65 17
## 5 c 1 62 13
## 6 c 2 66 14
# Avec reshape :
reshape(
data = plants_large,
direction = "long",
idvar = "Plant",
timevar = "Variable",
times = c("Hauteur_Temps1", "Hauteur_Temps2", "Diametre_Temps1", "Diametre_Temps2"),
v.names = "Valeur",
varying = c("Hauteur_Temps1", "Hauteur_Temps2", "Diametre_Temps1", "Diametre_Temps2")
)
## Plant Variable Valeur
## a.Hauteur_Temps1 a Hauteur_Temps1 67
## b.Hauteur_Temps1 b Hauteur_Temps1 59
## c.Hauteur_Temps1 c Hauteur_Temps1 62
## a.Hauteur_Temps2 a Hauteur_Temps2 69
## b.Hauteur_Temps2 b Hauteur_Temps2 65
## c.Hauteur_Temps2 c Hauteur_Temps2 66
## a.Diametre_Temps1 a Diametre_Temps1 15
## b.Diametre_Temps1 b Diametre_Temps1 16
## c.Diametre_Temps1 c Diametre_Temps1 13
## a.Diametre_Temps2 a Diametre_Temps2 15
## b.Diametre_Temps2 b Diametre_Temps2 17
## c.Diametre_Temps2 c Diametre_Temps2 14
# Avec pivot_longer :
pivot_longer(
data = plants_large,
cols = c(Hauteur_Temps1, Hauteur_Temps2, Diametre_Temps1, Diametre_Temps2),
names_to = "Variable",
values_to = "Valeur"
)
## # A tibble: 12 x 3
## Plant Variable Valeur
## <chr> <chr> <dbl>
## 1 a Hauteur_Temps1 67
## 2 a Hauteur_Temps2 69
## 3 a Diametre_Temps1 15
## 4 a Diametre_Temps2 15
## 5 b Hauteur_Temps1 59
## 6 b Hauteur_Temps2 65
## 7 b Diametre_Temps1 16
## 8 b Diametre_Temps2 17
## 9 c Hauteur_Temps1 62
## 10 c Hauteur_Temps2 66
## 11 c Diametre_Temps1 13
## 12 c Diametre_Temps2 14
# Avec reshape :
reshape(
data = plants_inter,
direction = "long",
idvar = c("Plant", "Temps"), # n'accepte pas "Plant" seul, car la colonne
timevar = "Variable", # contient des combinaisons répétées
times = c("Hauteur", "Diametre"),
v.names = "Valeur",
varying = c("Hauteur", "Diametre")
)
## Plant Temps Variable Valeur
## a.1.Hauteur a 1 Hauteur 67
## b.1.Hauteur b 1 Hauteur 59
## c.1.Hauteur c 1 Hauteur 62
## a.2.Hauteur a 2 Hauteur 69
## b.2.Hauteur b 2 Hauteur 65
## c.2.Hauteur c 2 Hauteur 66
## a.1.Diametre a 1 Diametre 15
## b.1.Diametre b 1 Diametre 16
## c.1.Diametre c 1 Diametre 13
## a.2.Diametre a 2 Diametre 15
## b.2.Diametre b 2 Diametre 17
## c.2.Diametre c 2 Diametre 14
# Avec pivot_longer :
pivot_longer(
data = plants_inter,
cols = c(Hauteur, Diametre),
names_to = "Variable",
values_to = "Valeur"
)
## # A tibble: 12 x 4
## Plant Temps Variable Valeur
## <chr> <int> <chr> <dbl>
## 1 a 1 Hauteur 67
## 2 a 1 Diametre 15
## 3 b 1 Hauteur 59
## 4 b 1 Diametre 16
## 5 c 1 Hauteur 62
## 6 c 1 Diametre 13
## 7 a 2 Hauteur 69
## 8 a 2 Diametre 15
## 9 b 2 Hauteur 65
## 10 b 2 Diametre 17
## 11 c 2 Hauteur 66
## 12 c 2 Diametre 14
Il faut partir de plants_long
.
# Avec reshape :
reshape(
data = plants_long,
direction = "wide",
idvar = c("Plant", "Temps"),
timevar = "Variable",
v.names = "Valeur"
)
## Plant Temps Valeur.Hauteur Valeur.Diametre
## 1 a 1 67 15
## 2 b 1 59 16
## 3 c 1 62 13
## 4 a 2 69 15
## 5 b 2 65 17
## 6 c 2 66 14
# Avec pivot_wider :
pivot_wider(
data = plants_long,
names_from = Variable,
values_from = Valeur
)
## # A tibble: 6 x 4
## Plant Temps Hauteur Diametre
## <chr> <int> <dbl> <dbl>
## 1 a 1 67 15
## 2 b 1 59 16
## 3 c 1 62 13
## 4 a 2 69 15
## 5 b 2 65 17
## 6 c 2 66 14
Il faut partir de plants_long_2
.
# Avec reshape :
reshape(
data = plants_long_2,
direction = "wide",
idvar = "Plant",
timevar = "Variable",
v.names = "Valeur"
)
## Plant Valeur.Hauteur_Temps1 Valeur.Hauteur_Temps2 Valeur.Diametre_Temps1
## 1 a 67 69 15
## 2 b 59 65 16
## 3 c 62 66 13
## Valeur.Diametre_Temps2
## 1 15
## 2 17
## 3 14
# Avec pivot_wider :
pivot_wider(
data = plants_long_2,
names_from = Variable,
values_from = Valeur
)
## # A tibble: 3 x 5
## Plant Hauteur_Temps1 Hauteur_Temps2 Diametre_Temps1 Diametre_Temps2
## <chr> <dbl> <dbl> <dbl> <dbl>
## 1 a 67 69 15 15
## 2 b 59 65 16 17
## 3 c 62 66 13 14
# Avec reshape :
reshape(
data = plants_inter,
direction = "wide",
idvar = "Plant",
timevar = "Temps",
v.names = c("Hauteur", "Diametre")
)
## Plant Hauteur.1 Diametre.1 Hauteur.2 Diametre.2
## 1 a 67 15 69 15
## 2 b 59 16 65 17
## 3 c 62 13 66 14
# Avec pivot_wider :
pivot_wider(
data = plants_inter,
names_from = Temps,
values_from = c(Hauteur, Diametre)
)
## # A tibble: 3 x 5
## Plant Hauteur_1 Hauteur_2 Diametre_1 Diametre_2
## <chr> <dbl> <dbl> <dbl> <dbl>
## 1 a 67 69 15 15
## 2 b 59 65 16 17
## 3 c 62 66 13 14