library(data.table) knitr::opts_chunk$set( comment = "#", error = FALSE, tidy = FALSE, cache = FALSE, collapse = TRUE) .old.th = setDTthreads(1)
La première section, FAQ pour débutants, est destinée à être lue dans l'ordre, du début à la fin. Elle est simplement rédigée dans le style d'une FAQ afin d'être plus facile à assimiler. Il ne s'agit pas vraiment des questions les plus fréquemment posées. Une meilleure mesure pour cela est de regarder sur Stack Overflow.
Cette FAQ est une lecture obligatoire et est considérée comme une documentation de base. Ne posez pas de questions sur Stack Overflow ou ne soulevez pas de problèmes (issues) sur GitHub avant de l'avoir lue. Nous savons tous que vous n'avez pas lu la FAQ lorsque vous posez une question. Si vous posez une question sans l'avoir lue, n'utilisez pas votre vrai nom.
Ce document a été rapidement révisé en fonction des changements apportés à la version 1.9.8 publiée en novembre 2016. N'hésitez pas à soumettre des pull requests pour corriger des erreurs ou faire des améliorations. Si quelqu'un sait pourquoi la table des matières est si étroite et écrasée lorsqu'elle est affichée par le CRAN, merci de nous le faire savoir. Ce document était auparavant un PDF et nous l'avons récemment changé en HTML.
DT[ , 5]
et DT[2, 5]
renvoient-ils un data.table à une colonne plutôt que des vecteurs comme data.frame
? {#j-num}Pour des raisons de cohérence, lorsque vous utilisez data.table dans des fonctions qui acceptent des entrées variables, vous pouvez compter sur DT[...]
qui renvoie un data.table. Vous n'avez pas à vous souvenir d'inclure drop=FALSE
comme vous le faites dans data.frame. data.table a été publié pour la première fois en 2006 et cette différence avec data.frame a été une caractéristique depuis le tout début.
Vous avez peut-être entendu dire qu'il n'est généralement pas judicieux de désigner les colonnes par leur numéro plutôt que par leur nom. Si votre collègue vient à lire votre code plus tard, il devra peut-être chercher à savoir quelle colonne porte le numéro 5. Si vous ou lui changez l'ordre des colonnes plus haut dans votre programme R, vous risquez de produire des résultats erronés sans avertissement ni erreur si vous oubliez de modifier tous les endroits de votre code qui font référence à la colonne numéro 5. C'est votre faute, pas celle de R ou de data.table. C'est vraiment très mauvais. S'il vous plaît, ne le faites pas. C'est le même mantra que celui des développeurs SQL professionnels : ne jamais utiliser select *
, toujours sélectionner explicitement par le nom de la colonne pour au moins essayer d'être robuste aux changements futurs.
Disons que la colonne 5 s'appelle "region" et que vous devez vraiment extraire cette colonne en tant que vecteur et non en tant que data.table. Il est plus robuste d'utiliser le nom de la colonne et d'écrire DT$region
ou DT["region"]]
; c'est à dire, la même chose que R de base. Pas lorsqu'ils sont combinés avec <-
pour assigner (utilisez :=
à la place pour cela) mais juste pour sélectionner une seule colonne par son nom, ils sont encouragés.
Il y a des circonstances où se référer à une colonne par un numéro semble être la seule façon, comme une séquence de colonnes. Dans ces situations, tout comme data.frame, vous pouvez écrire DT[, 5:10]
et DT[,c(1,4,10)]
. Cependant, encore une fois, il est plus robuste (face à de futurs changements dans le nombre et l'ordre des colonnes de vos données) d'utiliser une plage nommée comme DT[,columnRed:columnViolet]
ou de nommer chacune DT[,c("columnRed", "columnOrange", "columnYellow")]
. C'est un travail plus difficile au départ, mais vous vous en féliciterez probablement et vos collègues vous en remercieront peut-être à l'avenir. Au moins, vous pourrez dire que vous avez fait de votre mieux pour écrire un code robuste en cas de problème.
Cependant, ce que nous voulons vraiment que vous fassiez est DT[,.(colonneRouge,colonneOrange,colonneJaune)]
; c'est-à-dire, utiliser les noms de colonnes comme s'ils étaient des variables directement à l'intérieur de DT[...]
. Vous n'avez pas besoin de préfixer chaque colonne avec DT$
comme vous le faites dans data.frame. La partie .()
est juste un alias pour list()
et vous pouvez utiliser list()
à la place si vous préférez. Vous pouvez placer n'importe quelle expression R de noms de colonnes, en utilisant n'importe quel package R, retournant différents types de longueurs différentes, juste ici. Nous voulions tellement vous encourager à le faire dans le passé que nous avons délibérément fait en sorte que DT[,5]
ne fonctionne pas du tout. Avant la version 1.9.8 publiée en novembre 2016, DT[,5]
retournait simplement 5
. L'idée était d'enseigner plus simplement que les parties à l'intérieur de DT[...]
sont toujours évaluées dans le cadre de DT (ils voient les noms de colonnes comme s'il s'agissait de variables). Et 5
est évalué à 5
, de sorte que ce comportement est cohérent avec la règle unique. Nous vous avons demandé de passer par un obstacle supplémentaire délibéré DT[,5,with=FALSE]
si vous vouliez vraiment sélectionner une colonne par nom ou par nombre. A partir de Nov 2016, vous n'aurez plus besoin d'utiliser with=FALSE
et nous verrons comment une plus grande cohérence avec data.frame à cet égard aidera ou gênera les nouveaux utilisateurs et les utilisateurs de longue date. Les nouveaux utilisateurs qui ne lisent pas cette FAQ, pas même cette toute première entrée, ne trébucheront pas aussi vite avec data.table qu'ils l'ont fait auparavant s'ils s'attendaient à ce qu'il fonctionne comme data.frame. Nous espérons qu'ils ne manqueront pas de comprendre notre intention et notre recommandation de placer les expressions de colonnes à l'intérieur de DT[i, j, by]
. S'ils utilisent data.table comme data.frame, ils n'en tireront aucun bénéfice. Si vous connaissez quelqu'un dans ce cas, donnez-lui un coup de pouce amical pour qu'il lise ce document comme vous le faites.
Rappel : vous pouvez placer n'importe quelle expression R à l'intérieur de DT[...]
en utilisant les noms de colonnes comme s'il s'agissait de variables ; par exemple, essayez DT[, colA*colB/2]
. Cela renvoie un vecteur parce que vous avez utilisé les noms de colonnes comme s'il s'agissait de variables. Enveloppez avec .()
pour retourner un data.table ; i.e. DT[,.(colA*colB/2)]
. Nommez-le : DT[,.(myResult = colA*colB/2)]
. Et nous vous laissons deviner comment retourner deux choses à partir de cette requête. Il est aussi assez courant de faire un tas de choses à l'intérieur d'un corps anonyme : DT[, { x<-colA+10 ; x*x/2 }]
ou d'appeler une fonction d'un autre package : DT[ , fitdistr(columnA, "normal")]
.
DT[, "region"]
renvoie-t-il un data.table à une colonne plutôt qu'un vecteur ?Voir la réponse ci-dessus. Essayez DT$region
à la place. Ou DT[["region"]]
.
DT[, region]
retourne un vecteur pour la colonne "region" ? Je voudrais un data.table à 1 colonne.Essayez plutôt DT[ , .(region)]
. .()
est un alias de list()
et assure qu'un data.table est retournée.
Poursuivez également votre lecture et consultez la FAQ qui suit. Parcourez des documents entiers avant de rester bloqué sur une partie.
DT[ , x, y, z]
ne fonctionne pas ? Je voulais les 3 colonnes x
,y
et z
.L'expression j
est le 2ème argument. Essayez DT[ , c("x", "y", "z")]
ou DT[ , .(x,y,z)]
.
mycol="x"
mais DT[, mycol]
renvoie une erreur. Comment faire pour qu'il recherche le nom de la colonne contenue dans la variable mycol
?L'erreur est que la colonne nommée "mycol"
ne peut pas être trouvée, et cette erreur est correcte. la portée de data.table
est différente de celle de data.frame
dans la mesure où vous pouvez utiliser les noms de colonnes comme s'il s'agissait de variables directement à l'intérieur de DT[...]
sans préfixer chaque nom de colonne par DT$
; voir la FAQ 1.1 ci-dessus.
Pour utiliser mycol
afin de sélectionner la colonne x
de DT
, il y a quelques options :
DT[, ..mycol] # ... préfixe indique qu'il faut rechercher le mycol un niveau plus haut dans l'appel DT[, mycol, with=FALSE] # revient au comportement data.frame DT[[mycol]] # traiter DT comme une liste et utiliser [[ de la base R
Voir ?data.table
pour plus de détails sur le préfixe ..
.
L'argument with
tire son nom de la fonction base
with()
. Lorsque with=TRUE
(par défaut), data.table
fonctionne de manière similaire à with()
, c'est-à-dire que DT[, mycol]
se comporte comme with(DT, mycol)
. Lorsque with=FALSE
, les règles d'évaluation standard de data.frame
s'appliquent à toutes les variables de j
et vous ne pouvez plus utiliser les noms de colonnes directement.
DT[...]
?j
n'a pas besoin d'être uniquement un nom de colonne. Vous pouvez écrire n'importe quelle expression R de noms de colonnes directement dans j
, e.g., DT[ , mean(x*y/z)]
. La même chose s'applique à i
, e.g., DT[x>1000, sum(y*z)]
.
Ceci exécute l'expression j
sur l'ensemble des lignes où l'expression i
est vraie. Vous n'avez même pas besoin de renvoyer des données, e.g., DT[x>1000, plot(y, z)]
. Vous pouvez faire j
par groupe en ajoutant simplement by =
; par exemple, DT[x>1000, sum(y*z), by = w]
. Ceci exécute j
pour chaque groupe dans la colonne w
mais seulement sur les lignes où x>1000
. En plaçant les 3 parties de la requête (i=where, j=select et by=group by) à l'intérieur des crochets, data.table voit cette requête comme un tout avant qu'aucune partie ne soit évaluée. Il peut ainsi optimiser les performances de la requête combinée. Il peut le faire parce que le langage R dispose uniquement d'une évaluation paresseuse (ce qui n'est pas le cas de Python et de Julia). data.table voit les expressions à l'intérieur de DT[...]
avant qu'elles ne soient évaluées et les optimise avant l'évaluation. Par exemple, si data.table voit que vous n'utilisez que 2 colonnes sur 100, il ne s'embêtera pas à sous-sélectionner les 98 qui ne sont pas nécessaires à votre expression j.
data.frame
dans R ? Pourquoi faut-il que ce soit un nouveau package ?Comme [souligné ci-dessus] (#j-num), j
dans [.data.table
est fondamentalement différent de j
dans [.data.frame
. Même si quelque chose d'aussi simple que DF[ , 1]
était modifié dans la base R pour retourner un data.frame plutôt qu'un vecteur, cela casserait le code existant dans des milliers de package CRAN et dans le code utilisateur. Dès que nous avons pris la décision de créer une nouvelle classe héritant de data.frame, nous avons eu l'opportunité de changer certaines choses et nous l'avons fait. Nous voulons que data.table soit légèrement différent et qu'il fonctionne de cette façon pour que la syntaxe plus compliquée fonctionne. Il existe également d'autres différences (voir ci-dessous ).
De plus, data.table hérite de data.frame
. C'est aussi un data.frame
. Un data.table peut être passé à n'importe quel package qui n'accepte que data.frame
et ce package peut utiliser la syntaxe [.data.frame
sur le data.table. Voir [cette réponse] (https://stackoverflow.com/a/10529888/403310) pour savoir comment procéder.
Nous avons également proposé des améliorations à R chaque fois que cela était possible. L'une d'entre elles a été acceptée comme nouvelle fonctionnalité dans R 2.12.0 :
unique()
etmatch()
sont maintenant plus rapides sur les vecteurs de caractères où tous les éléments sont dans le cache global CHARSXP et ont un encodage non marqué (ASCII). Merci à Matt Dowle pour avoir suggéré des améliorations dans la façon dont le code de hachage est généré dans unique.c.
Une deuxième proposition était d'utiliser memcpy
dans duplicate.c, qui est beaucoup plus rapide qu'une boucle for en C. Cela améliorerait la manière dont R copie les données en interne (sur certaines mesures, de 13 fois). Le fil de discussion sur r-devel est [ici] (https://stat.ethz.ch/pipermail/r-devel/2010-April/057249.html).
Une troisième proposition plus significative qui a été acceptée est que R utilise maintenant le code de tri par base (radix sort) de data.table à partir de R 3.3.0 :
L'algorithme de tri par base (radix sort) et l'implémentation de data.table (forder) remplace l'ancien tri par base (comptage) et ajoute une nouvelle méthode pour order(). Proposé par Matt Dowle et Arun Srinivasan, le nouvel algorithme supporte les vecteurs de logiques, d’entiers (même avec de grandes valeurs), de réels et de caractères. Il est plus performant que toutes les autres méthodes, mais il y a quelques mises en garde (voir ?sort).
C'était un grand événement pour nous et nous l'avons fêté jusqu'à ce que les vaches rentrent à la maison. (Pas vraiment.)
La réponse est simple : l'auteur principal l'a conçu à l'origine pour son propre usage. C'est ce qu'il voulait. Il trouve que c'est une façon plus naturelle et plus rapide d'écrire du code, qui s'exécute également plus rapidement.
with()
et subset()
dans base
?Certaines des caractéristiques discutées jusqu'à présent sont, oui. Le package s'appuie sur la fonctionnalité de base. Il fait le même genre de choses, mais avec moins de code et s'exécute beaucoup plus rapidement s'il est utilisé correctement.
X[Y]
retourne-t-il aussi toutes les colonnes de Y
? Ne devrait-elle pas retourner un sous-ensemble de X
?Cela a été modifié dans la version 1.5.3 (février 2011). Depuis lors, X[Y]
inclut les colonnes non-jointes de Y
. Nous nous référons à cette fonctionnalité comme join inherited scope parce que non seulement les colonnes X
sont disponibles pour l'expression j
, mais les colonnes Y
le sont aussi. L'inconvénient est que X[Y]
est moins efficace puisque chaque élément des colonnes non-jointes de Y
est dupliqué pour correspondre au nombre (probablement grand) de lignes dans X
qui correspondent. Nous encourageons donc fortement l'utilisation de X[Y, j]
au lieu de X[Y]
. Voir FAQ suivante.
X[Y]
et merge(X, Y)
? {#MergeDiff}X[Y]
est une jointure, qui recherche les lignes de X
en utilisant Y
(ou la clé de Y
si elle en a une) comme index.
Y[X]
est une jointure, qui recherche les lignes de Y
en utilisant X
(ou la clé de X
si elle en a une) comme index.
merge(X,Y)
[^1] fait les deux en même temps. Le nombre de lignes de X[Y]
et de Y[X]
est généralement différent, alors que le nombre de lignes retournées par merge(X, Y)
et merge(Y, X)
est le même.
MAIS cela ne tient pas compte de l'essentiel. La plupart des tâches exigent que l'on fasse quelque chose sur les données après une jointure ou une fusion. Pourquoi fusionner toutes les colonnes de données pour n'en utiliser qu'un petit sous-ensemble par la suite ? Vous pouvez suggérer merge(X[ , ColsNeeded1], Y[ , ColsNeed2])
, mais cela demande au programmeur de déterminer quelles colonnes sont nécessaires. X[Y, j]
dans data.table fait tout cela en une seule étape pour vous. Quand vous écrivez X[Y, sum(foo*bar)]
, data.table inspecte automatiquement l'expression j
pour voir quelles colonnes elle utilise. Il ne prend qu’un sous-ensemble des colonnes ; les autres sont ignorées. La mémoire n'est créée que pour les colonnes que j
utilise et les colonnes Y
bénéficient des règles de recyclage standard de R dans le contexte de chaque groupe. Disons que foo
est dans X
et bar
est dans Y
(avec 20 autres colonnes dans Y
). Est-ce que X[Y, sum(foo*bar)]
n'est pas plus rapide à programmer et à exécuter qu'une fusion
de tout ce qui est suivi par un subset
?
[^1]: Il s'agit ici de la méthode merge
method pour data.table ou de la méthode merge
pour data.frame
puisque les deux méthodes fonctionnent de la même manière à cet égard. Voir ?merge.data.table
et below pour plus d'informations sur la répartition des méthodes.
X[Y, sum(foo*bar)]
?Ce comportement a changé dans la version 1.9.4 (septembre 2014). Il fait maintenant la jointure X[Y]
et exécute ensuite sum(foo*bar)
sur toutes les lignes ; c'est à dire, X[Y][ , sum(foo*bar)]
. Il avait l'habitude d'exécuter j
pour chaque groupe de X
auquel correspondait chaque ligne de Y
. Cela peut toujours être fait et c'est très utile, mais vous devez maintenant être explicite et spécifier by = .EACHI
, c'est-à-dire X[Y, sum(foo*bar), by = .EACHI]
. C'est ce que nous appelons le regroupement par chaque i
*.
Par exemple, (en le compliquant encore en utilisant join inherited scope, aussi) :
X = data.table(grp = c("a", "a", "b", "b", "b", "c", "c"), foo = 1:7) setkey(X, grp) Y = data.table(c("b", "c"), bar = c(4, 2)) X Y X[Y, sum(foo*bar)] X[Y, sum(foo*bar), by = .EACHI]
La demande de changement est venue des utilisateurs. Le sentiment était que si une requête fait du groupage, alors un by=
explicite devrait être présent pour des raisons de lisibilité du code. Une option a été fournie pour retourner l'ancien comportement : options(datatable.old.bywithoutby)
, par défaut FALSE
. Cela a permis de tester les autres nouvelles fonctionnalités et corrections de bogues de la version 1.9.4, avec une migration ultérieure des requêtes by-without-by lorsqu'elles sont prêtes en ajoutant by=.EACHI
à ces requêtes. Nous avons conservé 47 tests antérieurs au changement et les avons ajoutés en tant que nouveaux tests, testés sous options(datatable.old.bywithoutby=TRUE)
. Nous avons ajouté un message de démarrage à propos du changement et de la façon de revenir à l'ancien comportement. Après 1 an, l'option a été dépréciée avec un avertissement en cas d'utilisation. Après 2 ans, l'option permettant de revenir à l'ancien comportement a été supprimée.
Sur les 66 packages sur CRAN ou Bioconductor qui dépendaient ou importaient data.table au moment de la publication de la v1.9.4 (il y en a maintenant plus de 300), un seul a été affecté par le changement. Cela peut être dû au fait que de nombreux packages n'ont pas de tests complets, ou simplement au fait que le regroupement par chaque ligne dans i
n'était pas beaucoup utilisé par les packages en aval. Nous testons toujours la nouvelle version avec tous les packages dépendants avant de la publier et nous coordonnons les changements avec les responsables de ces packages. Cette version a donc été assez simple à cet égard.
Une autre raison convaincante de faire ce changement est qu'auparavant, il n'y avait pas de moyen efficace de réaliser ce que X[Y, sum(foo*bar)]
fait maintenant. Il fallait écrire X[Y][ , sum(foo*bar)]
. C'était sous-optimal parce que X[Y]
joignait toutes les colonnes et les passait toutes à la seconde requête composée sans savoir que seuls foo
et bar
étaient nécessaires. Pour résoudre ce problème d'efficacité, un effort de programmation supplémentaire a été nécessaire : X[Y, list(foo, bar)][ , sum(foo*bar)]
. Le passage à by = .EACHI
a simplifié cela en permettant aux deux requêtes d'être exprimées dans une seule requête DT[...]
pour plus d'efficacité.
j
très longue ? Vous avez dit que je devrais utiliser les noms de colonnes, mais j'ai beaucoup de colonnes.Lors du regroupement, l'expression j
peut utiliser les noms de colonnes comme variables, comme vous le savez, mais elle peut aussi utiliser un symbole réservé .SD
qui fait référence au Sous-ensemble de la Data.table pour chaque groupe (à l'exclusion des colonnes de regroupement). Donc pour résumer toutes vos colonnes, c'est juste DT[ , lapply(.SD, sum), by = grp]
. Cela peut sembler compliqué, mais c'est rapide à écrire et à exécuter. Notez que vous n'avez pas besoin de créer une fonction anonyme. L'objet .SD
est efficacement implémenté en interne et plus efficace que de passer un argument à une fonction. Mais si le symbole .SD
apparaît dans j
alors data.table doit remplir .SD
complètement pour chaque groupe même si j
ne l'utilise pas entièrement.
Ne faites donc pas, par exemple, DT[ , sum(.SD[["sales"]]), by = grp]
. Cela fonctionne, mais c'est inefficace et inélégant. DT[ , sum(sales), by = grp]
est ce qui était prévu, et il pourrait être des centaines de fois plus rapide. Si vous utilisez toutes les données de .SD
pour chaque groupe (comme dans DT[ , lapply(.SD, sum), by = grp]
) alors c'est une très bonne utilisation de .SD
. Si vous utilisez plusieurs mais pas toutes les colonnes, vous pouvez combiner .SD
avec .SDcols
; voir ?data.table
.
mult
est-elle maintenant "all"
?Dans la version 1.5.3, la valeur par défaut a été changée en "all". Quand i
(ou la clé de i
si elle en a une) a moins de colonnes que la clé de x
, mult
était déjà mis à "all"
automatiquement. Changer la valeur par défaut rend la chose plus claire et plus facile pour les utilisateurs, car la question se posait assez souvent.
Dans les versions antérieures à la v1.3, "all"
était plus lent. En interne, "all"
était implémenté en joignant en utilisant "first"
, puis à nouveau à partir de zéro en utilisant "last"
, après quoi un diff entre eux était effectué pour calculer l'étendue des correspondances dans x
pour chaque ligne dans i
. La plupart du temps, nous effectuons des jointures sur des lignes individuelles, où "first"
,"last"
et "all"
renvoient le même résultat. Nous avons préféré une performance maximale dans la majorité des cas, c'est pourquoi nous avons choisi par défaut "first"
. Lorsque l'on travaille avec une clé non unique (généralement une colonne unique contenant une variable de regroupement), DT["A"]
renvoie la première ligne de ce groupe, donc DT["A", mult = "all"]
est nécessaire pour renvoyer toutes les lignes de ce groupe.
Dans la version 1.4, la recherche binaire en C a été modifiée pour se brancher au niveau le plus profond afin de trouver le premier et le dernier. Ce branchement se produira probablement dans les mêmes pages finales de RAM, donc il ne devrait plus y avoir de désavantage en termes de vitesse en mettant par défaut mult
à "all"
. Nous avons prévenu que la valeur par défaut pourrait changer et nous avons fait le changement dans la version 1.5.3.
Une future version de data.table pourrait permettre une distinction entre une clé et une clé unique. En interne, mult = "all"
fonctionnerait plus comme mult = "first"
lorsque toutes les colonnes clés de x
sont jointes et que la clé de x
est une clé unique. data.table aurait besoin de vérifications lors de l'insertion et de la mise à jour pour s'assurer qu'une clé unique est maintenue. L'avantage de spécifier une clé unique serait que data.table s'assurerait qu'aucun duplicata ne puisse être inséré, en plus de la performance.
c()
dans j
et j'obtiens des résultats étranges.Il s'agit d'une source de confusion fréquente. Dans data.frame
, vous avez l'habitude de, par exemple :
DF = data.frame(x = 1:3, y = 4:6, z = 7:9) DF DF[ , c("y", "z")]
qui renvoie les deux colonnes. Dans data.table, vous savez que vous pouvez utiliser les noms de colonnes directement et vous pouvez essayer :
DT = data.table(DF) DT[ , c(y, z)]
mais il renvoie un vecteur. Rappelez-vous que l'expression j
est évaluée dans l'environnement de DT
et que c()
renvoie un vecteur. Si 2 colonnes ou plus sont nécessaires, utilisez list()
ou .()
à la place :
DT[ , .(y, z)]
c()
peut également être utile dans un data.table, mais son comportement est différent de celui de [.data.frame
.
Si votre table complexe s'appelle DT
, essayez NEWDT = DT[0]
.
DT[0]
?Non. Par « null.data.table », nous entendons le résultat de data.table(NULL)
ou de as.data.table(NULL)
; c'est-à-dire ,
data.table(NULL) data.frame(NULL) as.data.table(NULL) as.data.frame(NULL) is.null(data.table(NULL)) is.null(data.frame(NULL))
Le data.table|frame
null est NULL
avec quelques attributs attachés, ce qui signifie qu'il n'est plus NULL
. Dans R, seul le NULL
pur est NULL
, comme testé par is.null()
. Lorsque l'on se réfère au « nul data.table », on utilise la minuscule null pour faire la différence avec la majuscule NULL
. Pour tester la nullité d'un data.table, utilisez length(DT) == 0
ou ncol(DT) == 0
(length
est légèrement plus rapide car il s'agit d'une fonction primitive).
Un data.table vide (DT[0]
) possède une ou plusieurs colonnes, toutes vides. Ces colonnes vides ont toujours des noms et des types.
DT = data.table(a = 1:3, b = c(4, 5, 6), d = c(7L,8L,9L)) DT[0] sapply(DT[0], class)
DT()
a-t-il été supprimé ? {#DTremove1}DT
a été introduit à l'origine comme une enveloppe pour une liste d'expressions j
. Comme DT
était un alias de data.table, c'était un moyen pratique de prendre en charge le recyclage silencieux dans les cas où chaque élément de la liste j
était évalué à des longueurs différentes. L'alias était l'une des raisons pour lesquelles le groupage était lent.
Depuis la version 1.3, list()
ou .()
devraient être passés à la place de l'argument j
. Ces méthodes sont beaucoup plus rapides, en particulier lorsqu'il y a beaucoup de groupes. En interne, il s'agit d'un changement non trivial. Le recyclage des vecteurs est maintenant effectué en interne, ainsi que plusieurs autres améliorations de la vitesse de groupage.
j = DT(...)
et il fonctionne. La FAQ précédente dit que DT()
a été supprimé. {#DTremove2}Vous utilisez alors une version antérieure à la 1.5.3. Avant la version 1.5.3, [.data.table
détectait l'utilisation de DT()
dans le j
et le remplaçait automatiquement par un appel à list()
. Ceci avait pour but de faciliter la transition pour les utilisateurs existants.
j
?Considérez le sous-ensemble comme un environnement où tous les noms de colonnes sont des variables. Lorsqu'une variable foo
est utilisée dans le j
d'une requête telle que X[Y, sum(foo)]
, foo
est recherché dans l'ordre suivant :
X
; c'est-à-dire , les noms de colonnes de X
.Y
; i.e., les noms des colonnes de Y
(joint inherited scope)globalenv()
? Il s'agit d'un cadrage logique comme expliqué dans R FAQ 3.3.1. L'environnement dans lequel la fonction a été créée n'est pas pertinent, cependant, parce qu'il n'y a pas de fonction. Aucune fonction anonyme n'est passée à j
. Au lieu de cela, un corps anonyme est passé à j
; par exemple,
DT = data.table(x = rep(c("a", "b"), c(2, 3)), y = 1:5) DT DT[ , {z = sum(y) ; z + 3}, by = x]
Certains langages de programmation appellent cela un lambda.
j
au fur et à mesure qu'elle passe dans les groupes ? {#j-trace}Essayez quelque chose comme ceci :
DT[ , { cat("Objets :", paste(objects(), collapse = ","), "\n") cat("Trace : x=", as.character(x), " y=", y, "\n") sum(y)}, by = x]
Above, x
est une variable de regroupement et (à partir de la version 1.6.1) a une longueur de 1 (si elle est inspectée ou utilisée dans j
). C'est pour des raisons d'efficacité et de commodité. Par conséquent, il n'y a pas de différence entre les deux déclarations suivantes :
DT[ , .(g = 1, h = 2, i = 3, j = 4, repeatgroupname = x, sum(y)), by = x] DT[ , .(g = 1, h = 2, i = 3, j = 4, repeatgroupname = x[1], sum(y)), by = x]
Si vous avez besoin de la taille du groupe actuel, utilisez .N
plutôt que d'appeler length()
sur n'importe quelle colonne.
Il se passe deux choses ici. Premièrement, si le nombre de lignes d'un data.table est important (> 100
par défaut), alors un résumé du data.table est imprimé sur la console par défaut. Deuxièmement, le résumé d'un grand data.table est imprimé en prenant les n
(= 5
par défaut) lignes du haut et du bas du data.table et en n'imprimant que celles-ci. Ces deux paramètres (quand déclencher un résumé et quelle partie du tableau utiliser comme résumé) sont configurables par le mécanisme options
de R, ou en appelant directement la fonction print
.
Par exemple, pour forcer le résumé (summary) d'un data.table à ne se produire que lorsqu'un data.table est supérieur à 50 lignes, vous pourriez faire options(datatable.print.nrows = 50)
. Pour désactiver complètement le résumé par défaut, vous pourriez faire options(datatable.print.nrows = Inf)
. Vous pouvez aussi appeler print
directement, comme dans print(your.data.table, nrows = Inf)
.
Si vous voulez afficher plus que les 10 premières (et dernières) lignes d'un tableau de données (disons 20), mettez options(datatable.print.topn = 20)
, par exemple. Encore une fois, vous pouvez aussi appeler directement print
, comme dans print(your.data.table, topn = 20)
.
X[Y]
, que se passe-t-il si X
contient une colonne appelée "Y"
?Lorsque i
est un nom unique tel que Y
, il est évalué dans d'appel. Dans tous les autres cas, comme les appels à .()
ou d'autres expressions, i
est évalué dans la portée de X
. Cela facilite les auto-joints comme X[J(unique(colA)), mult = "first"]
.
X[Z[Y]]
échoue parce que X
contient une colonne "Y"
. J'aimerais qu'il utilise la table Y
dans l’appel.La partie Z[Y]
n'est pas un nom unique, elle est donc évaluée dans le cadre de X
et le problème se produit. Essayez tmp = Z[Y] ; X[tmp]
. Ceci est robuste à X
contenant une colonne "tmp"
parce que tmp
est un nom unique. Si vous rencontrez souvent des conflits de ce type, une solution simple peut être de nommer toutes les tables en majuscules et tous les noms de colonnes en minuscules, ou un schéma similaire.
A[B]
de base
?Considérons la syntaxe A[B]
en utilisant un exemple de matrice A
:
A = matrix(1:12, nrow = 4) A
Pour obtenir les cellules (1, 2) = 5
et (3, 3) = 11
, de nombreux utilisateurs (nous pensons) peuvent d'abord essayer ceci :
A[c(1, 3), c(2, 3)]
Cependant, cette méthode renvoie l'union de ces lignes et de ces colonnes. Pour référencer les cellules, une matrice à 2 colonnes est nécessaire. ?Extract
dit :
Lors de l'indexation des tableaux par
[
, un seul argumenti
peut être une matrice avec autant de colonnes qu'il y a de dimensions dex
; le résultat est alors un vecteur avec des éléments correspondant aux ensembles d'indices dans chaque ligne dei
.
Essayons encore une fois.
B = cbind(c(1, 3), c(2, 3)) B A[B]
Une matrice est une structure à 2 dimensions avec des noms de lignes et de colonnes. Peut-on faire la même chose avec les noms ?
rownames(A) = letters[1:4] colnames(A) = LETTERS[1:3] A B = cbind(c("a", "c"), c("B", "C")) A[B]
Donc oui, nous pouvons le faire. Peut-on faire la même chose avec un data.frame
?
A = data.frame(A = 1:4, B = letters[11:14], C = pi*1:4) rownames(A) = letters[1:4] A B A[B]
Mais, remarquez que le résultat a été forcé en character.
R a forcé A
en matrix
d'abord pour que la syntaxe puisse fonctionner, mais le résultat n'est pas idéal. Essayons de faire de B
un data.frame
.
B = data.frame(c("a", "c"), c("B", "C")) cat(try(A[B], silent = TRUE))
Nous ne pouvons donc pas extraire un sous-ensemble d’un data.frame
par un data.frame
dans la base R. Que faire si nous voulons des noms de lignes et de colonnes qui ne sont pas des caractères
mais des integer
ou des float
? Que faire si nous voulons plus de 2 dimensions de types mixtes ? Entrez dans data.table.
En outre, les matrices, en particulier les matrices creuses, sont souvent stockées dans un tuple à trois colonnes : (i, j, valeur). Cela peut être considéré comme une paire clé-valeur où
iet
jforment une clé à 2 colonnes. Si nous avons plus d'une valeur, peut-être de types différents, cela peut ressembler à
(i, j, val1, val2, val3, ...). Cela ressemble beaucoup à un
data.frame. C'est pourquoi data.table étend
data.framede sorte qu'un
data.frameX
puisse extraire un sous-ensemble d’un
data.frameY
, ce qui conduit à la syntaxe
X[Y]`.
data.frame
est utilisé partout et il est donc très difficile d'y apporter un quelconque changement. data.table hérite de data.frame
. C'est aussi un data.frame
. Un data.table peut être passé à n'importe quel package qui seulement accepte data.frame
. Quand ce package utilise la syntaxe [.data.frame
sur le data.table, cela fonctionne. Cela fonctionne parce que [.data.table
regarde d'où il a été appelé. S'il a été appelé à partir d'un tel package, [.data.table
se dirige vers [.data.frame
.
Oui :
i
$\Leftrightarrow$ wherej
$\Leftrightarrow$ select:=
$\Leftrightarrow$ updateby
$\Leftrightarrow$ group byi
$\Leftrightarrow$ order by (en syntaxe composée)i
$\Leftrightarrow$ having (en syntaxe composée)nomatch = NA
$\Leftrightarrow$ outer joinnomatch = NULL
$\Leftrightarrow$ inner joinmult = "first"|"last"
$\Leftrightarrow$ N/A parce que SQL est intrinsèquement non ordonnéroll = TRUE
$\Leftrightarrow$ N/A parce que SQL est intrinsèquement non ordonnéLa forme générale est la suivante :
DT[where, select|update, group by][order by][...] ... [...]
L'un des principaux avantages des vecteurs colonnes dans R est qu'ils sont ordonnés, contrairement à SQL[^2]. Nous pouvons utiliser des fonctions ordonnées dans les requêtes data.table
telles que diff()
et nous pouvons utiliser n'importe quelle fonction R de n'importe quel package, pas seulement les fonctions qui sont définies dans SQL. L'inconvénient est que les objets R doivent tenir dans la mémoire, mais avec plusieurs packages R tels que ff
, bigmemory
, mmap
et indexing
, cela est en train de changer.
[^2]: Il peut être surprenant d'apprendre que select top 10 * from ...
ne renvoie pas de manière fiable les mêmes lignes dans le temps en SQL. Vous devez inclure une clause order by
, ou utiliser un index en grappe pour garantir l'ordre des lignes ; i.e., SQL est intrinsèquement non ordonné.
data.frame
et data.table {#SmallerDiffs}DT[3]
fait référence à la 3ème lignes, mais DF[3]
fait référence à la 3ème colonneDT[3, ] == DT[3]
, mais DF[ , 3] == DF[3]
(un peu déroutant dans data.frame, alors que data.table est cohérent)DT
, mais pas optionnelle dans DF
DT[[3]] == DF[, 3] == DF[[3]]
DT[i, ]
, où i
est un seul entier, renvoie une seule ligne, tout comme DF[i, ]
, mais contrairement à un sous-ensemble de matrice à une seule ligne qui renvoie un vecteur.DT[ , j]
où j
est un entier renvoie un data.table à une colonne, contrairement à DF[, j]
qui renvoie un vecteur par défautDT[ , "colA"][[1]] == DF[ , "colA"]
.DT[ , colA] == DF[ , "colA"]
(actuellement dans data.table v1.9.8 mais est sur le point de changer, voir les notes de version)DT[ , list(colA)] == DF[ , "colA", drop = FALSE]
DT[NA]
renvoie 1 ligne de NA
, mais DF[NA]
renvoie une copie entière de DF
contenant NA
tout au long. Le symbole NA
est de type logique
dans R et est donc recyclé par [.data.frame
. L'intention de l'utilisateur était probablement DF[NA_integer_]
. par commodité, `[.data.table' se réoriente automatiquement vers cette intention probable.DT[c(TRUE, NA, FALSE)]
traite le NA
comme FALSE
, mais DF[c(TRUE, NA, FALSE)]
renvoie===== lignes NA
pour chaque NA
===== - DT[ColA == ColB]
est plus simple que DF[!is.na(ColA) & !is.na(ColB) & ColA == ColB, ]
data.frame(list(1:2, "k", 1:4))
crée 3 colonnes, data.table crée une colonne list
.check.names
est par défaut TRUE
dans data.frame
mais FALSE
dans data.table, par commodité.data.table
a toujours mis stringsAsFactors=FALSE
par défaut. Dans R 4.0.0 (Apr 2020), la valeur par défaut de data.frame
a été changée de TRUE
à FALSE
et il n'y a plus de différence à cet égard ; voir stringsAsFactors, Kurt Hornik, Feb 2020.list
sont réduits lorsqu'ils sont imprimés en utilisant ", "
dans data.frame
, mais ","
dans data.table avec une virgule après le 6ème élément pour éviter l'impression accidentelle de gros objets intégrés.nrow(DF[, 0])
renvoie le nombre de lignes, tandis que nrow(DT[, 0])
renvoie toujours 0 ; mais voir le numéro #2422.Dans [.data.frame
, nous mettons très souvent drop = FALSE
. Lorsque nous l'oublions, des bogues peuvent apparaître dans les cas où une seule colonne est sélectionnée et où, tout à coup, un vecteur est retourné au lieu d'un data.frame
à une seule colonne. Dans [.data.table
, nous avons saisi l'opportunité de rendre les choses plus cohérentes et nous avons supprimé drop
.
Lorsqu'un data.table est transmis à un package ne prenant pas en compte data.table, ce package ne se préoccupe pas de ces différences ; il fonctionne simplement.
j
pour son effet secondaire uniquement, mais je reçois toujours des données en retour. Comment arrêter cela ?Dans ce cas, j
peut être entouré de invisible()
; par exemple, DT[ , invisible(hist(colB)), by = colA]
[^3]
[^3]: e.g., hist()
renvoie les points d'arrêt en plus du tracé sur le périphérique graphique.
[.data.table
a maintenant un argument drop
depuis la version v1.5 ?Ainsi, data.table peut hériter de data.frame
sans utiliser ...
. Si nous utilisions ...
, les noms d'arguments invalides ne seraient pas détectés.
L'argument drop
n'est jamais utilisé par [.data.table
. C'est un substitut pour les packages non compatibles avec data.table lorsqu'ils utilisent la syntaxe [.data.frame
directement sur un data.table.
La ligne dominante sur ou avant la ligne i
est la ligne finale que la recherche binaire teste de toute façon. Donc roll = TRUE
est essentiellement un interrupteur dans le code C de la recherche binaire pour retourner cette ligne.
DT[i, col := valeur]
retourne-t-il la totalité de DT
? Je m'attendais à ce qu'il n'y ait pas de valeur visible (ce qui est cohérent avec <-
), ou à ce qu'il y ait un message ou une valeur de retour contenant le nombre de lignes mises à jour. Il n'est pas évident que les données aient été mises à jour par référence.Ceci a été modifié dans la version 1.8.3 pour répondre à vos attentes. Veuillez mettre à jour.
L'ensemble de DT
est retourné (maintenant de manière invisible) pour que la syntaxe composée puisse fonctionner ; e.g., DT[i, done := TRUE][ , sum(done)]
. Le nombre de lignes mises à jour est retourné quand verbose
est TRUE
, soit sur une base par requête, soit globalement en utilisant options(datatable.verbose = TRUE)
.
DT[i, col := value]
soit renvoyé de façon invisible ?R force en interne la visibilité pour [
. La valeur de la colonne eval de FunTab (voir src/main/names.c) pour [
est 0
ce qui signifie "force R_Visible
on" (voir R-Internals section 1.6 ). Par conséquent, lorsque nous avons essayé invisible()
ou de mettre R_Visible
à 0
directement nous-mêmes, eval
dans src/main/eval.c l'a forcé à nouveau.
Pour résoudre ce problème, la clé était de ne plus essayer d'arrêter l'exécution de la méthode print après un :=
. Au lieu de cela, à l'intérieur de :=
nous mettons maintenant (à partir de la version 1.8.3) un drapeau global que la méthode print utilise pour savoir si elle doit imprimer ou non.
DT
parfois deux fois après avoir utilisé :=
pour imprimer le résultat dans la console ?C'est un inconvénient malheureux pour faire fonctionner #869. Si un :=
est utilisé à l'intérieur d'une fonction sans DT[]
avant la fin de la fonction, alors la prochaine fois que DT
est tapé à l'invite, rien ne sera affiché. Un DT
répété sera affiché. Pour éviter cela : incluez un DT[]
après le dernier :=
dans votre fonction. Si ce n'est pas possible (par exemple, ce n'est pas une fonction que vous pouvez changer), alors print(DT)
et DT[]
à l'invite sont garantis de s’afficher. Comme précédemment, l'ajout d'un []
supplémentaire à la fin de la requête :=
est un idiome recommandé pour mettre à jour et ensuite imprimer ; e.g.> DT[,foo:=3L][]
.
base::cbind.data.frame
(et base::rbind.data.frame
) semble être modifié par data.table. Comment cela est-il possible ? Pourquoi ?C'était une solution temporaire de dernier recours avant que le dispatching des méthodes S3 de rbind et cbind ne soit corrigé dans R >= 4.0.0. Essentiellement, le problème était que data.table
hérite de data.frame
, et base::cbind
et base::rbind
(uniquement) font leur propre dispatching S3 en interne comme documenté par ?cbind
. La solution pour data.table
était d'ajouter une boucle for
au début de chaque fonction directement dans base
. Cette modification était faite dynamiquement, c'est-à-dire que la définition base
de cbind.data.frame
était récupérée, la boucle for
ajoutée au début, et ensuite réassignée à base
. Cette solution a été conçue pour être robuste aux différentes définitions de base::cbind.data.frame
dans les différentes versions de R, y compris les changements futurs inconnus. Elle a bien fonctionné. Les exigences concurrentes étaient les suivantes :
cbind(DT, DF)
doit fonctionner. Définir cbind.data.table
ne fonctionnait pas parce que base::cbind
fait sa propre distribution S3 et requiert (avant R 4.0.0) que la première méthode cbind
pour chaque objet qui lui est passé soit identique. Ce n'est pas vrai dans cbind(DT, DF)
parce que la première méthode pour DT
est cbind.data.table
mais la première méthode pour DF
est cbind.data.frame
. base::cbind
passe alors à son code bind
interne qui semble traiter DT
comme une liste
normale et renvoie une sortie matrix
très bizarre et inutilisable. Voir ci-dessous. Nous ne pouvons pas simplement conseiller aux utilisateurs de ne pas appeler cbind(DT, DF)
parce que des packages comme ggplot2
font un tel appel (test 167.2).
Cela a naturellement conduit à essayer de masquer cbind.data.frame
à la place. Puisqu'une data.table est un data.frame
, cbind
trouverait la même méthode pour DT
et DF
. Cependant, cela n'a pas fonctionné non plus parce que base::cbind
semble trouver les méthodes dans base
en premier ; i.e., base::cbind.data.frame
n'est pas masquable.
Finalement, nous avons essayé de masquer cbind
lui-même (v1.6.5 et v1.6.6). Cela a permis à cbind(DT, DF)
de fonctionner, mais a introduit des problèmes de compatibilité avec le package IRanges
, puisque IRanges
masque aussi cbind
. Cela fonctionnait si IRanges
était plus bas dans le chemin search()
que data.table, mais si IRanges
était plus haut que data.table, cbind
n'était jamais appelé et l'étrange sortie matrix
se produisait à nouveau (voir ci-dessous).
Un grand merci à l'équipe de base de R pour avoir résolu le problème en septembre 2019. data.table v1.12.6+ n'applique plus la solution de contournement dans R >= 4.0.0.
merge
peut ou non être réparti dans merge.data.table
) mais comment R sait-il comment répartir ? Les points sont-ils significatifs ou spéciaux ? Comment diable R sait-il quelle fonction doit être distribuée et à quel moment ? {#r-dispatch}On en parle souvent, mais c'est d'une simplicité déconcertante. Une fonction telle que merge
est générique si elle consiste en un appel à UseMethod
. Quand vous voyez des gens parler de la question de savoir si les fonctions sont génériques ou non, ils tapent simplement la fonction sans ()
après, regardent le code du programme à l'intérieur et s'ils voient un appel à UseMethod
alors c'est générique. Que fait UseMethod
? Elle colle littéralement le nom de la fonction avec la classe du premier argument, séparés par un point (.
) et appelle ensuite cette fonction, en lui passant les mêmes arguments. C'est aussi simple que cela. Par exemple, merge(X, Y)
contient un appel à UseMethod
, ce qui signifie qu'il dispatche (c'est-à-dire appelle) paste("merge", class(X), sep = ".")
. Les fonctions avec des points dans leur nom peuvent ou non être des méthodes. Le point n'est pas vraiment pertinent, autre que le point est le séparateur utilisé par UseMethod
. Connaître ce contexte devrait maintenant permettre de comprendre pourquoi, par exemple, il est évident pour les utilisateurs de R que as.data.table.data.frame
est la méthode data.frame
pour la fonction générique as.data.table
. De plus, il peut être utile d'élucider que, oui, vous avez raison, il n'est pas évident à partir de son seul nom que ls.fit
n'est pas la méthode fit de la fonction générique ls
. Vous ne le savez qu'en tapant ls
(pas ls()
) et en observant qu'il n'y a pas un seul appel à UseMethod
.
Vous pouvez maintenant vous demander : où cela est-il documenté dans R ? Réponse : c'est assez clair, mais vous devez d'abord savoir qu'il faut chercher dans ?UseMethod
et ce fichier d'aide contient :
Lorsqu'une fonction appelant
UseMethod('fun')
est appliquée à un objet avec l'attribut de classec('first', 'second')
, le système recherche une fonction appeléefun.first
et, s'il la trouve, l'applique à l'objet. Si aucune fonction de ce type n'est trouvée, une fonction appeléefun.second
est essayée. Si aucun nom de classe ne produit une fonction appropriée, la fonctionfun.default
est utilisée, si elle existe, ou une erreur se produit.
Heureusement, une recherche internet sur "How does R method dispatch work" (à l'heure où j'écris ces lignes) renvoie la page d'aide ?UseMethod
dans les premiers liens. Certes, les autres liens sortent rapidement dans les subtilités de S3 vs S4, les génériques internes et ainsi de suite.
Cependant, des fonctionnalités telles que l'envoi S3 de base (coller le nom de la fonction avec le nom de la classe) sont la raison pour laquelle certains adeptes de R aiment R. C'est tellement simple. Aucune inscription ou signature compliquée n'est requise. Il n'y a pas grand chose à apprendre. Pour créer la méthode merge
pour data.table, tout ce qui était nécessaire, littéralement, était de créer une fonction appelée merge.data.table
.
Plusieurs raisons à cela :
j
et réalise qu'elle n'utilise pas les autres colonnes.key
sur une grande table, mais le regroupement est toujours très rapide. Comment cela se fait-il ?data.table utilise le tri par base (radix sort). Il est nettement plus rapide que les autres algorithmes de tri. Voir nos présentations pour plus d'informations, en particulier sur useR!2015 Danemark.
C'est aussi l'une des raisons pour lesquelles setkey()
est rapide.
Lorsqu'aucune "clé" (‘key’) n'est définie, ou que l'on regroupe dans un ordre différent de celui de la clé, on parle d'un "by" *ad hoc.
by
?Parce que chaque groupe est contigu en RAM, ce qui minimise les recherches de pages et que la mémoire peut être copiée en masse (memcpy
en C) plutôt qu'en boucle en C.
Manuel : ?setkey
S.O. : Quel est l'intérêt de définir une clé dans data.table ?
setkey(DT, col1, col2)
ordonne les lignes par la colonne col1
puis à l'intérieur de chaque groupe de col1
il ordonne par col2
. Il s'agit d'un index primaire. L'ordre des lignes est modifié par référence en RAM. Les jointures et les groupes ultérieurs sur ces colonnes clés profitent alors de l'ordre de tri pour plus d'efficacité. (Imaginez à quel point la recherche d'un numéro de téléphone dans un annuaire imprimé serait difficile s'il n'était pas trié par nom puis par prénom. C'est littéralement tout ce que fait setkey
. Il trie les lignes en fonction des colonnes que vous spécifiez) L'index n'utilise pas de RAM. Il change simplement l'ordre des lignes en RAM et marque les colonnes clés. Analogue à un index groupé en SQL.
Cependant, vous ne pouvez avoir qu'une seule clé primaire car les données ne peuvent être triées physiquement dans la mémoire vive que d'une seule manière à la fois. Choisissez l'index primaire comme étant celui que vous utilisez le plus souvent (par exemple [id,date]
). Parfois, il n'y a pas de choix évident pour la clé primaire ou vous devez joindre et grouper de nombreuses colonnes différentes dans des ordres différents. Entrez un index secondaire. Celui-ci utilise de la mémoire (4*nrow
bytes indépendamment du nombre de colonnes dans l'index) pour stocker l'ordre des lignes selon les colonnes que vous spécifiez, mais ne réordonne pas réellement les lignes en RAM. Les jointures et les groupes ultérieurs profitent de l'ordre de la clé secondaire mais doivent sauter via cet index et ne sont donc pas aussi efficaces que les index primaires. Ils sont donc moins efficaces que les index primaires. Mais ils sont tout de même beaucoup plus rapides qu'un balayage vectoriel complet. Il n'y a pas de limite au nombre d'index secondaires puisque chacun d'entre eux est simplement un vecteur d'ordre différent. En général, il n'est pas nécessaire de créer des index secondaires. Ils sont créés automatiquement et utilisés pour vous automatiquement en utilisant data.table normalement ; e.g. DT[someCol == someVal, ]
et DT[someCol %in% someVals, ]
créeront, attacheront et utiliseront ensuite l'index secondaire. Ceci est plus rapide dans data.table qu'un balayage vectoriel, donc l'indexation automatique est activée par défaut puisqu'il n'y a pas de pénalité initiale. Il existe une option pour désactiver l'indexation automatique ; e.g., si beaucoup d'index sont créés et que même la quantité relativement faible de mémoire supplémentaire devient trop importante.
Nous utilisons les mots index et key de manière interchangeable.
DT
»MySum = sum(v)
) »Cette erreur est générée par DT[ , MySum = sum(v)]
. DT[ , .(MySum = sum(v))]
était prévu, ou DT[ , j = .(MySum = sum(v))]
.
translateCharUTF8
doit être appelé sur un CHARSXP
"Cette erreur (et d'autres similaires, e.g., "getCharCE
must be called on a CHARSXP
") peut n'avoir rien à voir avec les données de caractères ou la locale. Au lieu de cela, cela peut être le symptôme d'une corruption de mémoire antérieure. Jusqu'à présent, ces problèmes ont pu être reproduits et corrigés (rapidement). Merci de le signaler sur notre gestionnaire de tickets (issues tracker).
cbind(DT, DF)
renvoie un format étrange, e.g. Integer,5
{#cbinderror}Cela se produit avant la version 1.6.5, pour rbind(DT, DF)
également. Veuillez mettre à jour vers la version 1.6.7 ou une version ultérieure.
.SD
».SD
est verrouillé par conception. Voir ?data.table
. Si vous voulez manipuler .SD
avant de l'utiliser ou de le retourner, et que vous ne souhaitez pas modifier DT
en utilisant :=
, prenez d'abord une copie (voir ?copy
), e.g.,
DT = data.table(a = rep(1:3, 1:3), b = 1:6, c = 7:12) DT DT[ , { mySD = copy(.SD) mySD[1, b := 99L] mySD}, by = a]
.N
»Veuillez mettre à jour vers la version 1.8.1 ou plus récente. A partir de cette version, si .N
est retourné par j
, il est renommé en N
pour éviter toute ambiguïté dans un regroupement ultérieur entre la variable spéciale .N
et une colonne appelée ".N"
.
L'ancien comportement peut être reproduit en forçant .N
à s'appeler .N
, comme ceci :
DT = data.table(a = c(1,1,2,2,2), b = c(1,2,2,2,1)) DT DT[ , list(.N = .N), list(a, b)] # montrer le résultat intermédiaire pour l'exposition cat(try( DT[ , list(.N = .N), by = list(a, b)][ , unique(.N), by = a] # composer une requête plus typique , silent = TRUE))
Si vous utilisez déjà la version 1.8.1 ou une version ultérieure, le message d'erreur est plus utile que l'erreur « Impossible de modifier la valeur d’une liaison verrouillée », comme vous pouvez le voir ci-dessus, puisque cette vignette a été produite avec la version 1.8.1 ou une version ultérieure.
La syntaxe plus naturelle fonctionne désormais :
if (packageVersion("data.table") >= "1.8.1") { DT[ , .N, by = list(a, b)][ , unique(N), by = a] } if (packageVersion("data.table") >= "1.9.3") { DT[ , .N, by = .(a, b)][ , unique(N), by = a] # same }
package:base
: cbind
, rbind
»Cet avertissement était présent dans les versions 1.6.5 et 1.6.6 uniquement, lors du chargement du package. La motivation était de permettre à cbind(DT, DF)
de fonctionner, mais il s'est avéré que cela rompait la compatibilité (totale) avec le package IRanges
. Veuillez mettre à jour vers la version 1.6.7 ou une version ultérieure.
J'espère que ce message s'explique de lui-même. Le message complet est le suivant :
RHS numérique forcé en entier pour correspondre au type de la colonne ; peut avoir une précision tronquée. Vous pouvez soit changer la colonne en numérique en créant vous-même un nouveau tableau numérique de longueur 5 (nrows du tableau entier) et en l'assignant (c.-à-d. colonne "replace"), soit forcer vous-même le RHS en entier (par ex. 1L ou as.integer) pour que votre intention soit claire (et pour plus de rapidité). Ou encore, définissez correctement le type de colonne dès la création de la table et respectez-le, s'il vous plaît.
Pour le générer, essayez :
DT = data.table(a = 1:5, b = 1:5) suppressWarnings( DT[2, b := 6] # fonctionne (plus lentement) avec l'avertissement ) class(6) # numérique pas entier DT[2, b := 7L] # fonctionne (plus rapidement) sans avertissement class(7L) # L en fait un entier DT[ , b := rnorm(5)] # « remplace » la colonne entière par une colonne numérique
*.RDS
et *.RData
sont des types de fichiers qui permettent de stocker efficacement des objets R en mémoire sur le disque. Cependant, le stockage de data.table dans le fichier binaire perd sa sur-allocation de colonnes. Ce n'est pas très grave -- votre data.table sera copié en mémoire lors de la prochaine opération par référence et lancera un avertissement. Il est donc recommandé d'appeler setalloccol()
sur chaque data.table chargée avec les appels readRDS()
ou load()
.
C'est exact. La version 1.3 n'était disponible que sur R-Forge. Il y a eu plusieurs changements importants en interne et il a fallu du temps pour les tester en développement.
Pas actuellement.
Oui, à la fois pour 32-bit et 64-bit sur toutes les plateformes. Merci au CRAN. Aucune bibliothèque spéciale ou spécifique au système d'exploitation n'est utilisée.
Veuillez déposer des suggestions, des rapports de bogues et des demandes d'amélioration sur notre gestionnaire de tickets (issues tracker). Cela permet d'améliorer le package.
Merci d'ajouter le package sur GitHub. Cela permet d'encourager les développeurs et d'aider les autres utilisateurs de R à trouver le package.
Vous pouvez soumettre des demandes d'extraction pour modifier le code et/ou la documentation vous-même ; voir nos Directives de contribution.
Nous ajoutons tous les articles dont nous avons connaissance (qu'ils soient positifs ou négatifs) à la page Articles. Toutes les pages du wiki du projet sur GitHub sont en accès libre sans restriction de modification. N'hésitez pas à écrire un article, à faire un lien vers un article négatif écrit par quelqu'un d'autre que vous avez trouvé, ou à ajouter une nouvelle page à notre wiki pour recueillir vos critiques. Veillez à ce qu'elles soient constructives afin que nous ayons une chance de nous améliorer.
Veuillez consulter le guide d'assistance sur la page d'accueil du projet, qui contient des liens actualisés.
La page d'accueil contient des liens vers les archives en plusieurs formats.
Bien sûr, mais il est plus probable que vous obteniez une réponse plus rapide sur la page Issues ou sur Stack Overflow. De plus, le fait de poser des questions publiquement à ces endroits aide à construire la base de connaissances générale.
data.frame
fonctionne ?Voir cette réponse.
setDTthreads(.old.th)
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.