我有一个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
来计算组内的滞后值。这并不能解决我的具体问题,但它确实提供了一种与手头的问题非常相关的不同方法。
答案 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
列。