Manipulations avancées avec data.table
L’extension data.table
permets d’étendre les tableaux de données. Elle modifie radicalement la syntaxe des crochets, permettant un code plus court et surtout plus puissant. Par ailleurs, elle est particulièrement rapide pour opérer des opérations sur les données et permets d’effectuer des opérations par assignation directe sans avoir à copier les objets en mémoire. Autrement dit, elle est particulièrement utile lorsque l’on travaille sur des gros fichiers de données.
Certes, l’apprentissage de cette nouvelle syntaxe peut faire peur au début, mais c’est un gain tellement notable une fois qu’on la maîtrise, qu’il est difficile de revenir en arrière.
Pour un tutoriel (en anglais et en ligne) écrit par les développeurs de data.table
, voir https://www.datacamp.com/courses/data-table-data-manipulation-r-tutorial. On pourra aussi se référer à la vignette officielle https://cran.r-project.org/web/packages/data.table/vignettes/datatable-intro.html.
Convertir un data.frame en data.table
Il suffit d’avoir recours à la fonction as.data.table
.
[1] "data.table" "data.frame"
Comme on le voit, cela ajoute plusieurs classes additionnelles au tableau de données, celui-ci restant malgré tout toujours un data.frame. Cependant, la syntaxe des crochets simples []
change radicalement, tandis que les crochets doubles [[]]
restent inchangés. Par contre, comme il s’agit toujours d’un tableau de données classique, on pourra l’utiliser avec les fonctions des autres extensions de R. Si jamais vous rencontriez un problème, il est toujours possible de reconvertir en tableau de données classique avec setDF
(voir ci-dessous).
setDT et setDF
Lors de l’utilisation de as.data.table
, le tableau de données original a d’abord été copié en mémoire, converti puis il a fallu le sauvegarder dans un objet avec <-
. Lorsqu’on l’on manipule de gros tableaux, cela est gourmand en ressources système et prend du temps.
C’est pour cela que data.table
fournie plusieurs fonctions (commençant parle préfixe set
) qui modifient directement l’objet sélectionné en mémoire, ce qu’on appelle modification par assignation
. Ce type de fonction est beaucoup plus rapide et efficace en termes de ressources système. On notera également qu’il est inutile de stocker le résultats dans un objet puisque l’objet a été modifié directement en mémoire.
setDT
converti un tableaux de données en data.table tandis que setDF
fait l’opération opposée.
[1] "data.table" "data.frame"
[1] "data.frame"
dplyr et data.table
Pour ceux travaillant également avec les extension dplyr
et tibble
, il est possible de concilier tibble et data.table avec l’extension dtplyr
et sa fonction tbl_dt
.
[1] "tbl_dt" "tbl" "data.table" "data.frame"
Le tableau de données est à la fois compatible avec data.table
(et notamment sa syntaxe particulière des crochets) et les verbes de dplyr
.
La syntaxe des crochets
La syntaxe des crochets change radicalement avec data.table
. Elle est de la forme objet[i, j, by]
(dans sa forme la plus simple, pour une présentation exhaustive, voir le fichier d’aide de data.table-package
).
Sélectionner des observations
Cela se fait en indiquant une indiquant une condition au premier argument, à savoir i
. Si l’on ne procède à une sélection en même temps sur les variables, il n’est pas nécessaire d’indiquer de virgule ,
dans les crochets.
On notera que les noms indiquer entre les crochets sont évalués en fonction du contexte, en l’occurence la liste des variables de l’objet considéré. Ainsi, les noms des variables peuvent être indiqués tels quels, sans utilisation du symbole $
ni des guillemets.
Une différence de taille : lorsqu’il y a des observations pour lesquelles la condition indiquée en i
renvoie NA
, elles ne sont pas sélectionnées par data.table
tandis que, pour un data.frame classique cela renvoie des lignes manquantes.
Sélectionner des variables
Pour sélectionner une variable, il suffit d’indiquer son nom dans la seconde partie, à savoir j
. Noter la virgule qui permets d’indiquer que c’est une condition sur j
et non sur i
.
[1] 5.1 4.9 4.7 4.6 5.0 5.4 4.6 5.0 4.4 4.9 5.4 4.8 4.8
[14] 4.3 5.8 5.7 5.4 5.1 5.7 5.1 5.4 5.1 4.6 5.1 4.8 5.0
[27] 5.0 5.2 5.2 4.7 4.8 5.4 5.2 5.5 4.9 5.0 5.5 4.9 4.4
[40] 5.1 5.0 4.5 4.4 5.0 5.1 4.8 5.1 4.6 5.3 5.0 7.0 6.4
[53] 6.9 5.5 6.5 5.7 6.3 4.9 6.6 5.2 5.0 5.9 6.0 6.1 5.6
[66] 6.7 5.6 5.8 6.2 5.6 5.9 6.1 6.3 6.1 6.4 6.6 6.8 6.7
[79] 6.0 5.7 5.5 5.5 5.8 6.0 5.4 6.0 6.7 6.3 5.6 5.5 5.5
[92] 6.1 5.8 5.0 5.6 5.7 5.7 6.2 5.1 5.7 6.3 5.8 7.1 6.3
[105] 6.5 7.6 4.9 7.3 6.7 7.2 6.5 6.4 6.8 5.7 5.8 6.4 6.5
[118] 7.7 7.7 6.0 6.9 5.6 7.7 6.3 6.7 7.2 6.2 6.1 6.4 7.2
[131] 7.4 7.9 6.4 6.3 6.1 7.7 6.3 6.4 6.0 6.9 6.7 6.9 5.8
[144] 6.8 6.7 6.7 6.3 6.5 6.2 5.9
Pour sélectionner plusieurs variables, on fournira une liste définie avec list
(et non un vecteur défini avec c
).
data.table
fourni un raccourci pour écrire une liste : .()
. A l’intérieur des crochets (mais pas en dehors), .()
sera compris comme list()
.
Il est possible de renommer une variable à la volée et même d’en calculer d’autres.
Seul le retour est ici affecté. Cela n’impacte pas le tableau d’origine. Nous verrons plus loin comment créer / modifier une variable.
Attention : on ne peut pas directement sélectionner une variable par sa position ou en indiquant une chaîne de caractères. En effet, une valeur numérique ou textuelle est comprise comme une constante.
Grouper les résultats
Si en j
on utilise des fonctions qui à partir d’un vecteur renvoient une valeur unique (telles que mean
, median
, min
, max
, first
, last
, nth
, etc.), on peut ainsi obtenir un résumé. On pourra également utiliser .N
pour obtenir le nombre d’observations.
iris2[, .(min_sepal_width = min(Sepal.Width), max_sepal_width = max(Sepal.Width),
n_observations = .N)]
Cela devient particulièrement intéressant en calculant ces mêmes valeurs par sous-groupe, grace au troisième paramètre : by
.
iris2[, .(min_sepal_width = min(Sepal.Width), max_sepal_width = max(Sepal.Width),
n_observations = .N), by = Species]
Ajouter / Modifier / Supprimer une variable
data.table
introduit un nouvel opérateur :=
permettant de modifier une variable par assignation directe. Cela signifie que la modification a lieu directement en mémoire dans le tableau de données, sans qu’il soit besoin réaffecter le résultat avec <-
.
On peut également combiner :=
avec une sélection sur les observations en i
pour ne modifier que certaines observations. De même, le recours à by
permets des calculs par groupe.
iris2[, group := "A"]
iris2[Species == "virginica", group := "B"]
iris2[, n_obs_per_species := .N, by = Species]
Enchaîner les opérations
Il est possible d’enchaîner les opérations avec une succession de crochets.
iris2[, .(petal_area = Petal.Width * Petal.Length, Species)][,
.(min_petal_area = min(petal_area)), by = Species]