tl.dr.我有一个聚合问题,我之前没有在文档中看到过.我设法完成它,但它对于预期的应用程序来说太慢了.我通常使用的数据有大约500行(我的直觉告诉我这对于dplyr来说并不多)并且根据system.time它运行大约4秒.我的困境是我想反复进行优化运行,目前我正在考虑运行时间.
你有没有看到我可以刮胡子的东西?
如果需要,我也可以发送一些我使用的数据.
算法
我有一个数据集:
sample_dataset <- data_frame( starts = c(1000,1008,1017,2000,2020,3000),ends = c(1009,1015,1020,2015,2030,3010),v = list(rep(1,10),rep(2,8),rep(3,4),rep(4,16),rep(5,11),rep(6,11)) )
所以每一行都编码一个信号和一个开始和结束索引.我想将所有具有小于接近度(例如10)距离的线聚合成一条线.如果重要,开始订购.
输出应该是:
structure(list(inds = 1:3,starts = c(1000,ends = c(1020,v = list(c(1,1,3,2,3),c(4,4,5,5),c(6,6,6))),class = c("tbl_df","tbl","data.frame" ),row.names = c(NA,-3L),.Names = c("inds","starts","ends","v"))
因此,原始数据集中的前三行是聚合的,第4行和第5行是聚合的,6是未更改的.对于重叠,应该添加数字,填充空白零.更新的开始值是第一次开始,更新的结束应该是最后的结束(假设我应该将其修复到最大值).但顺便说一下这些生成结束也应该排序.不应发生一个块完全被另一个块包围的情况.
我通过以下代码实现了这一点:
码
library(dplyr) join_lines <- function(dfi) { if (nrow(dfi)==1) return(select(dfi,starts,ends,v)) else with(dfi,{ start <- starts[[1]] end <- ends[[length(ends)]] vals <- numeric(end-start+1) add_val <- function(ddf) with(ddf,{ vals[(starts-start+1) : (ends-start+1)] <<- vals[(starts-start+1) : (ends-start+1)] + v }) dfi %>% rowwise() %>% do(tmp=add_val(.)) data_frame(starts=start,ends=end,v=list(vals))}) } simplify_semisparse <- function(aframe,closeness = 10){ aframe %>% mutate( join_pre = lag(ends,default=0)+closeness >= (starts),inds = cumsum(!join_pre) ) %>% group_by(inds) %>% do(join_lines(.)) %>% ungroup() } res <- simplify_semisparse(sample_dataset) dput(res) # see above
背景
我正在处理的数据来自质谱.非常特殊的是,矢量有大约500,000个条目,其中不到10%不是零,典型的光谱有大约500个这样的密集区块.我需要快速插入这样的光谱值 – 我的想法是在“密集”区域中使用约.
比较建议
我有机会比较你的建议.
@ matt-jewett解决方案产生的结果与我的预期结果不一致,所以我确实排除了它.
@jeremycgs解决方案最接近我原来的方法,但也没有产生完全相同的结果.
最重要的是我的运行时,我正在使用生产数据进行比较.我的原始解决方案需要2.165秒. @tjeremy的建议耗时0.532秒,@ uwe-block 0.012秒.
哇 – 我需要学习data.table.
虽然OP要求加速dplyr代码,但我想建议一个data.table解决方案,以提高性能.此外,迄今为止发布的其他答案中没有一个完全解决OP的要求,即
>使用开始,结束和v值列表保持sample_data的结构,
>将具有小于接近度(例如10)距离的所有线聚合成一条线.
以下代码尝试符合所有要求:
library(data.table) # CRAN versio 1.10.4 used # define threshold: closeness as defined by OP,max_gap used in code closeness <- 10L max_gap <- closeness - 1L # coerce to data.table,and key,i.e.,sort by starts and ends DT <- data.table(sample_dataset,key = c("starts","ends")) # compute gaps between ends and starts of next row # identify rows which belong together: inds is advanced if gap is greater threshhold DT[,gap := starts - shift(ends,fill = -Inf)][,inds := cumsum(gap > max_gap)][] # close gaps but only within groups DT0 <- DT[between(gap,2L,max_gap),.(starts = starts - (gap - 1L),ends = starts - 1L,v = Vectorize(rep.int)(0L,gap - 1L),gap,inds)] # bind rowwise (union in sql),setkey on result to maintain sort order,# remove column gap as no longer needed DT2 <- setkey(rbind(DT,DT0),ends)[,gap := NULL][] # aggregate groupwise,pick min/max,combine lists result <- DT2[,.(starts = min(starts),ends = max(ends),v = list(Reduce(c,v))),by = inds] # alternative code: pick first/last result <- DT2[,.(starts = first(starts),ends = last(ends),by = inds] result
产生
06001
同
result$v
06003
可以验证v向量中的元素数量是相同的,除了为组内间隙添加的额外零:
# test that all v values are included # original sum(lengths(sample_dataset$v)) #[1] 60 # result with additional zeros removed sum(sapply(result$v,function(x) sum(x > 0))) #[1] 60
我没有提供基准测试,因为样本数据集太小了.
数据
sample_dataset <- dplyr::data_frame( starts = c(1000,11)) )