【问题标题】:Rolling join on data.table with duplicate keys使用重复键在 data.table 上滚动连接
【发布时间】:2015-11-07 12:51:34
【问题描述】:

我试图在data.table 中理解rolling joins。最后给出了重现这一点的数据。

给定一个机场的交易数据表,在给定的时间:

> dt
   t_id airport thisTime
1:    1       a      5.1
2:    3       a      5.1
3:    2       a      6.2  

(注意t_ids 1 & 3 有相同的机场和时间)

以及从机场出发的航班查询表:

> dt_lookup
   f_id airport thisTime
1:    1       a        6
2:    2       a        6
3:    1       b        7
4:    1       c        8
5:    2       d        7
6:    1       d        9
7:    2       e        8

> tables()
     NAME      NROW NCOL MB COLS                  KEY             
[1,] dt           3    3  1 t_id,airport,thisTime airport,thisTime
[2,] dt_lookup    7    3  1 f_id,airport,thisTime airport,thisTime

我想将所有交易与从该机场起飞的所有下一个可能航班匹配,以提供:

   t_id airport thisTime f_id
      1       a        6    1
      1       a        6    2
      3       a        6    1
      3       a        6    2

所以我认为这会起作用:

> dt[dt_lookup, nomatch=0,roll=Inf]
   t_id airport thisTime f_id
1:    3       a        6    1
2:    3       a        6    2

但它没有返回交易t_id == 1

来自the documentation 它说:

通常,x 的键中不应有重复项,...

但是,我的“x 键”(即airportthisTime)中确实有重复项,并且不能完全看到/理解发生了什么意味着t_id = 1 被从输出中删除。

谁能解释为什么t_id = 1 没有返回,当我有重复时如何让加入工作?

数据

library(data.table)
dt <- data.table(t_id = seq(1:3),
                 airport = c("a","a","a"),
                 thisTime = c(5.1,6.2, 5.1), key=c( "airport","thisTime"))

dt_lookup <- data.table(f_id = c(rep(1,4),rep(2,3)),
                        airport = c("a","b","c","d",
                                 "a","d","e"),
                        thisTime = c(6,7,8,9,
                                 6,7,8), key=c("airport","thisTime"))

【问题讨论】:

    标签: r join data.table


    【解决方案1】:

    t_id = 1 没有出现在输出中的原因是滚动连接获取最后出现组合键的行。从文档(强调我的):

    适用于最后一个连接列,通常是日期,但可以是任何日期 有序变量,不规则且包括间隙。如果 roll=TRUE 并且 i 是 行匹配除了最后一个 x 连接列之外的所有列,并且它的值在 最后我加入列落在一个空白处(包括在最后一个之后 该组在 x 中的观察值),则 x 中的主要值是 向前滚动。此操作使用修改后的速度特别快 二进制搜索。 该操作也称为最后观察携带 正向 (LOCF)。

    让我们考虑一些更大的数据集:

    > DT
       t_id airport thisTime
    1:    1       a      5.1
    2:    4       a      5.1
    3:    3       a      5.1
    4:    2       d      6.2
    5:    5       d      6.2
    
    > DT_LU
       f_id airport thisTime
    1:    1       a        6
    2:    2       a        6
    3:    2       a        8
    4:    1       b        7
    5:    1       c        8
    6:    2       d        7
    7:    1       d        9
    

    当您像在您的问题中一样执行滚动连接时:

    DT[DT_LU, nomatch=0, roll=Inf]
    

    你得到:

       t_id airport thisTime f_id
    1:    3       a        6    1
    2:    3       a        6    2
    3:    3       a        8    2
    4:    5       d        7    2
    5:    5       d        9    1
    

    如您所见,从组合键a, 5.1d, 6.2 中,最后一行用于连接数据表。因为您使用Inf 作为滚动值,所以所有未来值都包含在结果数据表中。使用时:

    DT[DT_LU, nomatch=0, roll=1]
    

    你看到只包括未来的第一个值:

       t_id airport thisTime f_id
    1:    3       a        6    1
    2:    3       a        6    2
    3:    5       d        7    2
    

    如果您希望f_id 用于airportthisTime 的所有组合,其中DT$thisTime 低于DT_LU$thisTime,您可以通过创建一个新变量(或替换现有的@ 987654337@) 通过ceiling 函数。我创建一个新变量 thisTime2 然后与 DT_LU 进行正常连接的示例:

    DT[, thisTime2 := ceiling(thisTime)]
    setkey(DT, airport, thisTime2)[DT_LU, nomatch=0]
    

    给出:

       t_id airport thisTime thisTime2 f_id
    1:    1       a      5.1         6    1
    2:    4       a      5.1         6    1
    3:    3       a      5.1         6    1
    4:    1       a      5.1         6    2
    5:    4       a      5.1         6    2
    6:    3       a      5.1         6    2
    7:    2       d      6.2         7    2
    8:    5       d      6.2         7    2
    

    应用于您提供的数据:

    > dt[, thisTime2 := ceiling(thisTime)]
    > setkey(dt, airport, thisTime2)[dt_lookup, nomatch=0]
    
       t_id airport thisTime thisTime2 f_id
    1:    1       a      5.1         6    1
    2:    3       a      5.1         6    1
    3:    1       a      5.1         6    2
    4:    3       a      5.1         6    2
    

    当您想要包含所有未来值而不仅仅是第一个值时,您需要一种稍微不同的方法,您需要 i.col 功能(尚未记录):

    1:首先将键设置为仅airport 列:

    setkey(DT, airport)
    setkey(DT_LU, airport)
    

    2:使用j 中的i.col 功能(尚未记录)来获得您想要的内容,如下所示:

    DT1 <- DT_LU[DT, .(tid = i.t_id,
                       tTime = i.thisTime,
                       fTime = thisTime[i.thisTime < thisTime],
                       fid = f_id[i.thisTime < thisTime]),
                 by=.EACHI]
    

    这给了你:

    > DT1
        airport tid tTime fTime fid
     1:       a   1   5.1     6   1
     2:       a   1   5.1     6   2
     3:       a   1   5.1     8   2
     4:       a   4   5.1     6   1
     5:       a   4   5.1     6   2
     6:       a   4   5.1     8   2
     7:       a   3   5.1     6   1
     8:       a   3   5.1     6   2
     9:       a   3   5.1     8   2
    10:       d   2   6.2     7   2
    11:       d   2   6.2     9   1
    12:       d   5   6.2     7   2
    13:       d   5   6.2     9   1
    

    一些解释:当你连接两个使用相同列名的数据表时,你可以通过在列名前面加上i.来引用i中数据表的列。现在可以比较来自DTthisTime 和来自DT_LUthisTime。使用by = .EACHI,您可以确保所有符合条件的组合都包含在结果数据表中。

    或者,您可以通过以下方式实现相同的目的:

    DT2 <- DT_LU[DT, .(airport=i.airport,
                       tid=i.t_id,
                       tTime=i.thisTime,
                       fTime=thisTime[i.thisTime < thisTime],
                       fid=f_id[i.thisTime < thisTime]),
                 allow.cartesian=TRUE]
    

    给出相同的结果:

    > identical(DT1, DT2)
    [1] TRUE
    

    当您只想在某个边界内包含未来值时,您可以使用:

    DT1 <- DT_LU[DT, 
                 {
                   idx = i.thisTime < thisTime & thisTime - i.thisTime < 2
                   .(tid  = i.t_id,
                     tTime = i.thisTime,
                     fTime = thisTime[idx],
                     fid = f_id[idx])
                   },
                 by=.EACHI]
    

    给出:

    > DT1
       airport tid tTime fTime fid
    1:       a   1   5.1     6   1
    2:       a   1   5.1     6   2
    3:       a   4   5.1     6   1
    4:       a   4   5.1     6   2
    5:       a   3   5.1     6   1
    6:       a   3   5.1     6   2
    7:       d   2   6.2     7   2
    8:       d   5   6.2     7   2
    

    当您将其与之前的结果进行比较时,您会看到现在第 3、6、9、10 和 12 行已被删除。


    数据:

    DT <- data.table(t_id = c(1,4,2,3,5),
                     airport = c("a","a","d","a","d"),
                     thisTime = c(5.1, 5.1, 6.2, 5.1, 6.2),
                     key=c("airport","thisTime"))
    
    DT_LU <- data.table(f_id = c(rep(1,4),rep(2,3)),
                        airport = c("a","b","c","d","a","d","e"),
                        thisTime = c(6,7,8,9,6,7,8),
                        key=c("airport","thisTime"))
    

    【讨论】:

    • 很好的解释 - “滚动连接采用最后出现组合键的行” - 是我理解的关键,谢谢。
    • 您的ceiling 示例在这种情况下运行良好,但我希望当dt$thisTime2 值大于1 时间单位远离dt_lookup$thisTime 值时它不会工作试图匹配,所以我可能不得不想出一个替代方案?
    • 我认为这可能是我见过的最好的 SO 答案之一!感谢您抽出宝贵时间向我介绍一些新技术。
    • @tospig 这可能很有趣:我向new question 询问了我在解决您的问题时遇到的一些奇怪问题。
    • @tospig 基于answer on my question,我再次更新了我的答案。这些改进将防止您遇到与我遇到的相同的怪事。
    猜你喜欢
    • 2022-01-07
    • 2019-08-01
    • 2016-04-18
    • 1970-01-01
    • 2018-11-27
    • 2012-08-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多