Die*_*ego 4 r dplyr data.table
我有一个这样的数据框(但实际上有约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")
Run Code Online (Sandbox Code Playgroud)
(编辑,感谢Uwe)
对于每一行,我想计算数据帧中有多少行具有与当前行相同的ID,以及与当前行的周期重叠的开始-结束周期。例如,对于第一行,结果将为2,因为存在另一行id = 174095,并且其结尾大于第一行的开始。
我试图用dplyr的按行操作,例如:
df = df %>% rowwise() %>% mutate(count = sum(id == df$id & ((start >= df$start & start <= df$end) | (end >= df$start & end <= df$end))))
Run Code Online (Sandbox Code Playgroud)
但这非常慢。我试了一下,两个小时后它仍在运行。
我也尝试使用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)
Run Code Online (Sandbox Code Playgroud)
是否有一种有效的合理方法来做到这一点?
非常感谢你
编辑2019-03-06
@Uwe建议的解决方案:
df[, overlapping.rows := df[.SD, on = .(id, start <= end, end >= start), .N, by = .EACHI]$N][]
Run Code Online (Sandbox Code Playgroud)
适用于上面的示例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")
Run Code Online (Sandbox Code Playgroud)
现在,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
Run Code Online (Sandbox Code Playgroud)
因此,结果应为:
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
Run Code Online (Sandbox Code Playgroud)
但实际上是:
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
Run Code Online (Sandbox Code Playgroud)
如果我没有记错的话,这是因为最后的联接仅由“ id”完成,所以所有具有相同id的行都具有相同的结果。
我的解决方案包括通过“开始”和“结束”执行最终合并:
df[tmp, on = .(id, start, end), overlapping.rows := N]
Run Code Online (Sandbox Code Playgroud)
由于某种原因(我很想找出...),在自我联接上,开始日期以“结束”列结尾,反之亦然,因此我必须在其后添加此行:
setnames(tmp, c("id", "end", "start", "N"))
Run Code Online (Sandbox Code Playgroud)
现在,结果是:
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
Run Code Online (Sandbox Code Playgroud)
正是我所期望的!
编辑2019-03-07以处理OP的扩展数据集
这可以通过聚合非等价自连接来解决
library(data.table)
# coerce character dates to IDate class
cols <- c("start", "end")
setDT(df)[, (cols) := lapply(.SD, as.IDate), .SDcols = cols]
# non-equi self-join and aggregate
tmp <- df[df, on = .(id, start <= end, end >= start), .N, by = .EACHI]
# append counts to original dataset
df[, overlapping.rows := tmp$N]
df
Run Code Online (Sandbox Code Playgroud)
Run Code Online (Sandbox Code Playgroud)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
使用data.table链接可以以更紧凑但也更复杂的方式编写代码:
library(data.table)
cols <- c("start", "end")
setDT(df)[, (cols) := lapply(.SD, as.IDate), .SDcols = cols][
, overlapping.rows := df[df, on = .(id, start <= end, end >= start), .N, by = .EACHI]$N][]
Run Code Online (Sandbox Code Playgroud)
请注意,将结果附加到原始文件的df部分基于Frank的注释。
如果OP指出的df计数相同id,我尝试使用第二个连接将结果附加到原始的尝试失败。可以通过在第二个联接中包括行号来解决此问题:
library(data.table)
# coerce character dates to IDate class
cols <- c("start", "end")
setDT(df)[, (cols) := lapply(.SD, as.IDate), .SDcols = cols]
# append row number
tmp <- df[, rn := .I][
# non-equi self-join and aggregate
df, on = .(id, start <= end, end >= start), .(rn = i.rn, .N), by = .EACHI]
# append counts to original dataset by joining on row number
df[tmp, on = "rn", overlapping.rows := N][, rn := NULL]
df
Run Code Online (Sandbox Code Playgroud)
Run Code Online (Sandbox Code Playgroud)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
非等额联接中的联接条件可以解决问题。如果第一个间隔在第二个间隔开始之前结束,或者第一个间隔在第二个间隔结束之后开始,则两个间隔不重叠,
e 1 <s 2或e 2 <s 1
现在,如果两个间隔确实相交/重叠,则上述情况必须相反。通过否定和应用德摩根定律,我们得到了条件
s 2 <= e 1和e 2 > = s 1
在non-equi join中使用。
如OP的EDIT 2019-03-06中所述,OP的扩展数据集为:
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-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")
Run Code Online (Sandbox Code Playgroud)