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

Добавить новые столбцы в таблицу данных, содержащую множество переменных

Я хочу добавить много новых столбцов одновременно в data.table на основе вычислений по группам. Рабочий пример моих данных будет выглядеть примерно так:

     Time     Stock x1 x2 x3
1: 2014-08-22     A 15 27 34
2: 2014-08-23     A 39 44 29
3: 2014-08-24     A 20 50  5
4: 2014-08-22     B 42 22 43
5: 2014-08-23     B 44 45 12
6: 2014-08-24     B  3 21  2

Теперь я хочу scale и sum использовать многие переменные, чтобы получить такой вывод:

         Time Stock x1 x2 x3   x2_scale   x3_scale x2_sum x3_sum
1: 2014-08-22     A 15 27 34 -1.1175975  0.7310560    121     68
2: 2014-08-23     A 39 44 29  0.3073393  0.4085313    121     68
3: 2014-08-24     A 20 50  5  0.8102582 -1.1395873    121     68
4: 2014-08-22     B 42 22 43 -0.5401315  1.1226726     88     57
5: 2014-08-23     B 44 45 12  1.1539172 -0.3274462     88     57
6: 2014-08-24     B  3 21  2 -0.6137858 -0.7952265     88     57

Реализация моей проблемы с грубой силой будет следующей:

library(data.table)

set.seed(123)
d <- data.table(Time = rep(seq.Date( Sys.Date(), length=3, by="day" )),
                Stock = rep(LETTERS[1:2], each=3 ),
                x1 = sample(1:50, 6),
                x2 = sample(1:50, 6),
                x3 = sample(1:50, 6))

d[,x2_scale:=scale(x2),by=Stock]
d[,x3_scale:=scale(x3),by=Stock]
d[,x2_sum:=sum(x2),by=Stock]
d[,x3_sum:=sum(x3),by=Stock]

Другие сообщения, описывающие подобную проблему (Добавить несколько столбцов в R data.table в один вызов функции? и Назначить несколько столбцы с использованием: = в data.table, по группе) предлагают следующее решение:

  d[, c("x2_scale","x3_scale"):=list(scale(x2),scale(x3)), by=Stock]
  d[, c("x2_sum","x3_sum"):=list(sum(x2),sum(x3)), by=Stock]

Но опять-таки это будет очень грязно с множеством переменных, а также это приведет к сообщению об ошибке с scale (но не с sum, так как это не возвращает вектор).

Существует ли более эффективный способ достижения требуемого результата (имея в виду, что мой фактический набор данных довольно велик)?

4b9b3361

Ответ 1

Я думаю, что с небольшой модификацией вашего последнего кода вы можете легко сделать как для так много переменных, которые вы хотите

vars <- c("x2", "x3") # <- Choose the variable you want to operate on

d[, paste0(vars, "_", "scale") := lapply(.SD, function(x) scale(x)[, 1]), .SDcols = vars, by = Stock]
d[, paste0(vars, "_", "sum") := lapply(.SD, sum), .SDcols = vars, by = Stock]

##          Time Stock x1 x2 x3   x2_scale   x3_scale x2_sum x3_sum
## 1: 2014-08-22     A 13 14 32 -1.1338934  1.1323092     87     44
## 2: 2014-08-23     A 25 39  9  0.7559289 -0.3701780     87     44
## 3: 2014-08-24     A 18 34  3  0.3779645 -0.7621312     87     44
## 4: 2014-08-22     B 44  8  6 -0.4730162 -0.7258662     59     32
## 5: 2014-08-23     B 49  3 18 -0.6757374  1.1406469     59     32
## 6: 2014-08-24     B 15 48  8  1.1487535 -0.4147807     59     32

Для простых функций (которые не требуют специального лечения, например scale), вы можете легко сделать что-то вроде

vars <- c("x2", "x3") # <- Define the variable you want to operate on
funs <- c("min", "max", "mean", "sum") # <- define your function
for(i in funs){
  d[, paste0(vars, "_", i) := lapply(.SD, eval(i)), .SDcols = vars, by = Stock] 
}

Ответ 2

Другая вариация с использованием data.table

  vars <- c("x2", "x3")
  d[,  paste0(rep(vars, each=2), "_", c("scale", "sum")) := do.call(`cbind`,
               lapply(.SD, function(x) list(scale(x)[,1], sum(x)))), .SDcols=vars, by=Stock]
   d
   #        Time Stock x1 x2 x3   x2_scale x2_sum   x3_scale x3_sum
  #1: 2014-08-22     A 15 27 34 -1.1175975    121  0.7310560     68
  #2: 2014-08-23     A 39 44 29  0.3073393    121  0.4085313     68
  #3: 2014-08-24     A 20 50  5  0.8102582    121 -1.1395873     68
  #4: 2014-08-22     B 42 22 43 -0.5401315     88  1.1226726     57
  #5: 2014-08-23     B 44 45 12  1.1539172     88 -0.3274462     57
  #6: 2014-08-24     B  3 21  2 -0.6137858     88 -0.7952265     57

Основываясь на комментариях от @Arun, вы также можете:

   cols <- paste0(rep(vars, each=2), "_", c("scale", "sum"))
    d[,(cols):= unlist(lapply(.SD, function(x) list(scale(x)[,1L], sum(x))), 
                              rec=F), by=Stock, .SDcols=vars]

Ответ 3

Возможно, вы ищете чистое решение data.table, но вы также можете использовать dplyr здесь, так как он работает с data.table (нет необходимости в преобразовании). Затем из dplyr вы можете использовать функцию mutate_all, как я делаю в этом примере здесь (с первым набором данных, который вы указали в своем вопросе):

library(dplyr)
dt %>%
  group_by(Stock) %>%
  mutate_all(funs(sum, scale), x2, x3)
#Source: local data table [6 x 9]
#Groups: Stock
#
#        Time Stock x1 x2 x3 x2_sum x3_sum   x2_scale   x3_scale
#1 2014-08-22     A 15 27 34    121     68 -1.1175975  0.7310560
#2 2014-08-23     A 39 44 29    121     68  0.3073393  0.4085313
#3 2014-08-24     A 20 50  5    121     68  0.8102582 -1.1395873
#4 2014-08-22     B 42 22 43     88     57 -0.5401315  1.1226726
#5 2014-08-23     B 44 45 12     88     57  1.1539172 -0.3274462
#6 2014-08-24     B  3 21  2     88     57 -0.6137858 -0.7952265

Вы можете легко добавить больше функций, которые будут вычислены, что создаст вам больше столбцов. Обратите внимание, что mutate_all по умолчанию применяет функцию к каждому столбцу, кроме переменной группировки (запаса). Но вы можете указать столбцы, в которые вы хотите применить функции (что я сделал в этом примере), или вы можете указать, в каких столбцах вы не хотите применять функции (например, -c(x2,x3) вместо где я написал x2, x3).

EDIT: замените mutate_each выше на mutate_all, поскольку mutate_each будет устаревшим в ближайшем будущем.

Ответ 4

EDIT: более чистая версия с использованием functional. Я думаю, что это самое близкое к ответу dplyr.

library(functional)
funs <- list(scale=Compose(scale, c), sum=sum)    # See data.table issue #783 on github for the need for this
cols <- paste0("x", 2:3)
cols.all <- outer(cols, names(funs), paste, sep="_")

d[, 
  c(cols.all) := unlist(lapply(funs, Curry(lapply, X=.SD)), rec=F),
  .SDcols=cols,
  by=Stock
]

Выдает:

         Time Stock x1 x2 x3   x2_scale   x3_scale x2_sum x3_sum
1: 2014-08-22     A 15 27 34 -1.1175975  0.7310560    121     68
2: 2014-08-23     A 39 44 29  0.3073393  0.4085313    121     68
3: 2014-08-24     A 20 50  5  0.8102582 -1.1395873    121     68
4: 2014-08-22     B 42 22 43 -0.5401315  1.1226726     88     57
5: 2014-08-23     B 44 45 12  1.1539172 -0.3274462     88     57
6: 2014-08-24     B  3 21  2 -0.6137858 -0.7952265     88     57