【发布时间】:2015-06-05 01:08:45
【问题描述】:
我想知道是否可以在 R 中有效地解决孤岛和间隙问题,类似于 SQL。如果我们检查一个 ID,我有以下可用数据:
ID StartDate StartTime EndDate EndTime
1 19-05-2014 19:00 19-05-2014 20:00
1 19-05-2014 19:30 19-05-2014 23:30
1 19-05-2014 16:00 19-05-2014 18:00
1 20-05-2014 20:00 20-05-2014 20:30
注意前两行重叠,我想做的是合并重叠的行,结果:
ID StartDate StartTime EndDate EndTime
1 19-05-2014 19:00 19-05-2014 23:30
1 19-05-2014 16:00 19-05-2014 18:00
1 20-05-2014 20:00 20-05-2014 20:30
有没有办法在 R 中做到这一点?
我很清楚这是在 SQL 中完成的,但由于我的数据已经在 R 中,我更喜欢在 R 中执行此操作。其次,我对查找间隙和孤岛的性能有一些疑问,我知道 SQL 是这样做的速度非常快,但我想知道由于所有数据都在内存中,R 是否更快。
我想使用data.table 来做这个,但我不知道怎么做。
更新 - 对阿伦的回应
我创建了以下测试用例,其中包含所有可能的区间方向。
dat <- structure(
list(ID = c(1L, 1L, 1L, 1L, 1L, 1L),
stime = structure(c(as.POSIXct("2014-01-15 08:00:00"),
as.POSIXct("2014-01-15 10:00:00"),
as.POSIXct("2014-01-15 08:30:00"),
as.POSIXct("2014-01-15 09:00:00"),
as.POSIXct("2014-01-15 11:30:00"),
as.POSIXct("2014-01-15 12:00:00")),
class = c("POSIXct", "POSIXt"), tzone = ""),
etime = structure(c(as.POSIXct("2014-01-15 09:30:00"),
as.POSIXct("2014-01-15 11:00:00"),
as.POSIXct("2014-01-15 10:00:00"),
as.POSIXct("2014-01-15 09:30:00"),
as.POSIXct("2014-01-15 12:30:00"),
as.POSIXct("2014-01-15 13:00:00")),
class = c("POSIXct", "POSIXt"), tzone = "")
),
.Names = c("ID", "stime", "etime"),
sorted = c("ID", "stime", "etime"),
class = c("data.table", "data.frame"),
row.names = c(NA,-6L)
)
我希望从 8:30 到 10:00 的时间间隔将“粘合”到 10:00 到 11:00,但事实并非如此。结果是:
idx ID stime etime
1: 4 1 2014-01-15 08:00:00 2014-01-15 10:00:00
2: 3 1 2014-01-15 10:00:00 2014-01-15 11:00:00
3: 6 1 2014-01-15 11:30:00 2014-01-15 13:00:00
以下数据集提供了更彻底的测试:
# The numbers represent seconds from 1970-01-01 01:00:01
dat <- structure(
list(ID = c(1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L),
stime = structure(c(as.POSIXct("2014-01-15 08:00:00"),
as.POSIXct("2014-01-15 10:00:00"),
as.POSIXct("2014-01-15 08:30:00"),
as.POSIXct("2014-01-15 09:00:00"),
as.POSIXct("2014-01-15 11:30:00"),
as.POSIXct("2014-01-15 12:00:00"),
as.POSIXct("2014-01-15 07:30:00"),
as.POSIXct("2014-01-15 08:00:00"),
as.POSIXct("2014-01-15 08:30:00"),
as.POSIXct("2014-01-15 09:00:00"),
as.POSIXct("2014-01-15 09:00:00"),
as.POSIXct("2014-01-15 09:30:00"),
as.POSIXct("2014-01-15 10:00:00")
),
class = c("POSIXct", "POSIXt"), tzone = ""),
etime = structure(c(as.POSIXct("2014-01-15 09:30:00"),
as.POSIXct("2014-01-15 11:00:00"),
as.POSIXct("2014-01-15 10:00:00"),
as.POSIXct("2014-01-15 09:30:00"),
as.POSIXct("2014-01-15 12:30:00"),
as.POSIXct("2014-01-15 13:00:00"),
as.POSIXct("2014-01-15 08:30:00"),
as.POSIXct("2014-01-15 09:00:00"),
as.POSIXct("2014-01-15 09:30:00"),
as.POSIXct("2014-01-15 10:00:00"),
as.POSIXct("2014-01-15 10:00:00"),
as.POSIXct("2014-01-15 10:30:00"),
as.POSIXct("2014-01-15 11:00:00")
),
class = c("POSIXct", "POSIXt"), tzone = "")
),
.Names = c("ID", "stime", "etime"),
sorted = c("ID", "stime", "etime"),
class = c("data.table", "data.frame"),
row.names = c(NA,-6L)
)
所以我们的结果是:
idx ID stime etime
1: 4 1 2014-01-15 08:00:00 2014-01-15 10:00:00
2: 3 1 2014-01-15 10:00:00 2014-01-15 11:00:00
3: 6 1 2014-01-15 11:30:00 2014-01-15 13:00:00
4: 12 2 2014-01-15 07:30:00 2014-01-15 09:30:00
5: 13 2 2014-01-15 09:00:00 2014-01-15 11:00:00
现在对于 ID=2 的受访者,我们看到区间是重叠的,但没有报告为一个区间。正确的解决方案是:
idx ID stime etime
1: ? 1 2014-01-15 08:00:00 2014-01-15 11:00:00
3: ? 1 2014-01-15 11:30:00 2014-01-15 13:00:00
4: ?? 2 2014-01-15 07:30:00 2014-01-15 11:00:00
更新 - 基准测试和大型数据集
我有以下数据集,其中包含大约 1000 个用户,每个用户有 500 个持续时间,提供 50 万行。您可以通过我的Google Drive 下载数据集,包括 Google Drive 中的解决方案。
使用 Itzik Ben-Gan 在 SQL 中提供的解决方案,在 8GB RAM、64 位、i5-4210U CPU @ 1.70Ghz - 2.39Ghz 的笔记本电脑上运行 SQL Server 2014 大约需要 5 秒。 5 秒不包括创建函数的过程。此外,不会为任何表创建任何索引。
PS:我用library(lubridate);
【问题讨论】:
-
什么是 SQL 解决方案?
-
亲爱的 Eddi,以下是该问题的一些示例:sqlmag.com/blog/…。 Itzik Ben-Gan 能够在令人印象深刻的 2 秒内做到这一点。
-
@alexis_laz,是的,它可以在这里工作,但 POSIXct 也可以有毫秒,这会失败(因为 IRanges::reduce 会隐式将其转换为整数范围)..
标签: r data.table gaps-and-islands