【问题标题】:More efficient rolling join backwards not forwards?更有效的滚动加入向后而不是向前?
【发布时间】:2026-01-06 10:45:01
【问题描述】:

data.table 实现 asof(也称为 rollingLOCF)开箱即用。我发现了这个相关的问题:

Filling in missing (blanks) in a data table, per category - backwards and forwards

但该问题的数据中有 NA。就我而言,我遵循那里的建议以保持数据不规则并使用roll=TRUE 加入它。我想做的不是将最后一个观察结果向前推进,而是尽可能有效地向后进行下一个观察。

这是我尝试过的,首先使用time:=-time 尝试欺骗它。我可以做得更好吗?我可以做得更快吗?

llorJoin <- function(A,B){
    B <- copy(B);
    keys <- key(A);
    if( !identical(key(A), key(B)) | is.null(keys) ){
       stop("llorJoin::ERROR; A and B should have the same non-empty keys");
    }

    lastKey <- tail(keys,1L);
    myStr <- parse(text=paste0(lastKey,":=-as.numeric(",lastKey,")"));
    A <- A[,eval(myStr)]; setkeyv(A,keys);
    B <- B[,eval(myStr)]; setkeyv(B,keys);

    origin <- "1970-01-01 00:00.00 UTC";
    A <- B[A,roll=T];
    myStr2 <- parse(text=paste0(lastKey,":=as.POSIXct(-",lastKey,",origin=origin)"));
    A <- A[,eval(myStr2)]; setkeyv(A,keys);
    return(A);
}

library(data.table)
A <- data.table(time=as.POSIXct(c("10:01:01","10:01:02","10:01:04","10:01:05","10:01:02","10:01:01","10:01:01"),format="%H:%M:%S"),
                b=c("a","a","a","a","b","c","c"),
                d=c(1,1.9,2,1.8,5,4.1,4.2));
B <- data.table(time=as.POSIXct(c("10:01:01","10:01:03","10:01:00","10:01:01"),format="%H:%M:%S"),b=c("a","a","c","d"), e=c(1L,2L,3L,4L));
setkey(A,b,time)
setkey(B,b,time)

library(rbenchmark)
benchmark(llorJoin(A,B),B[A,roll=T],replications=10)
            test replications elapsed relative user.self sys.self user.child sys.child
1 llorJoin(A, B)           10   0.045        1     0.048        0          0         0
2 B[A, roll = T]           10   0.009        1     0.008        0          0         0

   b                time  e   d
1: a 2013-01-12 09:01:01  1 1.0
2: a 2013-01-12 09:01:02  2 1.9
3: a 2013-01-12 09:01:04 NA 2.0
4: a 2013-01-12 09:01:05 NA 1.8
5: b 2013-01-12 09:01:02 NA 5.0
6: c 2013-01-12 09:01:01 NA 4.1
7: c 2013-01-12 09:01:01 NA 4.2

因此,作为比较,初始数据上的 asof 连接要快 5 倍。

【问题讨论】:

  • 您的问题具体是什么?你说你想要最后一次观察而不是locf,但你已经有了一个实现。它有效吗?是不是太慢了?你在寻找更惯用的东西吗?我对data.table 包一无所知,所以无论如何我都帮不上忙,但是对于您当前形式的问题,那些确实使用data.table 的潜在回答者可能和我一样困惑。
  • 问题是B[A,roll=T]AB必须排序。然后反转time:=-time 会破坏排序。我试图争取时间删除setkey 排序,然后按A[,time:=time[.N:.1],by="b"] 对“我自己”进行排序并使用setattr(A,c("b","time")),但它似乎更长......
  • @statquant * 的重点是提出明确的问题,产生有用的答案,以便其他人受益。我查看了您的问题,您确实有冗长的倾向;这不是英语的问题,而是心理组织的问题。 This one 是一个不错的问题,this one 发现了一个错误。所以这是有用的东西,但请记住简洁很重要!

标签: r join data.table


【解决方案1】:

roll 参数很久以前就可以执行nocb了。更新此答案,以便 #615 可以关闭。

您也不再需要设置密钥。相反,您可以使用on= 参数(在v1.9.6 中实现)指定要加入的列。有了这两个特性,任务就可以完成如下:

require(data.table) # v1.9.6+
A[B, on=c("b", "time"), roll=-Inf]
#                   time b  e   d
# 1: 2015-10-11 10:01:01 a  1 1.0
# 2: 2015-10-11 10:01:02 a  2 1.9
# 3: 2015-10-11 10:01:04 a NA 2.0
# 4: 2015-10-11 10:01:05 a NA 1.8
# 5: 2015-10-11 10:01:02 b NA 5.0
# 6: 2015-10-11 10:01:01 c NA 4.1
# 7: 2015-10-11 10:01:01 c NA 4.2

就是这样。


在不更改 data.table 的情况下,您已经非常接近最快的方式了。以下功能请求已在一段时间前提交:

FR#2300 Add backwards and firstback to roll=TRUE

我已经添加了一个链接回到这个问题。您可以在 R-Forge 上搜索功能请求列表。在这种情况下,像“roll”、“forwards”和“backwards”这样的词都可以找到。您可能需要 4 或 5 次尝试搜索来确认错误或功能请求尚未提交。

实现该功能请求(仅需要内部几行代码)对我来说可能比尝试为您提供最快的解决方法要快。

【讨论】:

  • 谢谢 Matthew,您是升级功能请求的优先级还是保持原样?我可以建议将roll=T 更改为roll={"locf","nocb"}(下一个观察结果向后)
  • roll=-30(下一次观察提供的差异不超过 30 天,可能是几天),roll=+30(locf 提供它是在 30 天前)的某个地方有一个想法。所以当前的TRUE 将对应于+Inf。和FALSE0-Inf 是 nocb。你怎么看?
  • 我觉得很棒,它增加了“窗口”加入的附加功能!
最近更新 更多