R数据表分组用于滞后回归

13

下面是一个看起来像以下格式的数据表(它是一个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

可以创建如下:

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'))

当然,实际的表格要大得多,包含许多其他股票 ID 和日期。目的是将此数据表重新整形,以便我可以对所有股票 ID 的对数收益率与其前一天(在周末的情况下为前一个交易日)的对应对数收益率进行回归分析。

最终结果应如下所示:

      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

我发现这种数据结构真的很难建立,而不混淆我的股票ID。


2011-04-012011-04-04之间的时间差不是1天。 - Roland
1
setkey(stockid,date)。然后使用 :=rolldate-1 上添加滞后列。 然后按股票进行回归分析。 - Matt Dowle
嗨,Matthew,roll在哪个包中?我是R的新手,从Matlab转换过来。 - user1480926
我最接近的理解是这是一种自连接的方式:DT[DT,lagret:=log_return,roll=TRUE],它会使用接近纪元的日期填充lagret变量。我已经排除了一些显而易见的问题(例如,日期实际上是日期而不是字符串,键设置正确)。 - user1480926
1
嗯。尝试使用 DT[,lagret:=DT[list(id,date-1),logret,roll=TRUE][[3L]]]。您正在使用哪个版本的 data.table?最好提供一个完整可重现的示例 - 可以将其粘贴到 R 会话中。 - Matt Dowle
显示剩余6条评论
3个回答

21

根据Alex的评论,这里提供一些额外的注释。你难以理解这里发生了什么,是因为很多事情都在一行代码中完成。所以最好将它们分解成更小的部分。

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

DT[, lagret := xxx]

其中xxx必须填写您想要在lagret列中拥有的任何内容。因此,如果我们只想要一个给出行的新列,我们可以简单地调用:

DT[, lagret := seq(from=1, to=nrow(DT))]

这里实际上我们需要 logret 的滞后值,但是我们必须考虑到这里有很多股票。因此,我们进行自连接操作,即通过列 stock_iddate 将数据表 DT 与其自身连接,但是由于我们需要每只股票的前一个值,所以使用 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
...

如你所见,现在我们已经得到了想要的结果。 logret 现在滞后了一个周期。但实际上我们希望将其放在一个新的列 lagret 中,并且通过调用 [[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
...

这已经是正确的解决方案。在这种简单情况下,我们不需要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
...

正如您所见,问题现在是我们没有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
...

仅查看有关如何使用roll=TRUE的文档。简而言之:如果无法找到先前的值(这里是1月5日的logret),则会使用最后可用的值(这里是1月3日的值)。


+10。我自己也无法更好地解释了。[[3L]] 的丑陋(必须硬编码3L以忽略结果中的分组列)应该在实现FR#1757 Add drop to [.data.table时更快、更方便。 - Matt Dowle
1
使用[,logret]似乎比[[3L]]更直观。你不应该使用它吗?除了更直观之外,使用[,logret]还可以在将来更改列顺序而无需更改列号引用。 (当然,如果列名更改,则必须更新列名引用,但至少应该更明显...) - dnlbrky
1
好的观点,我只是使用了[[3L]],因为这是Matthew的原始提议,在我的回答中,我想解释一下它的工作原理。然而,我不确定你的选项是否总是有效。如果现在有效,那肯定是更清晰的语法,我同意。 - Christoph_J
非常出色的解释!谢谢! - Konstantinos
似乎在data.table中有些变化,因为这个答案好像不起作用了。看起来有一个错误——应该是DT[,lagret:=DT[list(stock_id,date-1),logret]]或者DT[,lagret:=DT[list(stock_id,date-1)][[3L]]],而不是DT[,lagret:=DT[list(stock_id,date-1),logret][[3L]]] - djas

4

更新:

在当前的data.table开发版本中,v1.9.5,实现了shift()函数#965,该函数目前支持两种类型:type = "lag"(默认)和type = "lead"。更多使用方法请参考?shift

有了这个函数,我们可以简单地执行以下操作:

# type="lag" may be omitted, as it is the default.
require(data.table) ## 1.9.5+
DT[, lagret := shift(logret, 1L, type="lag"), by=stock_id]
#           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
#  6: 2011-01-06        1  0.011  0.009
#  7: 2011-01-01        2  0.013     NA
#  8: 2011-01-02        2  0.015  0.013
#  9: 2011-01-03        2  0.017  0.015
# 10: 2011-01-04        2  0.019  0.017
# 11: 2011-01-05        2  0.021  0.019
# 12: 2011-01-06        2  0.023  0.021
# 13: 2011-01-01        3  0.025     NA
# 14: 2011-01-02        3  0.027  0.025
# 15: 2011-01-03        3  0.029  0.027
# 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

1
如果您的日期是定期间隔的(即它们没有“间隙”),那么这样做很好。接受的答案允许这种间隙的存在。 - JS1204
但是这种方法无法处理不规则时间序列,而且会得到错误的滞后值,对吗? - Matifou

2
感谢Matthew Dowle的建议,我能够使用以下内容:
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-04        1  0.007  0.005
 5: 2011-01-05        1  0.009  0.007
 6: 2011-01-06        1  0.011  0.009
 7: 2011-01-01        2  0.013     NA
 8: 2011-01-02        2  0.015  0.013
 9: 2011-01-03        2  0.017  0.015
10: 2011-01-04        2  0.019  0.017
11: 2011-01-05        2  0.021  0.019
12: 2011-01-06        2  0.023  0.021
13: 2011-01-01        3  0.025     NA
14: 2011-01-02        3  0.027  0.025
15: 2011-01-03        3  0.029  0.027
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

2
再次感谢@MatthewDowle,data.table是一款非常出色的软件,希望在我的研究中深入了解它后,能够添加更多未被充分记录的应用场景。对于编写这个软件和指导我们这些新手所做的努力,表示赞扬。 - user1480926
2
有人能否解释一下这个答案是如何工作的?我不太理解发生了什么。 - Alex

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接