将新列添加到包含许多变量的data.table

时间:2014-08-22 09:23:14

标签: r data.table

我想基于分组计算同时向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

现在我希望scalesum许多变量获得如下输出:

         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]

其他描述类似问题的帖子(Add multiple columns to R data.table in one function call?Assign multiple columns using := in data.table, by group)提出了以下解决方案:

  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,因为这不会返回向量)。 / p>

是否有更有效的方法来实现所需的结果(请记住我的实际数据集非常大)?

4 个答案:

答案 0 :(得分:17)

我认为通过对您上一个代码的一个小修改,您可以轻松地为所需的变量做两件事

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] 
}

答案 1 :(得分:4)

使用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]

答案 2 :(得分: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默认情况下将该函数应用于除分组变量(Stock)之外的每一列。但您可以指定您只想应用函数的列(我在此示例中执行过),或者您可以指定您要将函数应用于哪些列(将是,例如-c(x2,x3),而不是我写x2, x3)的地方。

编辑:将上面的mutate_each替换为mutate_allmutate_each将在不久的将来弃用。

答案 3 :(得分:2)

编辑:使用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