R语言:基于另一数据框的时间区间为数据框添加列
嘿,我懂你的问题啦——你本来想给DF添加一列标记,判断每行的Time是否落在StartEnd里的任意一个区间,但用mapply跑出来的结果是个矩阵,完全不是想要的单列,对吧?
为啥会返回矩阵?
mapply会对StartEnd里的每一组Start和End单独生成一个和DF$Time长度一致的0/1向量,最后把这些向量按列拼起来,所以你得到了4列的矩阵(刚好对应StartEnd的4行)。而我们需要的是只要Time落在任意一个区间就标记1,否则0,所以得把这个矩阵的行结果合并起来。
几种解决方法:
方法1:用rowSums快速处理矩阵结果
这是最直接的修正方案,利用rowSums计算每行的和——只要行和大于0,就说明这个Time至少落在一个区间里,再转成1/0格式:
DF <- data.frame(Time=c(1:20)) StartEnd <- data.frame(Start=c(2,6,14,19), End=c(4,10,17,20)) # 修正后的代码 DF$Activity <- as.integer(rowSums( mapply(FUN = function(Start,End) DF$Time >= Start & DF$Time <= End, Start=StartEnd$Start, End=StartEnd$End) ) > 0)
运行后DF$Activity就是你要的单列标记:比如Time=2-4、6-10、14-17、19-20这些行的Activity都是1,其余是0。
方法2:直接遍历每个Time值判断
如果觉得矩阵处理有点绕,也可以直接对每个Time值检查是否在任意区间里,用sapply实现:
DF$Activity <- as.integer( sapply(DF$Time, function(t) { any(t >= StartEnd$Start & t <= StartEnd$End) }) )
这里any()函数会判断当前Time是否满足至少一个区间条件,返回TRUE/FALSE,再用as.integer()转成1/0。
方法3:用data.table做高效区间匹配(适合大数据)
如果你的数据量很大,用data.table的区间匹配功能会更高效:
library(data.table) setDT(DF) setDT(StartEnd) # 给DF加一个临时的End列(和Time相等,变成单元素区间) DF[, temp_end := Time] # 设置StartEnd的键用于区间匹配 setkey(StartEnd, Start, End) # 用foverlaps找落在任意区间内的行 overlaps <- foverlaps(DF, StartEnd, by.x = c("Time", "temp_end"), type = "within") # 标记:匹配到的行Activity为1,否则0 DF$Activity <- as.integer(!is.na(overlaps$Start)) # 删掉临时列 DF[, temp_end := NULL]
这样不管哪种方法,都能得到你想要的单列标记啦~
内容的提问来源于stack exchange,提问作者user3640617
相关产品推荐
相关产品推荐

