使用:=在组内添加函数中的多个列到data.table - 不指定LHS

时间:2013-12-21 01:19:17

标签: r grouping data.table

我有一个data.table,其中包含各种时间序列作为列,我想添加滞后,差异和可能的某些计算列。每个grp都有固定的行数,并且在此grp中有许多data.table块堆叠在一起。这是一个简单的数据示例。

    grp period          y1         y2
 1:   a      1  0.96594315  0.2395888
 2:   a      2 -0.18635737  1.1289055
 3:   a      3  1.78466563  3.5153819
 4:   a      4 -0.53847076  1.6550108
 5:   a      5 -1.10865388 -0.4006585
 6:   b      1 -0.05804824 -0.3389047
 7:   b      2 -1.38495552  1.1148454
 8:   b      3 -0.21258003 -0.6976163
 9:   b      4 -0.79988363  0.4506847
10:   b      5  1.30752737 -0.9655459

我有一个函数,当它传递数据块时,会为单个grp生成所有附加列。现在我想为每个grp执行此操作(使用函数!),而不必在:=的LHS上指定新的列名,而无需将现有列复制回函数和无需创建新的data.table可重现的示例如下所示。

dt = data.table(grp = rep(letters[1:2], each=5), 
                period = rep(1:5,2), 
                y1 = rnorm(10),
                y2 = rnorm(10))
my.fun = function(dt.sub) {
    y1.lag = c(NA, dt.sub$y1[1:(nrow(dt.sub)-1)])
    y2.lag = c(NA, dt.sub$y2[1:(nrow(dt.sub)-1)])
    y1y2 = dt.sub$y1 * dt.sub$y2
    list(y1.lag = y1.lag, y2.lag = y2.lag, y1y2 = y1y2) #
}

以下代码完全产生了我所追求的内容,即添加了列的原始数据,但我不想将结果复制到另一个data.table对象中,因为这会占用比我可用的内存更多的内存(是的,我需要转向64位运行!)。

编辑:我热衷于使用现有的功能,而不是硬编码j部分中的完整表达式。这是因为a)我可能想要更改动态返回的列,b)可读性 - 在实际示例中添加了大量列,c)可维护性 - 可能需要多个脚本才能执行此操作而是在函数中只修改一次。

dt = dt[,c(.SD, my.fun(.SD)),by=grp]    # the problem here is using dt =

所以我尝试使用:=(在反引号中)在现有对象中创建新列,但由于我没有指定LHS新列名,因此返回错误。鉴于上面的代码提供了我想要的而没有命名列(它们已经在list中),我无法理解为什么下面的代码失败。

dt[,`:=`(my.fun(.SD)),by=grp]

Error in `[.data.table`(dt, , `:=`(my.fun(.SD)), by = grp) : 
  In `:=`(col1=val1, col2=val2, ...) form, all arguments must be named.

显然这是一个玩具示例,我的数据是> 500k记录> 30列。感谢任何帮助,谢谢。

编辑:我后来发现了一个问题(R data.table grouping for lagged regression)来处理分组,roll来计算组内的滞后值。这并不能解决我的具体问题,但它确实提供了一种与手头的问题非常相关的不同方法。

2 个答案:

答案 0 :(得分:2)

当我尝试在RHS的列表中使用:=时,我的错误在控制台上发出了建议:help(":="),一旦我这样做,我就获得了启示:

help(":=")  # advice about  `multiple :=`

dt[ , `:=`(y1.lag = c(NA, head(y1,-1) ),
              y2.lag = c(NA, head(y2,-1) ),
              y1y2 = y1*y2) ,by=grp]
#------------    
dt
    grp period         y1          y2     y1.lag      y2.lag        y1y2
 1:   a      1 -0.2127395  1.33549660         NA          NA -0.28411285
 2:   a      2 -2.2005742 -0.07679158 -0.2127395  1.33549660  0.16898556
 3:   a      3  0.3857444 -0.47996397 -2.2005742 -0.07679158 -0.18514341
 4:   a      4 -1.5117554  0.50728778  0.3857444 -0.47996397 -0.76689506
 5:   a      5  1.7713902 -0.03092824 -1.5117554  0.50728778 -0.05478598
 6:   b      1  0.5033163  0.69815100         NA          NA  0.35139079
 7:   b      2  0.1125835 -2.19959623  0.5033163  0.69815100 -0.24763815
 8:   b      3  1.0252230 -1.76477546  0.1125835 -2.19959623 -1.80928832
 9:   b      4 -0.5484611 -1.35167910  1.0252230 -1.76477546  0.74134341
10:   b      5  1.3801637  0.67293665 -0.5484611 -1.35167910  0.92876276

:=作为一个像do.call这样的pairlist工作的函数调用lag.y1.y2.expr = expression(`:=`( y1.lag = c(NA, head(y1, -1) ), y2.lag = c(NA, head(y2, -1)), y1y2 = y1 * y2 ) ) dt[, eval( lag.y1.y2.expr ), , by='grp' ] 可能会有一个常规列表。 [编辑}要解决滞后规范“外化”但仍编码以处理特定列的请求:

my.expr =  substitute(`:=`(
                          y1.lag = c(NA, head(X1, -1) ),
                          y2.lag = c(NA, head(X2, -1) ),
                          y1y2 = y1 * y2 
                           ) ,
                      list(X1=quote(y1),X2=quote(y2) ) )

dt[, eval(my.expr), , by='grp' ]

我认为这与您的代码相比没有任何缺陷,因为您的代码不允许(甚至暗示)对列名进行编程替换。如果你想要一个更易于维护的安排,只有一个入口点可以修改列的名称,这也会成功:

bquote

我怀疑你可以期待使用{{1}}成功,这有时简化了使用R表达式对象。

答案 1 :(得分:1)

与IShouldBuyABoat的答案结果相同,尝试:

data.table(dt, dt[, my.fun(.SD), by = grp][, grp := NULL])
   grp period       y1       y2   y1.lag  y2.lag      y1y2
1:   a      1  1.36677 -0.81025       NA      NA -1.107425
2:   a      2  0.43528  1.04277  1.36677 -0.8102  0.453895
3:   a      3 -1.40229  0.66223  0.43528  1.0428 -0.928633
4:   a      4  1.43362  0.10293 -1.40229  0.6622  0.147560
5:   a      5  0.46713  0.72508  1.43362  0.1029  0.338705
6:   b      1 -0.04418 -0.20014       NA      NA  0.008843
7:   b      2  1.32390  0.19651 -0.04418 -0.2001  0.260160
8:   b      3 -0.82543  1.11483  1.32390  0.1965 -0.920215
9:   b      4 -1.26415  0.53213 -0.82543  1.1148 -0.672698
10:  b      5  0.14549  0.04128 -1.26415  0.5321  0.006005

注意:您可以使用.SDcols指定传递给my.fun的列。 [, grp := NULL]来压制重复的grp列。