Les fonctions tidyr ne semblent pas fonctionner avec la fonctionnalité data.table. Par exemple, je voudrais remplir les valeurs manquantes d'un grand data.table de la manière suivante :
#install.packages("data.table")
#install.packages("tidyr")
library(data.table)
library(tidyr)
dt <- data.table(
year = c(2015, NA, NA, NA),
trt = c("A", NA, "B", NA)
)
dt[, year := fill(year)]
dt[, c("year", "trt") := .(fill(year), fill(trt))]
mais le code ci-dessus renvoie l'erreur suivante :
# > dt[, year := fill(year)]
# Error in UseMethod("fill_") :
# no applicable method for 'fill_' applied to an object of class "c('double', 'numeric')"
# > dt[, c("year", "trt") := .(fill(year), fill(trt))]
# Error in UseMethod("fill_") :
# no applicable method for 'fill_' applied to an object of class "c('double', 'numeric')"
Étant donné que l'application séquentielle de fill()
à chaque colonne ne modifie pas en place, la méthode ci-dessous est coûteuse pour les grandes données.tables.
rm(dt)
dt <- data.table(
year = c(2015, NA, NA, NA),
trt = c("A", NA, "B", NA)
)
tracemem(dt)
dt <- fill(dt, year)
dt <- fill(dt, trt)
# > rm(dt)
# > dt <- data.table(
# + year = c(2015, NA, NA, NA),
# + trt = c("A", NA, "B", NA)
# + )
# > tracemem(dt)
# [1] "<00000000088F6118>"
# > dt <- fill(dt, year)
# tracemem[0x00000000088f6118 -> 0x00000000092d8c50]: fill_.data.frame fill_ fill
# tracemem[0x00000000092d8c50 -> 0x00000000092d8b00]: [[<-.data.frame [[<- fill_.data.frame fill_ fill
# tracemem[0x00000000092d8b00 -> 0x00000000092d8ac8]: [[<-.data.frame [[<- fill_.data.frame fill_ fill
# > dt <- fill(dt, trt)
# tracemem[0x00000000092d8ac8 -> 0x000000000951f1c0]: fill_.data.frame fill_ fill
# tracemem[0x000000000951f1c0 -> 0x000000000951f0a8]: [[<-.data.frame [[<- fill_.data.frame fill_ fill
# tracemem[0x000000000951f0a8 -> 0x000000000951f070]: [[<-.data.frame [[<- fill_.data.frame fill_ fill
# >
Merci d'avoir jeté un coup d'œil !
Je serais prêt à examiner les demandes d'extraction pour le faire, mais je n'ai pas le temps (ou la connaissance de data.table) pour le faire moi-même.
@mindymallory , Ce que vous décrivez n'est pas un problème avec la fonction tidyr
ou une nouvelle fonctionnalité à implémenter. C'est plutôt une question d'utilisation des fonctions tidyr
avec data.table
.
Il y a peu d'erreurs dans les tests que vous avez fait avec dt[, year := fill(year)]
fill
prend 2 arguments avec d'abord une donnée (data.frame, data.table ou tbl) puis un nom de colonne nue. Dans votre test, vous spécifiez le nom de la colonne comme premier argument afin que fill
ne fonctionne pas indépendamment de data.table
.fill
ne renvoie pas la colonne que vous spécifiez mais toutes les données sur lesquelles vous avez appliqué fill
. C'est pourquoi vous ne pouvez pas remplacer year
par le résultat de la fonction fill
dans dt[, year := fill(year)]
. Avec LHS:=RHS
, RHS
doit être un vecteur de valeurs de remplacement, ce qui n'est pas le cas dans year := fill(year)
.Ainsi, le code que vous avez essayé a ces problèmes indépendamment de la compatibilité de tidyr
avec data.table
.
Voici quelques façons d'utiliser fill
dans data.table
. Cela pourrait aussi aider d'autres utilisateurs.
library(data.table)
library(tidyr)
dt <- data.table(
year = c(2015, NA, NA, NA),
trt = c("A", NA, "B", NA)
)
tracemem(dt)
#> [1] "<0000000019B796C8>"
Si vous souhaitez utiliser fill
, vous devez d'abord spécifier une donnée puis une colonne. Dans un data.table
, utilisez .SD
pour cela, et voyez que fill
renvoie toutes les colonnes de dt
dt[, fill(.SD, year)]
#> year trt
#> 1: 2015 A
#> 2: 2015 NA
#> 3: 2015 B
#> 4: 2015 NA
Vous pouvez ensuite sélectionner une colonne avec $
par exemple, et elle renvoie un vecteur.
dt[, fill(.SD, year)$year]
#> [1] 2015 2015 2015 2015
Si vous donnez plusieurs colonnes à remplir, il renvoie ces colonnes remplies.
dt[, fill(.SD, year, trt)]
#> year trt
#> 1: 2015 A
#> 2: 2015 A
#> 3: 2015 B
#> 4: 2015 B
Alors, comment appliquer fill
pour remplacer les colonnes à l'intérieur d'un data.table
.
Vous pouvez extraire le vecteur de votre choix et utiliser :=
. Et recommencez pour l'autre colonne.
dt[, year := fill(.SD, year)$year]
dt
#> year trt
#> 1: 2015 A
#> 2: 2015 NA
#> 3: 2015 B
#> 4: 2015 NA
dt[, trt := fill(.SD, trt)$trt]
dt
#> year trt
#> 1: 2015 A
#> 2: 2015 A
#> 3: 2015 B
#> 4: 2015 B
vous pouvez également appliquer le changement immédiatement en utilisant la syntaxe data.table
.
dt <- data.table(
year = c(2015, NA, NA, NA),
trt = c("A", NA, "B", NA)
)
tracemem(dt)
#> [1] "<0000000006104428>"
dt[, c("year", "trt") := .(fill(.SD, year)$year, fill(.SD, trt)$trt)]
dt
#> year trt
#> 1: 2015 A
#> 2: 2015 A
#> 3: 2015 B
#> 4: 2015 B
Cependant, comme fill
renvoie toutes les colonnes de l'argument data
, vous pouvez vous passer d'extraire la colonne avec $
dt <- data.table(
year = c(2015, NA, NA, NA),
trt = c("A", NA, "B", NA)
)
tracemem(dt)
#> [1] "<0000000005B57B98>"
dt[, c("year", "trt") := fill(.SD, year, trt)]
dt
#> year trt
#> 1: 2015 A
#> 2: 2015 A
#> 3: 2015 B
#> 4: 2015 B
Enfin, vous pouvez utiliser une approche plus adaptée à la programmation.
dt <- data.table(
year = c(2015, NA, NA, NA),
trt = c("A", NA, "B", NA)
)
tracemem(dt)
#> [1] "<00000000187CF230>"
cols <- c("year", "trt")
dt
#> year trt
#> 1: 2015 A
#> 2: NA NA
#> 3: NA B
#> 4: NA NA
dt[, (cols) := fill_(.SD, cols), .SDcols = cols]
dt
#> year trt
#> 1: 2015 A
#> 2: 2015 A
#> 3: 2015 B
#> 4: 2015 B
Notez qu'en utilisant cette méthode, vous pouvez facilement appliquer fill
sur un sous-ensemble de colonnes
dt <- data.table(
year = c(2015, NA, NA, NA),
trt = c("A", NA, "B", NA),
trt2 = c(NA, "C", NA, "D")
)
tracemem(dt)
#> [1] "<00000000049EFD30>"
cols <- c("year", "trt")
dt
#> year trt trt2
#> 1: 2015 A NA
#> 2: NA NA C
#> 3: NA B NA
#> 4: NA NA D
dt[, (cols) := fill_(.SD, cols), .SDcols = cols]
dt
#> year trt trt2
#> 1: 2015 A NA
#> 2: 2015 A C
#> 3: 2015 B NA
#> 4: 2015 B D
J'espère que cela répondra à votre question et à la demande de fonctionnalité que vous souhaitez. Notez que j'utilise tracemem
pour montrer qu'il n'y a pas de copies et que tidyr
est compatible avec le concept data.table
, car la syntaxe et le concept data.table
sont utilisés correctement.
@hadley , je pense que vous n'aurez pas de relations publiques à revoir sur ce sujet.
@cderv Merci pour cette explication détaillée ! Vous m'avez économisé beaucoup d'utilisation de la mémoire !
Commentaire le plus utile
@mindymallory , Ce que vous décrivez n'est pas un problème avec la fonction
tidyr
ou une nouvelle fonctionnalité à implémenter. C'est plutôt une question d'utilisation des fonctionstidyr
avecdata.table
.Il y a peu d'erreurs dans les tests que vous avez fait avec
dt[, year := fill(year)]
fill
prend 2 arguments avec d'abord une donnée (data.frame, data.table ou tbl) puis un nom de colonne nue. Dans votre test, vous spécifiez le nom de la colonne comme premier argument afin quefill
ne fonctionne pas indépendamment dedata.table
.fill
ne renvoie pas la colonne que vous spécifiez mais toutes les données sur lesquelles vous avez appliquéfill
. C'est pourquoi vous ne pouvez pas remplaceryear
par le résultat de la fonctionfill
dansdt[, year := fill(year)]
. AvecLHS:=RHS
,RHS
doit être un vecteur de valeurs de remplacement, ce qui n'est pas le cas dansyear := fill(year)
.Ainsi, le code que vous avez essayé a ces problèmes indépendamment de la compatibilité de
tidyr
avecdata.table
.Voici quelques façons d'utiliser
fill
dansdata.table
. Cela pourrait aussi aider d'autres utilisateurs.Si vous souhaitez utiliser
fill
, vous devez d'abord spécifier une donnée puis une colonne. Dans undata.table
, utilisez.SD
pour cela, et voyez quefill
renvoie toutes les colonnes dedt
Vous pouvez ensuite sélectionner une colonne avec
$
par exemple, et elle renvoie un vecteur.Si vous donnez plusieurs colonnes à remplir, il renvoie ces colonnes remplies.
Alors, comment appliquer
fill
pour remplacer les colonnes à l'intérieur d'undata.table
.Vous pouvez extraire le vecteur de votre choix et utiliser
:=
. Et recommencez pour l'autre colonne.vous pouvez également appliquer le changement immédiatement en utilisant la syntaxe
data.table
.Cependant, comme
fill
renvoie toutes les colonnes de l'argumentdata
, vous pouvez vous passer d'extraire la colonne avec$
Enfin, vous pouvez utiliser une approche plus adaptée à la programmation.
Notez qu'en utilisant cette méthode, vous pouvez facilement appliquer
fill
sur un sous-ensemble de colonnesJ'espère que cela répondra à votre question et à la demande de fonctionnalité que vous souhaitez. Notez que j'utilise
tracemem
pour montrer qu'il n'y a pas de copies et quetidyr
est compatible avec le conceptdata.table
, car la syntaxe et le conceptdata.table
sont utilisés correctement.@hadley , je pense que vous n'aurez pas de relations publiques à revoir sur ce sujet.