Применение нескольких функций к нескольким столбцам в data.table

Я пытаюсь применить несколько функций к нескольким столбцам data.table, Пример:

DT <- data.table("a"=1:5,
                 "b"=2:6,
                 "c"=3:7)

Допустим, я хочу получить среднее значение и медиану столбцов a а также b, Это работает:

stats <- DT[,.(mean_a=mean(a),
               median_a=median(a),
               mean_b=mean(b),
               median_b=median(b))]

Но это слишком повторяется. Есть ли хороший способ добиться аналогичного результата с помощью .SDcols а также lapply?

2 ответа

Решение

Я обычно делаю это:

my.summary = function(x) list(mean = mean(x), median = median(x))

DT[, unlist(lapply(.SD, my.summary)), .SDcols = c('a', 'b')]
#a.mean a.median   b.mean b.median 
#     3        3        4        4 

Другие ответы показывают, как это сделать, но никто не удосужился объяснить основной принцип. Основное правило заключается в том, что элементы списков возвращаются j выражения образуют столбцы результирующего data.table, любой j выражение, которое создает список, каждый элемент которого соответствует желаемому столбцу в результате, будет работать. Имея это в виду, мы можем использовать

DT[, c(mean = lapply(.SD, mean),
       median = lapply(.SD, median)),
  .SDcols = c('a', 'b')]
##    mean.a mean.b median.a median.b
## 1:      3      4        3        4

или же

DT[, unlist(lapply(.SD,
                   function(x) list(mean = mean(x),
                                    median = median(x))),
            recursive = FALSE),
   .SDcols = c('a', 'b')]
##    a.mean a.median b.mean b.median
## 1:      3        3      4        4

в зависимости от желаемого заказа.

Важно отметить, что мы можем использовать любой метод, который хотим получить желаемый результат, при условии, что мы упорядочим результат в виде списка, как описано выше. Например,

библиотека (matrixStats)

DT[, c(mean = as.list(colMeans(.SD)),
       median = setNames(as.list(colMedians(as.matrix(.SD))), names(.SD))),
   .SDcols = c('a', 'b')]
##    mean.a mean.b median.a median.b
## 1:      3      4        3        4

тоже работает.

Это немного неуклюже, но делает работу с data.table:

funcs = c('median', 'mean', 'sum')

m = DT[, lapply(.SD, function(u){
        sapply(funcs, function(f) do.call(f,list(u)))
     })][, t(.SD)]
colnames(m) = funcs

#  median mean sum
#a      3    3  15
#b      4    4  20
#c      5    5  25

Использовать дкаст

DT$dday <- 1 # add a constant column
dt <- dcast(DT, dday~dday, fun=list(sum, mean), value.var = c('a', 'b'))
# dday a_sum_1 b_sum_1 a_mean_1 b_mean_1
# 1      15      20        3        4

Фактически, мы можем использовать dcast для реализации onehot и Feature Engineer.

Это может быть немного перестроено, но если вы пришли из dplyr summarize_at() вы можете захотеть получить похожий структурированный результат.

Сначала определите функцию lapply_at() что требует .SDи вектор символов имен функций в качестве входных данных. Затем вы можете легко вычислить желаемую статистику и получить читаемый результат.

library(data.table)
iris_dt <- as.data.table(iris)

lapply_at <- function(var, funs, ...) {
  results <- sapply(var, function(var) {
    lapply(funs, do.call, list(var, ...))
  })
  names(results) <- vapply(names(var), paste, funs, sep = "_", 
                           FUN.VALUE = character(length(funs)),
                           USE.NAMES = FALSE)
  results
}

iris_dt[, lapply_at(.SD, c("mean", "sd"), na.rm = TRUE), 
        .SDcols = patterns("^Sepal"),
        by = Species]

#>       Species Sepal.Length_mean Sepal.Length_sd Sepal.Width_mean
#> 1:     setosa             5.006       0.3524897            3.428
#> 2: versicolor             5.936       0.5161711            2.770
#> 3:  virginica             6.588       0.6358796            2.974
#>    Sepal.Width_sd
#> 1:      0.3790644
#> 2:      0.3137983
#> 3:      0.3224966

Создано 03.07.2019 пакетом REPEX (v0.2.0).

Другие вопросы по тегам