R - 加快近似日期匹配.idata.frame?

Mat*_*att 7 r subset plyr data.table

我正在努力有效地在两个数据帧之间执行"关闭"日期匹配.这个问题探讨了idata.frameplyr包中使用的解决方案,但我也非常满意其他建议的解决方案.

这是两个数据框的非常简单的版本:

sampleticker<-data.frame(cbind(ticker=c("A","A","AA","AA"),
  date=c("2005-1-25","2005-03-30","2005-02-15","2005-04-21")))
sampleticker$date<-as.Date(sampleticker$date,format="%Y-%m-%d")

samplereport<-data.frame(cbind(ticker=c("A","A","A","AA","AA","AA"),
  rdate=c("2005-2-15","2005-03-15","2005-04-15",
  "2005-03-01","2005-04-20","2005-05-01")))
samplereport$rdate<-as.Date(samplereport$rdate,format="%Y-%m-%d")
Run Code Online (Sandbox Code Playgroud)

在实际数据中,sampleticker超过30,000行有40列,samplereport近300,000行有25列.

我想要做的是合并两个数据框,以便每一行sampleticker与最近的日期匹配组合,其中日期匹配samplereport发生在日期之后sampleticker.我在过去通过对股票代码字段进行简单合并,对升序进行排序,然后选择股票代码和日期的唯一组合来解决类似的问题.但是,由于此数据集的大小,合并会非常快速地爆发.

尽我所知,merge不允许这种近似匹配.我已经看到了一些使用的解决方案findInterval,但由于日期之间的距离会有所不同,我不确定我是否可以指定适用于所有行的间隔.

这里发表另一篇文章后,我编写了以下代码,用于adply每一行并执行连接:

library(plyr)
merge<-adply(sampleticker,1,function(x){
  y<-subset(samplereport,ticker %in% x$ticker & rdate > x$date)
  y[which.min(y$rdate),]
  }))
Run Code Online (Sandbox Code Playgroud)

这很好用:对于样本数据,我得到了下面的内容,这就是我想要的.

   date       ticker      rdate
 1 2005-01-25  A          2005-02-15
 2 2005-03-30  A          2005-04-15
 3 2005-02-15  AA         2005-03-01
 4 2005-04-21  AA         2005-05-01
Run Code Online (Sandbox Code Playgroud)

但是,由于代码执行了30,000多个子集操作,因此速度极慢:我在最终查杀之前运行了上述查询超过一天.

在这里看到plyr 1.0有一个结构,idata.frame它通过引用调用数据帧,大大加快了子集化操作.但是,我无法使用以下代码:

isamplereport<-idata.frame(samplereport)
adply(sampleticker,1,function(x){
  y<-subset(isamplereport,isamplereport$ticker %in% x$ticker & 
    isamplereport$rdate > x$date)
  y[which.min(y$rdate),]
})
Run Code Online (Sandbox Code Playgroud)

我收到了错误

Error in list_to_dataframe(res, attr(.data, "split_labels")) : 
Results must be all atomic, or all data frames
Run Code Online (Sandbox Code Playgroud)

这对我来说很有意义,因为操作返回idata.frame(我假设).但是,将最后一行更改为:

as.data.frame(y[which.min(y$rdate),]) 
Run Code Online (Sandbox Code Playgroud)

还会抛出一个错误:

Error in `[.data.frame`(x$`_data`, x$`_rows`, x$`_cols`) : 
undefined columns selected.
Run Code Online (Sandbox Code Playgroud)

请注意,调用as.data.frameplain old会samplereport返回原始数据框,如预期的那样.

我知道这idata.frame是实验性的,所以我不一定希望它能正常工作.但是,如果有人知道如何解决这个问题,我将不胜感激.或者,如果任何人都可以提出一种运行效率更高的完全不同的方法,那就太棒了.

马特

更新 Data.table是正确的方法.见下文.

Mat*_*att 8

感谢Matthew Dowle以及他在data.table中向前滚动和向前滚动的能力,现在执行此合并变得更加简单.

ST <- data.table(sampleticker)
SR <- data.table(samplereport)
setkey(ST,ticker,date)
SR[,mergerdate:=rdate]
setkey(SR,ticker,mergerdate)
merge<-SR[ST,roll=-Inf]
setnames(merge,"mergerdate","date")

#    ticker       date      rdate
# 1:      A 2005-01-25 2005-02-15
# 2:      A 2005-03-30 2005-04-15
# 3:     AA 2005-02-15 2005-03-01
# 4:     AA 2005-04-21 2005-05-01
Run Code Online (Sandbox Code Playgroud)


Jos*_*ien 6

这是一个data.table基于解决方案,可能比您目前使用的解决方案更好:

library(data.table)
ST <- data.table(sampleticker, key="ticker")
SR <- data.table(samplereport, key="ticker")
SR <- SR[with(SR, order(ticker, rdate)),] # rdates need to be in increasing order

SR[ST, list(date = date,
            rdate = rdate[match(TRUE, (rdate > date))]), ]
     ticker       date      rdate
[1,]      A 2005-01-25 2005-02-15
[2,]      A 2005-03-30 2005-04-15
[3,]     AA 2005-02-15 2005-03-01
[4,]     AA 2005-04-21 2005-05-01
Run Code Online (Sandbox Code Playgroud)

当然,听起来你真正想做的是将两个更广泛的data.frames合并在一起.为了演示实现这一点的一种方法,在下面的示例中,我向data.tables添加了一些列,然后展示了如何合并相应的行:

# Add some columns to both data.tables
ST$alpha <- letters[seq_len(nrow(ST))]
SR$n     <- seq_len(nrow(SR))
SR$ALPHA <- LETTERS[seq_len(nrow(SR))]

# Perform a merge that includes the whole rows from samplereport
# corresponding to the selected rdate
RES <- SR[ST, cbind(date, .SD[match(TRUE,(rdate>date)),-1]), ]

# Merge res (containing the selected rows from samplereport) back together
# with sampleticker
keycols <- c("ticker", "date")
setkeyv(RES, keycols)
setkeyv(ST, keycols)
ST[RES]
#      ticker       date alpha      rdate n ALPHA
# [1,]      A 2005-01-25     a 2005-02-15 1     A
# [2,]      A 2005-03-30     b 2005-04-15 3     C
# [3,]     AA 2005-02-15     c 2005-03-01 4     D
# [4,]     AA 2005-04-21     d 2005-05-01 6     F
Run Code Online (Sandbox Code Playgroud)