Подтвердить что ты не робот

Применение нескольких функций к нескольким столбцам в 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?

4b9b3361

Ответ 1

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

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 

Ответ 2

Это немного неуклюже, но делает работу с 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

Ответ 3

Другие ответы показывают, как это сделать, но никто не удосужился объяснить основной принцип. Основное правило заключается в том, что элементы списков, возвращаемые выражениями 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

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

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

library(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

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

Ответ 4

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

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.

Ответ 5

Это может быть немного перегружено, но если вы пришли из dplyr sumrize_at 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

Создано в 2019-07-03 пакетом представлением (v0.2.0).