我有一个数据集,每个 id
包含几行。每行包含事件的开始
日期和结束
日期。我想为每一行(事件)创建一个指示器,指示它是否与同一个人 (id) 的另一个事件重叠。
到目前为止,我已经成功地在 R 中做到了这一点。但是,我觉得好像我的代码不是很简洁。我怀疑我可以使用更短的 forloop 或结合使用 mutate()
、ifelse()
和 dplyr< 中的其他窗口函数来完成此操作
和 lubridate
库。
这是我的最小可重现示例代码:
df <- structure(list(id = c(6202924, 6202924, 6202924, 6202924, 6202924,
6202924, 6202924, 6203161, 6202802, 6202781, 6202781, 6202760,
6202890, 6203223, 6202766, 6203154, 6202891, 6202891, 6202876,
6202876, 6203075, 6202988, 6202805, 6202741, 6203144, 6203144,
6203144, 6203051, 6203140, 6203140, 6203140, 6203140, 6203140,
6203140, 6203115, 6202870, 6202870, 6202870, 6203180, 6203180,
6203180, 6202968), start = structure(c(NA, 14890, 14944, 14883,
14914, 14958, 14982, 14860, NA, 14867, 14867, NA, 14853, 14860,
15102, NA, NA, 14883, 14853, 14853, 14853, 14860, 14853, 14853,
15065, NA, NA, NA, 15048, 14867, 14928, 14853, 14853, 14867,
14914, 14975, 15013, 15013, NA, 14982, 15065, 14982), class = "Date"),
end = structure(c(NA, 14965, 14965, 14965, 14965, 14958,
14982, 15208, NA, 14874, 14874, NA, 15208, 15208, 15102,
NA, NA, 14904, 15147, 14965, 15208, 15027, 15208, 15208,
15208, NA, NA, NA, 15048, 15208, 14965, 15208, 15006, 14874,
14935, 14975, 15048, 15048, NA, 15079, 15208, 15208), class = "Date")), class = c("tbl_df",
"tbl", "data.frame"), row.names = c(NA, -42L), .Names = c("id",
"start", "end"))
df1 <- df %>%
arrange(id, start, end) %>% # Order chronologically
group_by(id) %>%
mutate(seq = row_number(), # An indicator of the seq of activity per id
count = n(), # An indicator of total number of activities per id
overlap = "No") %>% #Indicator of overlap
ungroup()
for(i in 1:nrow(df1)) {
# This loop compares the index row to the row below it
if(df1$end[i] >= df1$start[i + 1] &
df1$id[i] == df1$id[i + 1] &
!is.na(df1$end[i]) &
!is.na(df1$start[i + 1])) {
df1$overlap[i] <- "Yes"
}
if(i != 1 & df1$seq[i] != 1) {
for(j in 1:(df1$seq[i] - 1)) {
# This loop compares the index row to the rows behind it which also belong to the same id
if(df1$start[i] <= df1$end[i - j] &
df1$id[i] == df1$id[i - j] &
!is.na(df1$end[i - j]) &
!is.na(df1$start[i])) {
df1$overlap[i] <- "Yes"
break
}
}
}
}
我真的很喜欢 tidyverse
库套件,所以如果有人能帮助我找到一种使用这些库来完成此任务的方法,那么对我来说一等奖就是。
最佳答案
仅使用 dplyr
的解决方案:
我们可以利用 join
属性,给定重复的 id,它们会产生所有可能的行组合。不过,这种方法在内存中是二次方的,因此如果您有数百万行,则可能需要进行一些额外的优化。
首先,为每个事件添加一个id号:
dfi = mutate(df, act_id=seq_along(id))
然后为每个用户生成所有可能的事件组合(将数据框与其自身合并),删除两列中相同事件的行,并保持时间重叠的行:(注意我们只需要检查一侧重叠,x
在 y
之前开始)
df2 = inner_join(dfi, dfi, by="id") %>%
filter(act_id.x!=act_id.y,
start.x<=start.y,
start.y<=end.x)
要生成重叠事件 ID 的向量:
ovrl_ids = c(df2$act_id.x, df2$act_id.y)
或原始数据框的逻辑列:
dfi$ovrl = dfi$act_id %in% ovrl_ids
确认结果与您的解决方案生成的 df1
匹配:
dfb = full_join(df1, dfi, by=c("id", "start"))
table(dfb$ovrl, dfb$overlap, useNA="a")
关于r - 使 R 代码更简洁,以创建重叠日期的指示器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42666211/