使用重复键在data.table上滚动连接

tos*_*pig 15 join r data.table

我想了解rolling joinsdata.table.最后给出了重现这一点的数据.

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

> dt
   t_id airport thisTime
1:    1       a      5.1
2:    3       a      5.1
3:    2       a      6.2  
Run Code Online (Sandbox Code Playgroud)

(注t_ids1和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
Run Code Online (Sandbox Code Playgroud)

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

   t_id airport thisTime f_id
      1       a        6    1
      1       a        6    2
      3       a        6    1
      3       a        6    2
Run Code Online (Sandbox Code Playgroud)

所以我认为这会奏效:

> dt[dt_lookup, nomatch=0,roll=Inf]
   t_id airport thisTime f_id
1:    3       a        6    1
2:    3       a        6    2
Run Code Online (Sandbox Code Playgroud)

但它还没有退回交易t_id == 1.

文档中可以看出:

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

但是,我确实在我的'x key'(即airport&thisTime)中有重复项,并且无法完全看到/理解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"))
Run Code Online (Sandbox Code Playgroud)

Jaa*_*aap 23

t_id = 1未在输出中显示的原因是因为滚动连接采用最后发生键组合的行.从文档(强调我的):

适用于最后一个连接列,通常是日期,但可以是任何有序变量,不规则且包括间隙.如果roll = TRUE并且i的行与除最后一个x join列之外的所有行匹配,并且它在最后一个i join列中的值落在一个间隙中(包括在该组中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
Run Code Online (Sandbox Code Playgroud)

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

DT[DT_LU, nomatch=0, roll=Inf]
Run Code Online (Sandbox Code Playgroud)

你得到:

   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
Run Code Online (Sandbox Code Playgroud)

如您所见,从组合键a, 5.1d, 6.2最后一行用于连接数据表.由于您将其Inf用作滚动值,因此所有未来值都将合并到生成的数据表中.当你使用:

DT[DT_LU, nomatch=0, roll=1]
Run Code Online (Sandbox Code Playgroud)

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

   t_id airport thisTime f_id
1:    3       a        6    1
2:    3       a        6    2
3:    5       d        7    2
Run Code Online (Sandbox Code Playgroud)

如果你想f_id的为的所有组合airportthisTime其中DT$thisTime低于DT_LU$thisTime,您可以通过创建一个新的变量(或替换现有的做到这一点thisTime)的通过手段ceiling的功能.我创建一个新变量thisTime2然后执行普通连接的示例DT_LU:

DT[, thisTime2 := ceiling(thisTime)]
setkey(DT, airport, thisTime2)[DT_LU, nomatch=0]
Run Code Online (Sandbox Code Playgroud)

这使:

   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
Run Code Online (Sandbox Code Playgroud)

适用于您提供的数据:

> 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
Run Code Online (Sandbox Code Playgroud)

如果要包含未​​来的值而不是仅包含第一个值,则需要一种稍微不同的方法,您将需要该i.col功能(尚未记录):

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

setkey(DT, airport)
setkey(DT_LU, airport)
Run Code Online (Sandbox Code Playgroud)

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

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

这给你:

> 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
Run Code Online (Sandbox Code Playgroud)

一些解释:如果您要连接两个使用相同列名的数据表,您可以通过在列名i前面引用数据表的列i..现在,您可以比较thisTime来自DTthisTimeDT_LU.有了by = .EACHI你保证,对于与条件成立的所有组合都包括在结果DataTable.

或者,您可以实现相同的目标:

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]
Run Code Online (Sandbox Code Playgroud)

这给出了相同的结果:

> identical(DT1, DT2)
[1] TRUE
Run Code Online (Sandbox Code Playgroud)

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

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]
Run Code Online (Sandbox Code Playgroud)

这使:

> 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
Run Code Online (Sandbox Code Playgroud)

将其与上一个结果进行比较时,您会看到现在已删除第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"))
Run Code Online (Sandbox Code Playgroud)

  • 我想这可能是我见过的最好的答案之一!感谢您抽出时间并向我介绍一些新技术. (2认同)