R Language
data.table
Recherche…
Introduction
Data.table est un package qui étend les fonctionnalités des trames de données à partir de la base R, améliorant en particulier leurs performances et leur syntaxe. Reportez-vous à la section Docs du package à la rubrique Mise en route avec data.table pour plus de détails.
Syntaxe
-
DT[i, j, by]
# DT [où, sélectionnez | mettre à jour | faire, par] -
DT[...][...]
# chaînage -
################# Shortcuts, special functions and special symbols inside DT[...]
- . ()
# dans plusieurs arguments, remplace la liste () - J ()
# en i, remplace la liste () - : =
# en j, une fonction utilisée pour ajouter ou modifier des colonnes - .N
# dans i, le nombre total de lignes
# en j, le nombre de lignes d'un groupe - .JE
# en j, le vecteur des numéros de ligne dans le tableau (filtré par i) - .DAKOTA DU SUD
# en j, le sous-ensemble actuel des données
# sélectionné par l'argument .SDcols - .GRP
# en j, l'index actuel du sous-ensemble des données - .PAR
# en j, la liste des valeurs pour le sous-ensemble de données en cours - V1, V2, ...
# noms par défaut pour les colonnes sans nom créées en j -
################# Joins inside DT[...]
- DT1 [DT2, on, j]
# joindre deux tables - je.*
# préfixe spécial sur les colonnes de DT2 après la jointure - par = .EACHI
# option spéciale disponible uniquement avec une jointure - DT1 [! DT2, on, j]
# anti-joint deux tables - DT1 [DT2, on, roll, j]
# joindre deux tables, en roulant sur la dernière colonne de on = -
################# Reshaping, stacking and splitting
- faire fondre (DT, id.vars, measure.vars)
# transformer en format long
# pour plusieurs colonnes, utilisez measure.vars = patterns (...) - dcast (DT, formule)
# transformer en format large - rbind (DT1, DT2, ...)
# piles énumérées data.tables - rbindlist (DT_list, idcol)
# empile une liste de data.tables - split (DT, par)
# diviser un data.table en une liste -
################# Some other functions specialized for data.tables
- foverlaps
# chevauchement des jointures - fusionner
# une autre façon de joindre deux tables - ensemble
# autre moyen d'ajouter ou de modifier des colonnes - fintersect, fsetdiff, funion, fsetequal, unique, dupliqué, anyDuplicated
# Opérations de théorie des ensembles avec des lignes en tant qu'éléments - uniqueN
# le nombre de lignes distinctes - rowidv (DT, cols)
# ID de ligne (1 à .N) dans chaque groupe déterminé par les cols - rleidv (DT, cols)
# ID du groupe (1 à .GRP) dans chaque groupe déterminé par des exécutions de cols - shift (DT, n, type = c ("lag", "lead"))
# appliquer un opérateur de décalage à chaque colonne - setorder, setcolorder, setnames, setkey, setindex, setattr
# modifier les attributs et ordonner par référence
Remarques
Installation et support
Pour installer le package data.table:
# install from CRAN
install.packages("data.table")
# or install development version
install.packages("data.table", type = "source", repos = "http://Rdatatable.github.io/data.table")
# and to revert from devel to CRAN, the current version must first be removed
remove.packages("data.table")
install.packages("data.table")
Le site officiel du paquet contient des pages wiki pour vous aider à démarrer et des listes de présentations et d'articles du Web. Avant de poser une question - ici sur StackOverflow ou ailleurs - veuillez lire la page de support .
Chargement du paquet
De nombreuses fonctions dans les exemples ci-dessus existent dans l'espace de noms data.table. Pour les utiliser, vous devrez d'abord ajouter une ligne comme la library(data.table)
ou utiliser leur chemin complet, comme data.table::fread
au lieu de simplement fread
. Pour obtenir de l'aide sur des fonctions individuelles, la syntaxe est help("fread")
ou ?fread
. Encore une fois, si le paquet n'est pas chargé, utilisez le nom complet comme ?data.table::fread
.
Créer un data.table
Un data.table est une version améliorée de la classe data.frame de la base R. En tant que tel, son attribut class()
est le vecteur "data.table" "data.frame"
et les fonctions qui fonctionnent sur un data.frame seront également travailler avec un data.table. Il existe de nombreuses façons de créer, charger ou contraindre un fichier data.table.
Construire
N'oubliez pas d'installer et d'activer le package data.table
library(data.table)
Il y a un constructeur du même nom:
DT <- data.table(
x = letters[1:5],
y = 1:5,
z = (1:5) > 3
)
# x y z
# 1: a 1 FALSE
# 2: b 2 FALSE
# 3: c 3 FALSE
# 4: d 4 TRUE
# 5: e 5 TRUE
Contrairement à data.frame
, data.table
ne contraindra pas les chaînes aux facteurs:
sapply(DT, class)
# x y z
# "character" "integer" "logical"
Lire dans
Nous pouvons lire un fichier texte:
dt <- fread("my_file.csv")
Contrairement à read.csv
, fread
lira les chaînes comme des chaînes et non comme des facteurs.
Modifier un data.frame
Pour plus d'efficacité, data.table offre un moyen de modifier un data.frame ou une liste pour créer un fichier data.table (sans faire de copie ni modifier son emplacement de mémoire):
# example data.frame
DF <- data.frame(x = letters[1:5], y = 1:5, z = (1:5) > 3)
# modification
setDT(DF)
Notez que nous ne <-
assignons pas le résultat, puisque l'objet DF
a été modifié sur place. Les attributs de classe du data.frame seront conservés:
sapply(DF, class)
# x y z
# "factor" "integer" "logical"
Coerce l'objet à data.table
Si vous avez une list
, data.frame
ou data.table
, vous devez utiliser la fonction setDT
pour convertir en une data.table
car elle effectue la conversion par référence au lieu de faire une copie (ce as.data.table
fait as.data.table
). Ceci est important si vous travaillez avec des jeux de données volumineux.
Si vous avez un autre objet R (comme une matrice), vous devez utiliser as.data.table
pour le contraindre à une data.table
.
mat <- matrix(0, ncol = 10, nrow = 10)
DT <- as.data.table(mat)
# or
DT <- data.table(mat)
Ajout et modification de colonnes
DT[where, select|update|do, by]
syntaxe DT[where, select|update|do, by]
est utilisée pour travailler avec des colonnes d'un data.table.
- La partie "where" est l'argument
i
- La partie "select | update | do" est l'argument
j
Ces deux arguments sont généralement passés par position plutôt que par nom.
Nos exemples de données ci-dessous sont
mtcars = data.table(mtcars, keep.rownames = TRUE)
Modification de colonnes entières
Utilisez l'opérateur :=
dans j
pour attribuer de nouvelles colonnes:
mtcars[, mpg_sq := mpg^2]
Supprimer les colonnes en définissant la NULL
sur NULL
:
mtcars[, mpg_sq := NULL]
Ajoutez plusieurs colonnes en utilisant le format multivarié de l'opérateur :=
mtcars[, `:=`(mpg_sq = mpg^2, wt_sqrt = sqrt(wt))]
# or
mtcars[, c("mpg_sq", "wt_sqrt") := .(mpg^2, sqrt(wt))]
Si les colonnes sont dépendantes et doivent être définies en séquence, une façon est:
mtcars[, c("mpg_sq", "mpg2_hp") := .(temp1 <- mpg^2, temp1/hp)]
La syntaxe .()
Est utilisée lorsque le côté droit de LHS := RHS
est une liste de colonnes.
Pour les noms de colonnes déterminés dynamiquement, utilisez des parenthèses:
vn = "mpg_sq"
mtcars[, (vn) := mpg^2]
Les colonnes peuvent également être modifiées avec set
, bien que cela soit rarement nécessaire:
set(mtcars, j = "hp_over_wt", v = mtcars$hp/mtcars$wt)
Modification de sous-ensembles de colonnes
Utilisez l'argument i
pour créer un sous-ensemble des lignes "où" des modifications doivent être apportées:
mtcars[1:3, newvar := "Hello"]
# or
set(mtcars, j = "newvar", i = 1:3, v = "Hello")
Comme dans un data.frame, nous pouvons sous-utiliser des numéros de lignes ou des tests logiques. Il est également possible d'utiliser une "jointure" dans i
, mais cette tâche plus complexe est abordée dans un autre exemple.
Modification des attributs de colonne
Les fonctions qui modifient des attributs, tels que les levels<-
ou les names<-
, remplacent en réalité un objet par une copie modifiée. Même s'il n'est utilisé que sur une colonne d'un data.table, l'objet entier est copié et remplacé.
Pour modifier un objet sans copie, utilisez setnames
pour modifier les noms de colonne d'un data.table ou data.frame et setattr
pour modifier un attribut pour n'importe quel objet.
# Print a message to the console whenever the data.table is copied
tracemem(mtcars)
mtcars[, cyl2 := factor(cyl)]
# Neither of these statements copy the data.table
setnames(mtcars, old = "cyl2", new = "cyl_fac")
setattr(mtcars$cyl_fac, "levels", c("four", "six", "eight"))
# Each of these statements copies the data.table
names(mtcars)[names(mtcars) == "cyl_fac"] <- "cf"
levels(mtcars$cf) <- c("IV", "VI", "VIII")
Sachez que ces modifications sont faites par référence, elles sont donc globales . Les modifier dans un environnement affecte l'objet dans tous les environnements.
# This function also changes the levels in the global environment
edit_levels <- function(x) setattr(x, "levels", c("low", "med", "high"))
edit_levels(mtcars$cyl_factor)
Symboles spéciaux dans data.table
.DAKOTA DU SUD
.SD
fait référence au sous-ensemble de data.table
pour chaque groupe, à l'exclusion de toutes les colonnes utilisées by
.
.SD
avec lapply
peut être utilisé pour appliquer n'importe quelle fonction à plusieurs colonnes par groupe dans un data.table
Nous continuerons à utiliser le même jeu de données mtcars
, mtcars
:
mtcars = data.table(mtcars) # Let's not include rownames to keep things simpler
Moyenne de toutes les colonnes du jeu de données par nombre de cylindres , cyl
:
mtcars[ , lapply(.SD, mean), by = cyl]
# cyl mpg disp hp drat wt qsec vs am gear carb
#1: 6 19.74286 183.3143 122.28571 3.585714 3.117143 17.97714 0.5714286 0.4285714 3.857143 3.428571
#2: 4 26.66364 105.1364 82.63636 4.070909 2.285727 19.13727 0.9090909 0.7272727 4.090909 1.545455
#3: 8 15.10000 353.1000 209.21429 3.229286 3.999214 16.77214 0.0000000 0.1428571 3.285714 3.500000
En dehors de cyl
, il existe d'autres colonnes catégoriques dans le jeu de données, telles que vs
, am
, gear
et carb
. Cela n'a pas vraiment de sens de prendre la mean
de ces colonnes. Excluons donc ces colonnes. C'est ici que .SDcols
entre en jeu.
.SDcols
.SDcols
spécifie les colonnes de data.table
incluses dans .SD
.
Moyenne de toutes les colonnes (colonnes continues) dans l'ensemble de données selon le nombre d'engrenages gear
, et le nombre de cylindres, cyl
, disposés par gear
et cyl
:
# All the continuous variables in the dataset
cols_chosen <- c("mpg", "disp", "hp", "drat", "wt", "qsec")
mtcars[order(gear, cyl), lapply(.SD, mean), by = .(gear, cyl), .SDcols = cols_chosen]
# gear cyl mpg disp hp drat wt qsec
#1: 3 4 21.500 120.1000 97.0000 3.700000 2.465000 20.0100
#2: 3 6 19.750 241.5000 107.5000 2.920000 3.337500 19.8300
#3: 3 8 15.050 357.6167 194.1667 3.120833 4.104083 17.1425
#4: 4 4 26.925 102.6250 76.0000 4.110000 2.378125 19.6125
#5: 4 6 19.750 163.8000 116.5000 3.910000 3.093750 17.6700
#6: 5 4 28.200 107.7000 102.0000 4.100000 1.826500 16.8000
#7: 5 6 19.700 145.0000 175.0000 3.620000 2.770000 15.5000
#8: 5 8 15.400 326.0000 299.5000 3.880000 3.370000 14.5500
Peut-être que nous ne voulons pas calculer la mean
par groupes. Pour calculer la moyenne de toutes les voitures du jeu de données, nous ne spécifions pas la variable by
.
mtcars[ , lapply(.SD, mean), .SDcols = cols_chosen]
# mpg disp hp drat wt qsec
#1: 20.09062 230.7219 146.6875 3.596563 3.21725 17.84875
Remarque:
- Il n'est pas nécessaire de définir
cols_chosen
au préalable..SDcols
peut directement prendre des noms de colonnes -
.SDcols
peut également prendre directement un vecteur de colonnes. Dans l'exemple ci-dessus, il s'agirait demtcars[ , lapply(.SD, mean), .SDcols = c(1,3:7)]
.N
.N
est un raccourci pour le nombre de lignes d'un groupe.
iris[, .(count=.N), by=Species]
# Species count
#1: setosa 50
#2: versicolor 50
#3: virginica 50
Ecriture de code compatible à la fois avec data.frame et data.table
Différences dans la syntaxe de sous-ensemble
Une data.table
est l'une des structures de données bidimensionnelles disponibles dans R, outre data.frame
, matrix
et (2D) array
. Toutes ces classes utilisent une syntaxe très similaire mais pas identique pour le sous-ensemble, le schéma A[rows, cols]
.
Considérons les données suivantes stockées dans une matrix
, un data.frame
et un data.table
:
ma <- matrix(rnorm(12), nrow=4, dimnames=list(letters[1:4], c('X', 'Y', 'Z')))
df <- as.data.frame(ma)
dt <- as.data.table(ma)
ma[2:3] #---> returns the 2nd and 3rd items, as if 'ma' were a vector (because it is!)
df[2:3] #---> returns the 2nd and 3rd columns
dt[2:3] #---> returns the 2nd and 3rd rows!
Si vous voulez être sûr de ce qui sera retourné, il vaut mieux être explicite .
Pour obtenir des lignes spécifiques, ajoutez simplement une virgule après la plage:
ma[2:3, ] # \
df[2:3, ] # }---> returns the 2nd and 3rd rows
dt[2:3, ] # /
Toutefois, si vous souhaitez créer des sous-ensembles de colonnes , certains cas sont interprétés différemment. Tous les trois peuvent être regroupés de la même manière avec des index entiers ou de caractères non stockés dans une variable.
ma[, 2:3] # \
df[, 2:3] # \
dt[, 2:3] # }---> returns the 2nd and 3rd columns
ma[, c("Y", "Z")] # /
df[, c("Y", "Z")] # /
dt[, c("Y", "Z")] # /
Cependant, ils diffèrent pour les noms de variables non cotés
mycols <- 2:3
ma[, mycols] # \
df[, mycols] # }---> returns the 2nd and 3rd columns
dt[, mycols, with = FALSE] # /
dt[, mycols] # ---> Raises an error
Dans le dernier cas, mycols
est évalué en tant que nom d'une colonne. Comme dt
ne peut pas trouver une colonne nommée mycols
, une erreur est mycols
.
Remarque: Pour les versions du package data.table
1.9.8, ce comportement était légèrement différent. Tout élément de l'index de la colonne aurait été évalué en utilisant dt
comme environnement. Donc, dt[, 2:3]
et dt[, mycols]
renverraient le vecteur 2:3
. Aucune erreur ne serait soulevée pour le second cas, car la variable mycols
existe dans l'environnement parent.
Stratégies pour maintenir la compatibilité avec data.frame et data.table
Il existe de nombreuses raisons d'écrire du code qui est garanti pour fonctionner avec data.frame
et data.table
. Vous êtes peut-être obligé d'utiliser data.frame
, ou vous devrez peut-être partager un code que vous ne savez pas comment utiliser. Il existe donc des stratégies principales pour y parvenir, par ordre de commodité:
- Utilisez la syntaxe qui se comporte de la même manière pour les deux classes.
- Utilisez une fonction commune qui fait la même chose que la syntaxe la plus courte.
- Forcer
data.table
à se comporter commedata.frame
(ex .: appeler la méthode spécifiqueprint.data.frame
). - Traitez-les comme une
list
, ce qu'ils sont finalement. - Convertissez la table en
data.frame
avant de faire quoi que ce soit (mauvaise idée si c'est une énorme table). - Convertissez la table en
data.table
, si les dépendances ne sont pas un problème.
Sous-ensembles de lignes. C'est simple, utilisez simplement le sélecteur [, ]
, avec la virgule:
A[1:10, ]
A[A$var > 17, ] # A[var > 17, ] just works for data.table
Colonne de sous-ensemble. Si vous voulez une seule colonne, utilisez le sélecteur $
ou [[ ]]
:
A$var
colname <- 'var'
A[[colname]]
A[[1]]
Si vous voulez une méthode uniforme pour saisir plus d’une colonne, il faut faire appel à un peu:
B <- `[.data.frame`(A, 2:4)
# We can give it a better name
select <- `[.data.frame`
B <- select(A, 2:4)
C <- select(A, c('foo', 'bar'))
Sous-ensemble 'lignes indexées'. Alors que data.frame
possède row.names
, data.table
a sa fonctionnalité de key
unique. Le mieux est d'éviter complètement les row.names
et de tirer parti des optimisations existantes dans le cas de data.table
lorsque cela est possible.
B <- A[A$var != 0, ]
# or...
B <- with(A, A[var != 0, ]) # data.table will silently index A by var before subsetting
stuff <- c('a', 'c', 'f')
C <- A[match(stuff, A$name), ] # really worse than: setkey(A); A[stuff, ]
Obtenez un tableau à 1 colonne, obtenez une ligne en tant que vecteur. Ce sont faciles avec ce que nous avons vu jusqu'à maintenant:
B <- select(A, 2) #---> a table with just the second column
C <- unlist(A[1, ]) #---> the first row as a vector (coerced if necessary)
Définition des clés dans data.table
Oui, vous devez SETKEY avant 1.9.6
Auparavant (avant 1.9.6), votre data.table
était accélérée en définissant des colonnes comme clés de la table, en particulier pour les grandes tables. [Voir la vignette d'introduction page 5 de la version de septembre 2015, où la vitesse de recherche était 544 fois meilleure.]
library(data.table)
DT <- data.table(
x = letters[1:5],
y = 5:1,
z = (1:5) > 3
)
#> DT
# x y z
#1: a 5 FALSE
#2: b 4 FALSE
#3: c 3 FALSE
#4: d 2 TRUE
#5: e 1 TRUE
Définissez votre clé avec la commande setkey
. Vous pouvez avoir une clé avec plusieurs colonnes.
setkey(DT, y)
Vérifiez la clé de votre table dans les tableaux ()
tables()
> tables()
NAME NROW NCOL MB COLS KEY
[1,] DT 5 3 1 x,y,z y
Total: 1MB
Notez que cela reclassera vos données.
#> DT
# x y z
#1: e 1 TRUE
#2: d 2 TRUE
#3: c 3 FALSE
#4: b 4 FALSE
#5: a 5 FALSE
Maintenant c'est inutile
Avant la v1.9.6, il fallait définir une clé pour certaines opérations, en particulier pour joindre des tables. Les développeurs de data.table ont accéléré et introduit une fonctionnalité "on="
qui peut remplacer la dépendance aux clés. Voir SO répondre ici pour une discussion détaillée.
En janvier 2017, les développeurs ont écrit une vignette autour des indices secondaires qui explique la syntaxe "on" et permet d'identifier d'autres colonnes pour une indexation rapide.
Créer des indices secondaires?
De manière similaire à key, vous pouvez setindex(DT, key.col)
ou setindexv(DT, "key.col.string")
, où DT est votre data.table. Supprimez tous les index avec setindex(DT, NULL)
.
Voir vos indices secondaires avec des indices(DT)
.
Pourquoi des indices secondaires?
Cela ne trie pas la table (contrairement à key), mais permet une indexation rapide en utilisant la syntaxe "on". Notez qu'il ne peut y avoir qu'une seule clé, mais vous pouvez utiliser plusieurs index secondaires, ce qui évite de devoir ressaisir et utiliser la table. Cela accélérera votre sous-ensemble lors de la modification des colonnes sur lesquelles vous souhaitez créer un sous-ensemble.
Rappel, dans l'exemple ci-dessus, y était la clé pour la table DT:
DT
# x y z
# 1: e 1 TRUE
# 2: d 2 TRUE
# 3: c 3 FALSE
# 4: b 4 FALSE
# 5: a 5 FALSE
# Let us set x as index
setindex(DT, x)
# Use indices to see what has been set
indices(DT)
# [1] "x"
# fast subset using index and not keyed column
DT["c", on ="x"]
#x y z
#1: c 3 FALSE
# old way would have been rekeying DT from y to x, doing subset and
# perhaps keying back to y (now we save two sorts)
# This is a toy example above but would have been more valuable with big data sets