【发布时间】:2019-03-05 16:41:41
【问题描述】:
我有一个这样结构的数据框(但它实际上有大约 40 万行):
library(data.table)
df <- fread(" id start end
174095 2018-12-19 2018-12-31
227156 2018-12-19 2018-12-31
210610 2018-04-13 2018-09-27
27677 2018-04-12 2018-04-26
370474 2017-07-13 2017-08-19
303693 2017-02-20 2017-04-09
74744 2016-10-03 2016-11-05
174095 2018-12-01 2018-12-20
27677 2018-03-01 2018-05-29
111111 2018-01-01 2018-01-31
111111 2018-11-11 2018-12-31")
(已编辑,感谢 Uwe)
对于每一行,我想计算数据框中有多少行具有与当前行相同的 id 以及与当前行中的时间段重叠的起始时间段。例如,对于第一行,结果将为 2,因为还有另一行的 id = 174095,并且它的结尾大于第一行的开头。
我试着用 dplyr 的 rowwise 来做,比如:
df = df %>% rowwise() %>% mutate(count = sum(id == df$id & ((start >= df$start & start <= df$end) | (end >= df$start & end <= df$end))))
但这非常慢。我试了一下,两个小时后它还在运行。
我也尝试过使用 mapply,但它也需要太多时间:
df$count = mapply(function(id, start, end) {
return(sum(df$id == id & (between(df$start, start, end) | between(df$end, start, end))) }, id, start, end)
有没有一种有效合理的方法来做到这一点?
非常感谢
编辑 2019-03-06
@Uwe 的建议解决方案:
df[, overlapping.rows := df[.SD, on = .(id, start <= end, end >= start), .N, by = .EACHI]$N][]
适用于上面的示例 data.frame。但事实证明,示例说明性不够,或者我并没有真正让自己理解:)
我为 id 174095 添加了第三条记录并修改了另外两条:
df <- fread("id start end
174095 2018-12-19 2018-12-31
227156 2018-12-19 2018-12-31
210610 2018-04-13 2018-09-27
27677 2018-04-12 2018-04-26
370474 2017-07-13 2017-08-19
303693 2017-02-20 2017-04-09
74744 2016-10-03 2016-11-05
174095 2018-12-01 2018-12-18
27677 2018-03-01 2018-05-29
111111 2018-01-01 2018-01-31
111111 2018-11-11 2018-12-31
174095 2018-11-30 2018-12-25")
现在,id 174095 有两个不重叠的区间(第 1 行和第 2 行)和另一个与其他两个重叠的区间(第 3 行):
id start end
1: 174095 2018-12-19 2018-12-31
2: 174095 2018-12-01 2018-12-18
3: 174095 2018-11-30 2018-12-25
所以,结果应该是:
id start end overlapping.rows
1: 174095 2018-12-19 2018-12-31 2
2: 174095 2018-12-01 2018-12-18 2
3: 174095 2018-11-30 2018-12-25 3
但实际上是这样的:
id start end overlapping.rows
1: 174095 2018-12-19 2018-12-31 3
2: 174095 2018-12-01 2018-12-18 3
3: 174095 2018-11-30 2018-12-25 3
如果我没记错的话,这是因为最终连接仅由“id”完成,因此具有相同 id 的所有行都具有相同的结果。
我的解决方案还包括通过“开始”和“结束”执行最终合并:
df[tmp, on = .(id, start, end), overlapping.rows := N]
出于某种原因(我很想知道...),在自加入时,开始日期最终出现在“结束”列中,反之亦然,所以我必须在其后添加这一行:
setnames(tmp, c("id", "end", "start", "N"))
现在,结果是:
id start end overlapping.rows
1: 174095 2018-12-19 2018-12-31 2
2: 227156 2018-12-19 2018-12-31 1
3: 210610 2018-04-13 2018-09-27 1
4: 27677 2018-04-12 2018-04-26 2
5: 370474 2017-07-13 2017-08-19 1
6: 303693 2017-02-20 2017-04-09 1
7: 74744 2016-10-03 2016-11-05 1
8: 174095 2018-12-01 2018-12-18 2
9: 27677 2018-03-01 2018-05-29 2
10: 111111 2018-01-01 2018-01-31 1
11: 111111 2018-11-11 2018-12-31 1
12: 174095 2018-11-30 2018-12-25 3
这正是我所期望的!
【问题讨论】:
-
Frank 的方法
df[, n := df[.SD, on = .(id, start <= end, end >= start), .N, by = .EACHI]$N][]也为已编辑的数据集返回了正确答案。使用第二个连接时确实存在问题,可以通过包含行号来修复(请参阅我更新的答案)。 -
非 equi 连接中结果列的命名方式已在 GitHub 上多次报告,github.com/Rdatatable/data.table/pull/3093 正在解决此问题。
标签: r dplyr data.table