R 具有两个标识符的事件的重叠周期

R Overlapping period for event with two identifiers

我正在尝试为具有两个标识符的数据框查找重叠周期,这与在该主题上提出的其他问题不同,在这些问题中,重叠观察必须只计算一个标识符。

在我的数据框中,每笔交易都有一个唯一的 ID,每家进行交易的公司都有一个唯一的公司标识符。我正在尝试查找每个公司标识符的重叠周期,按交易 ID 计算。

我使用来自@Waldi 的代码创建了这个可重现的示例,在本主题中找到(我只向示例数​​据框添加了公司标识符或“FirmID”):

创建类似数据集的代码:

library (data.table)

size = 1e5

df <- data.frame(
  ID = sample(1:round(size / 5, 0)),
  FirmID = sample(1:20000),
  period = sample(c(5,10,30,45), size, replace = TRUE),
  start = sample(seq(
    as.Date('1999/01/01'), as.Date('2000/01/01'), by = "day"
  ), size, replace = TRUE)
) %>% mutate(end = start + period)

查找重叠时段的代码:

dt <- data.table(df, key = c("start", "end"))[, `:=`(row = 1:nrow(df))]

setkey(dt,FirmID,start,end)
foverlaps(dt,dt,by.x=c("FirmID","start","end"),by.y=c("FirmID","start","end"))[
  ,.(noverlap=.N),by=.(FirmID,row)][
    ,.(overlap = max(noverlap>1)),by=FirmID][
      ,.(n=.N),by=.(overlap)][
        ,pct:=n/sum(n)][]

此代码的唯一问题是它显示重叠公司的数量,而不是公司标识符内重叠交易的数量。

overlap     n     pct
1:       0  5333 0.26665
2:       1 14667 0.73335

如何针对公司标识符中重叠的交易 ID 更改此代码?我自己做的一个改变没有产生令人满意的结果:

setkey(dt,FirmID,start,end)
foverlaps(dt,dt,by.x=c("FirmID","start","end"),by.y=c("FirmID","start","end"))[
  ,.(noverlap=.N),by=.(ID,row)][
    ,.(overlap = max(noverlap>1)),by=ID][
      ,.(n=.N),by=.(overlap)][
        ,pct:=n/sum(n)][]

   overlap     n     pct
1:       0  5333 0.26665
2:       1 14667 0.73335

我费了好大劲才想出另一种情况的解决方案, 所以我不确定如何调整那个版本, 但我相信在这种情况下你可以通过内部 non-equi join:

获得你想要的结果
dt <- data.table(df, key = c("FirmID", "ID", "start", "end"))
dt[, firm_total := .N, by = "FirmID"][
    dt, .(FirmID, firm_total), on = .(FirmID, ID < ID, start <= end, end >= start), nomatch = NULL, mult = "all"][
        , .(n = .N, pct = .N / (firm_total[1L] * (firm_total[1L] - 1L) / 2L)), by = "FirmID"]

我们在与 ID < ID 的连接中避免了冗余行 (请注意,此处应解释为 left-hand side column's ID < right-hand side column's ID)。 冗余可能发生,因为, 如果 ID x 与 ID y 重叠, yx 重叠。

如果你把所有的ID对放到一个矩阵中, 重叠的最大数量将是下三角中的元素数量, 可以用 n * (n - 1) / 2 计算, 这就是我们最初添加 firm_total.

的原因

我没有进行广泛的测试,但这个版本可能更适合这种情况。 foverlaps 的文档指出它主要针对一个 table 比另一个小得多的连接, 并解释为什么它可能是一项昂贵的操作。 你正在做 self-join, 所以两个 table 大小相同。

还有一个 table.express 版本的解决方案,因为为什么不:

library(table.express)

dt %>%
    group_by(FirmID) %>%
    mutate(firm_total = .N) %>%
    inner_join(dt, FirmID, ID < ID, start <= end, end >= start, .expr = TRUE) %>%
    select(FirmID, firm_total) %>%
    group_by(FirmID) %>%
    summarize(n = .N, pct = .N / (firm_total[1L] * (firm_total[1L] - 1L) / 2))

编辑:如果你想要你的 overlap 列,你可以用 size of lower triangular - n.

计算它