Tidyr: Demande de fonctionnalité : faire en sorte que les fonctions de tidyr fonctionnent bien avec data.table

Créé le 4 mars 2016  ·  3Commentaires  ·  Source: tidyverse/tidyr

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 !

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 fonctions tidyr avec data.table .

Il y a peu d'erreurs dans les tests que vous avez fait avec dt[, year := fill(year)]

  • Tout d'abord, 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 .
  • Ensuite, 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.

Tous les 3 commentaires

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)]

  • Tout d'abord, 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 .
  • Ensuite, 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 !

Cette page vous a été utile?
0 / 5 - 0 notes