R data.table分组用于滞后回归

use*_*926 13 grouping r reshape data.table

带有数据的表(它是一个data.table对象),如下所示:

      date         stock_id logret
   1: 2011-01-01        1  0.001
   2: 2011-01-02        1  0.003
   3: 2011-01-03        1  0.005
   4: 2011-01-04        1  0.007
   5: 2011-01-05        1  0.009
   6: 2011-01-06        1  0.011
   7: 2011-01-01        2  0.013
   8: 2011-01-02        2  0.015
   9: 2011-01-03        2  0.017
  10: 2011-01-04        2  0.019
  11: 2011-01-05        2  0.021
  12: 2011-01-06        2  0.023
  13: 2011-01-01        3  0.025
  14: 2011-01-02        3  0.027
  15: 2011-01-03        3  0.029
  16: 2011-01-04        3  0.031
  17: 2011-01-05        3  0.033
  18: 2011-01-06        3  0.035
Run Code Online (Sandbox Code Playgroud)

以上可以创建为:

DT = data.table(
   date=rep(as.Date('2011-01-01')+0:5,3) , 
   stock_id=c(1,1,1,1,1,1,2,2,2,2,2,2,3,3,3,3,3,3),
  logret=seq(0.001, by=0.002, len=18));

setkeyv(DT,c('stock_id','date'))
Run Code Online (Sandbox Code Playgroud)

当然,真实的表格更大,有更多的stock_ids和日期.目的是重新整形这个数据表,以便我可以运行所有stockid log_returns及其对应的log_returns的回归,滞后为1天(或者在周末的情况下交易前一天).

最终结果如下:

      date         stock_id logret lagret
   1: 2011-01-01        1  0.001    NA
   2: 2011-01-02        1  0.003    0.001
   3: 2011-01-03        1  0.005    0.003
   ....
  16: 2011-01-04        3  0.031  0.029
  17: 2011-01-05        3  0.033  0.031
  18: 2011-01-06        3  0.035  0.033
Run Code Online (Sandbox Code Playgroud)

我发现这个数据结构非常难以构建而不会混淆我的stockid.

Chr*_*h_J 21

由于Alex的评论,还有一些额外的注释.你很难理解这里发生的事情是很多事情都是在一条线上完成的.因此,打破局面总是一个好主意.

我们真正想要的是什么?我们想要一个新列lagret和语法在data.table中添加一个新列如下:

DT[, lagret := xxx]
Run Code Online (Sandbox Code Playgroud)

哪里xxx必须填满你想要的任何列lagret.因此,如果我们只想要一个给我们行的新列,我们就可以调用

DT[, lagret := seq(from=1, to=nrow(DT))]
Run Code Online (Sandbox Code Playgroud)

在这里,我们实际上想要滞后值logret,但我们必须考虑到这里有很多股票.这就是为什么我们做一个自我联接,即我们加入data.table DT由柱与本身stock_iddate,但由于我们希望每个股票的前值,我们使用date-1.请注意,我们必须先设置密钥才能进行这样的连接:

setkeyv(DT,c('stock_id','date'))
DT[list(stock_id,date-1)]
    stock_id       date logret
 1:        1 2010-12-31     NA
 2:        1 2011-01-01  0.001
 3:        1 2011-01-02  0.003
 4:        1 2011-01-03  0.005
 5:        1 2011-01-04  0.007
 6:        1 2011-01-05  0.009
...
Run Code Online (Sandbox Code Playgroud)

如您所见,我们现在拥有我们想要的东西.logret现在落后一个时期.但是我们实际上想要在一个新的列lagretDT,所以我们只需通过调用[[3L]]来获取该列(这意味着没有别的东西,然后让我得到第三列)并命名这个新列lagret:

DT[,lagret:=DT[list(stock_id,date-1),logret][[3L]]]
          date stock_id logret lagret
 1: 2011-01-01        1  0.001     NA
 2: 2011-01-02        1  0.003  0.001
 3: 2011-01-03        1  0.005  0.003
 4: 2011-01-04        1  0.007  0.005
 5: 2011-01-05        1  0.009  0.007
...
Run Code Online (Sandbox Code Playgroud)

这已经是正确的解决方案.在这个简单的例子中,我们不需要roll=TRUE因为日期中没有空白.但是,在一个更现实的例子中(如上所述,例如当我们有周末时),可能存在差距.因此,让我们通过DT在第一个股票中删除两天来制作这样一个现实的例子:

DT <- DT[-c(4, 5)]
setkeyv(DT,c('stock_id','date'))
DT[,lagret:=DT[list(stock_id,date-1),logret][[3L]]]
          date stock_id logret lagret
 1: 2011-01-01        1  0.001     NA
 2: 2011-01-02        1  0.003  0.001
 3: 2011-01-03        1  0.005  0.003
 4: 2011-01-06        1  0.011     NA
 5: 2011-01-01        2  0.013     NA
...
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,现在的问题是我们没有1月6日的价值.这就是我们使用的原因roll=TRUE:

DT[,lagret:=DT[list(stock_id,date-1),logret,roll=TRUE][[3L]]]
          date stock_id logret lagret
 1: 2011-01-01        1  0.001     NA
 2: 2011-01-02        1  0.003  0.001
 3: 2011-01-03        1  0.005  0.003
 4: 2011-01-06        1  0.011  0.005
 5: 2011-01-01        2  0.013     NA
...
Run Code Online (Sandbox Code Playgroud)

只需查看有关roll=TRUE工作原理的文档.简而言之:如果它找不到之前的值(这里logret是1月5日),它只需要最后一个值(这里是从1月3日开始).