1
votes

Une alternative à mutate_if dans data.table

J'ai beaucoup utilisé le tidyverse, mais pour certains projets, j'ai besoin de la vitesse de data.table. Jusqu'à présent, je comprends la plupart de la syntaxe DT, mais je souhaite supprimer les niveaux inutilisés dans data.table sans utiliser mutate_if .

Avec dplyr je peux utiliser mutate_if (dataframe, is.factor, droplevels) et c'est tout. Cependant, je ne trouve pas de solution avec data.table.

J'ai essayé d'appliquer cette réponse en utilisant dataframe [ (.SD): = droplevels (.SD), .SDcols = sapply (dataframe, is. factor)]

Il renvoie l'erreur suivante: Erreur dans [.data.table (DT_, : = ((.SD), droplevels (.SD)), .SDcols = sapply (DT_,: LHS de: = n'est pas des noms de colonnes ('caractère') ou des positions ('entier' ou 'numérique') .

Je m'attends à avoir le même résultat que dans mutate_if sans utiliser le tidyverse.

UPDATE

J'ai accepté G. Réponse de Grothendieck parce que le code était plus comme je m'y attendais.

L'exemple qu'il a utilisé était celui-ci:

> DT1[, C]
 [1] Q E A J D R Z O G V
Levels: A D E G J O Q R V Z
> DT1[, D]
 [1] Y E N T R O C I D Z
Levels: C D E I N O R T Y Z

Les données que j'ai utilisées pour cet exemple étaient les suivantes:

# with base
DT1 = droplevels(DT1)

# or by reference
DT1[, (names(DT1)) := droplevels(.SD)]


3 commentaires

Jetez un œil au tableau .express , il existe un équivalent de mutate_if (montré dans les exemples ici).


Pourriez-vous montrer un exemple de vos données ainsi que votre résultat attendu?


BTW, c'est probablement un doublon de ceci , mais celui-ci n'est pas accepté comme réponse.


5 Réponses :



2
votes

Et ça?

x <- data.table(
  x=sample(letters[1:5],10,rep=T), 
  y=factor(sample(letters[1:5],10,rep=T), levels=letters),
  w=factor(sample(letters[1:5],10,rep=T), levels=letters)
  )
factors <- colnames(x)[sapply(x, is.factor)]
lapply(factors, function(z) x[, eval(z):=droplevels(get(z))])


0 commentaires

5
votes

Ce n'est pas une solution data.table , mais cela peut être fait sans problème avec le rapply de la base R :

## data
data("iris")
## add dummy level
levels(iris$Species) <- c(levels(iris$Species), "dummy")
str(iris)
#> 'data.frame':    150 obs. of  5 variables:
#>  $ Sepal.Length: num  5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
#>  $ Sepal.Width : num  3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
#>  $ Petal.Length: num  1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
#>  $ Petal.Width : num  0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
#>  $ Species     : Factor w/ 4 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...

iris2 <- rapply(iris, f = droplevels, classes = "factor", how = "replace")
str(iris2)
#> 'data.frame':    150 obs. of  5 variables:
#>  $ Sepal.Length: num  5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
#>  $ Sepal.Width : num  3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
#>  $ Petal.Length: num  1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
#>  $ Petal.Width : num  0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
#>  $ Species     : Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...


0 commentaires

3
votes

Une autre option utilisant set()

Données d'entrée

DT[, levels(Species)]
#[1] "setosa"

Obtenir les noms de colonne qui sont des facteurs et les remplacer par référence p >

cols <- DT[, names(Filter(is.factor, .SD))]
for(j in cols) {
  set(DT, j = j, value = droplevels(DT[[j]]))
}
# could also be written as a one-liner - thanks to @MattSummersgill
# for(j in cols) set(DT, j = j, value = droplevels(DT[[j]]))

Donner

library(data.table)
DT <- as.data.table(iris)
DT[, Species := as.factor(Species)]
DT <- DT[Species == "setosa"]

DT[, levels(Species)]
#[1] "setosa"     "versicolor" "virginica"


1 commentaires

J'utilise également data.table :: set () de cette façon. Pour ce que ça vaut, je préfère faire la boucle sur une seule ligne si elle est inférieure à ~ 80 caractères. c'est-à-dire pour (j en cols) set (DT, j = j, value = droplevels (DT [[j]]))



5
votes

En utilisant les données de la note à la fin

library(data.table)
DT <- data.table(a = 1:5, 
                 b = factor(1:5, levels = 1:10), 
                 c = factor(6:10, levels = 1:10))

ou

wx <- which(sapply(DT, is.factor))
DT[, (wx) := lapply(.SD, droplevels), .SDcols = wx]

Vérifiez:

levels(DT$b)
## [1] "1" "2" "3" "4" "5"

levels(DT$c)
## [1] "6"  "7"  "8"  "9"  "10"


0 commentaires