Я активно использую tidyverse, но для некоторых проектов мне нужна скорость data.table. Пока я понимаю большую часть синтаксиса DT, но я хочу удалить неиспользуемые уровни в data.table без использования mutate_if
.
С dplyr
я могу использовать mutate_if (dataframe, is.factor, droplevels)
и все. Однако я не могу найти способ с data.table.
Я пытался применить этот ответ, используя dataframe[, (.SD) := droplevels(.SD), .SDcols = sapply(dataframe, is.factor)]
Выдает следующую ошибку: Error in
[.data.table(DT_, ,
:=((.SD), droplevels(.SD)), .SDcols = sapply(DT_, :
LHS of := isn't column names ('character') or positions ('integer' or 'numeric')
.
Я ожидаю того же результата, что и в mutate_if
, без использования tidyverse.
ОБНОВИТЬ
Я принял ответ Г. Гротендика, потому что код больше походил на то, что я ожидал.
Пример, который он использовал, был следующим:
library(data.table)
DT <- data.table(a = 1:5,
b = factor(1:5, levels = 1:10),
c = factor(6:10, levels = 1:10))
Данные, которые я использовал для этого примера, были следующими:
set.seed(42)
DT1 = data.table(
A = LETTERS[1:10],
B = c(1:10),
C = factor(sample(LETTERS, 10), levels = LETTERS),
D = factor(sample(LETTERS, 10), levels = LETTERS)
)
Интересующие столбцы:
> DT1[, C]
[1] Q E A J D R Z O G V
Levels: A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
> DT1[, D]
[1] Y E N T R O C I D Z
Levels: A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
И результаты
# with base
DT1 = droplevels(DT1)
# or by reference
DT1[, (names(DT1)) := droplevels(.SD)]
Со следующим выводом:
> 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
Спасибо всем за ответы, это было быстро!
Не могли бы вы показать пример ваших данных, а также ожидаемый результат?
Кстати, это, вероятно, дубликат это, но он не принимается в качестве ответа.
Чтобы добавить к моему комментарию,
вы можете попробовать table.express
,
хотя примеры должны быть обновлены, потому что их можно упростить.
Вот один пример, эквивалентный mutate_if
:
library(data.table)
library(table.express)
data("iris")
DT <- as.data.table(iris)
DT %>%
start_expr %>%
mutate(Species = as.factor(Species)) %>%
mutate_sd(is.factor(.COL), droplevels) %>%
end_expr
Проверьте всю виньетку, хотя, некоторые глаголы нетерпеливы, а некоторые ленивы.
Как насчет этого?
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))])
Не data.table
решение, но это можно аккуратно сделать с помощью базы R rapply
:
## 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 ...
Другой вариант использования set()
Входные данные
library(data.table)
DT <- as.data.table(iris)
DT[, Species := as.factor(Species)]
DT <- DT[Species == "setosa"]
DT[, levels(Species)]
#[1] "setosa" "versicolor" "virginica"
Получите имена столбцов, которые являются факторами, и замените ссылкой
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]]))
Предоставление
DT[, levels(Species)]
#[1] "setosa"
Я тоже использую data.table::set()
таким образом. Что бы это ни стоило, я предпочитаю выполнять цикл в одной строке, если он меньше ~ 80 символов. то есть for(j in cols) set(DT, j = j, value = droplevels(DT[[j]]))
Используя данные в примечании в конце
DT[, (names(DT)) := droplevels(.SD)]
или
DT <- droplevels(DT)
Проверить:
levels(DT$b)
## [1] "1" "2" "3" "4" "5"
levels(DT$c)
## [1] "6" "7" "8" "9" "10"
Если droplevels
в вопросе — это просто пример, а реальная функция, которую вы используете, не имеет метода data.frame, тогда используйте код, соответствующий этому:
wx <- which(sapply(DT, is.factor))
DT[, (wx) := lapply(.SD, droplevels), .SDcols = wx]
library(data.table)
DT <- data.table(a = 1:5,
b = factor(1:5, levels = 1:10),
c = factor(6:10, levels = 1:10))
Упрощенный.
Взгляните на
table.express
, там есть эквивалентmutate_if
(показан в примерах).