滚动联接在data.table中生效时如何标记

时间:2019-06-28 13:25:43

标签: r data.table

嗨,我很难区分两次特定事件何时发生在我的数据中。举例来说,假设我有3种类型的事件-a,b,c-每个事件在3个周期内都有效。

我想创建一个活动事件的周期系列,其中任何事件更改都将替换前一个事件,但是如果当前事件重复发生,它将重新开始。这是一些代码来说明:

a1 <- data.table(
  c(1, 4, 7, 8),
  c("a", "a", "c", "d")
)

a2 <- data.table(
  c(1, 7, 8),
  c("a", "c", "d")
)

b <- data.table(
  seq(1, 8)
)

setkey(a1, V1)
setkey(a2, V1)
setkey(b, V1)

a1[b, roll = 2]

a2[b, roll = 2]

正如您在a1上看到的那样,我得到了事件类型为a的消息,而我没有得到有关事件实际上在何处重复的大量信息。理想情况下,我希望我的参加者能够对活动进行如下限定:

data.table(
  c(1:8),
  c("a", "a", "a", "a", "a", "a", "c", "d"),
  event = c(1, 0, 0, 1, 0, 0, 1, 1)
)

有什么想法吗?谢谢!!!

4 个答案:

答案 0 :(得分:4)

使用rleid形成每个组,将向量1,0,0的长度重复为每个组的.N。

k <- 3 # restart after this number of events that are the same
one.zeros <- rep(1:0, c(1, k-1))  # length k vector.  For k=3, c(1,0,0)
DT[, V3 := rep(one.zeros, length = .N), by = rleid(V2)]

给予:

> DT
   V1 V2 V3
1:  1  a  1
2:  2  a  0
3:  3  a  0
4:  4  a  1
5:  5  a  0
6:  6  a  0
7:  7  c  1
8:  8  c  0
9:  9  d  1

注意

输入DT为:

library(data.table)
DT <- data.table(1:9, c("a", "a", "a", "a", "a", "a", "c", "c", "d"))

答案 1 :(得分:3)

@GaborGrothendieck答案的另一种形式:

DT[, v := 0L]
DT[(rowid(rleid(V2)) - 1L) %% 3 == 0, v := 1L][]

   V1 V2 v
1:  1  a 1
2:  2  a 0
3:  3  a 0
4:  4  a 1
5:  5  a 0
6:  6  a 0
7:  7  c 1
8:  8  c 0
9:  9  d 1

这只是对每个rowid组内的rleid(1,2,3 ...)进行算术。

答案 2 :(得分:2)

您可以检查联接期间键(示例中的V1)是否匹配:

a1[b, .(V2 = x.V2, event = isTRUE(x.V1 == i.V1)), roll = 2, by = .EACHI]
   V1 V2 event
1:  1  a  TRUE
2:  2  a FALSE
3:  3  a FALSE
4:  4  a  TRUE
5:  5  a FALSE
6:  6  a FALSE
7:  7  c  TRUE
8:  8  d  TRUE

a2[b, .(V2 = x.V2, event = isTRUE(x.V1 == i.V1)), roll = 2, by = .EACHI]
   V1   V2 event
1:  1    a  TRUE
2:  2    a FALSE
3:  3    a FALSE
4:  4 <NA> FALSE
5:  5 <NA> FALSE
6:  6 <NA> FALSE
7:  7    c  TRUE
8:  8    d  TRUE

考虑到此答案中的评论:

set.seed(5438L)
n <- 1e5
a <- data.table(
        sample(2 * n, n, replace = FALSE),
        sample(c("a", "b", "c"), n, replace = TRUE),
        key = "V1"
)

b <- data.table(1:(2 * n), key = "V1")

library(microbenchmark)

microbenchmark(
        during = a[b, c(.SD, event = isTRUE(x.V1 == i.V1)), roll = 2, by = .EACHI],
        after = a[b, roll = 2][a, event := !is.na(i.V1), on = "V1"],
        times = 30L
)
Unit: milliseconds
   expr       min        lq      mean    median        uq       max neval cld
 during 767.49338 771.49878 795.02283 776.20243 787.96382 964.11575    30   b
  after  26.14068  26.46543  28.58425  27.51831  29.73552  37.36052    30  a 

因此,在这种情况下,IceCreamToucan的答案可能更好。

答案 3 :(得分:0)

我不确定我是否完全理解OP在此问题背后的意图,但我相信可以选择无需加入即可解决。

如果我理解正确,那么目标是扩展事件列表

   V1 V2
1:  1  a
2:  4  a
3:  7  c
4:  8  d

进入一系列活动事件

   V1 V2 event
1:  1  a     1
2:  2  a     0
3:  3  a     0
4:  4  a     1
5:  5  a     0
6:  6  a     0
7:  7  c     1
8:  8  d     1

在输入数据表中,V1表示发生V2类型的事件时的时间索引。因此,我们可以重复V2的每个值直到下一个事件。通过将event中给出的位置与时间索引序列进行比较来创建a1$V1列:

a1[, .(1:max(V1), rep(V2, c(diff(V1), 1)), event = +(1:max(V1) %in% V1))]
   V1 V2 event
1:  1  a     1
2:  2  a     0
3:  3  a     0
4:  4  a     1
5:  5  a     0
6:  6  a     0
7:  7  c     1
8:  8  d     1

(请注意,1:max(V1) %in% V1是逻辑类型,+(1:max(V1) %in% V1)将其强制为整数。)


此外,有两种变体可以返回相同的结果,但性能可能有所不同(未进行基准测试):

a1[, .(1:max(V1), rep(V2, c(diff(V1), 1)), event = replace(rep(0L, max(V1)), V1, 1L))]

使用replace()创建event列。

a1[, .(1:max(V1), rep(V2, c(diff(V1), 1)), event = 0L)][a1$V1, event := 1L][]

初始化event列,但在后续步骤中有选择地更新它。