不同来源区间重叠百分比计算的优化与扩展性需求问询
问题
我有一个包含不同来源(A、B、C)区间的dataframe,想要计算各来源区间的两两重叠百分比。我写了以下代码,想知道有没有更简便/高效的实现方式(实际数据量远大于示例),以及如何让方案适配新增来源(比如第四个来源)。
data=data.frame(StartA=c(134000,765888,243634,576098,398776,128598,NA), StopA=c(181654,842465, 244377, 582626, 399102, 129893, NA), StartB=c(134023,765880,243634,576098,NA,128598,849356), StopB=c(181654,842465, 244352, 582626, NA, 129893, 868654), StartC=c(132065,NA,NA,592626,398776,128698,867656), StopC=c(191604,NA, NA, 593391, 399102, 129993, 868654)) data StartA StopA StartB StopB StartC StopC 1 134000 181654 134023 181654 132065 191604 2 765888 842465 765880 842465 NA NA 3 243634 244377 243634 244352 NA NA 4 576098 582626 576098 582626 592626 593391 5 398776 399102 NA NA 398776 399102 6 128598 129893 128598 129893 128698 129993 7 NA NA 849356 868654 867656 868654 # For loop to compute each overlap for each row ################################################# data$overlap_reciproq_AB=NA data$overlap_reciproq_BC=NA data$overlap_reciproq_AC=NA for (i in 1:nrow(data)) { # overlap A vs B if(!is.na(data$StartA[i]) & !is.na(data$StopA[i]) & !is.na(data$StartB[i]) & !is.na(data$StopB[i])) { overlapAB = max(0, as.numeric(min(data$StopA[i],data$StopB[i]))-as.numeric(max(data$StartA[i], data$StartB[i]))) overlap_A_B = overlapAB / (data$StopA[i] - data$StartA[i]) *100 overlap_B_A = overlapAB / (data$StopB[i] - data$StartB[i]) *100 data$overlap_reciproq_AB[i] = min(overlap_A_B, overlap_B_A) } # overlap A vs C if(!is.na(data$StartA[i]) & !is.na(data$StopA[i]) & !is.na(data$StartC[i]) & !is.na(data$StopC[i])) { overlapAC = max(0, as.numeric(min(data$StopA[i],data$StopC[i]))-as.numeric(max(data$StartA[i], data$StartC[i]))) overlap_A_C = overlapAC / (data$StopA[i] - data$StartA[i]) *100 overlap_C_A = overlapAC / (data$StopC[i] - data$StartC[i]) *100 data$overlap_reciproq_AC[i] = min(overlap_A_C, overlap_C_A) } # overlap B vs C if(!is.na(data$StartC[i]) & !is.na(data$StopC[i]) & !is.na(data$StartB[i]) & !is.na(data$StopB[i])) { overlapBC = max(0, as.numeric(min(data$StopB[i],data$StopC[i]))-as.numeric(max(data$StartB[i], data$StartC[i]))) overlap_B_C = overlapBC / (data$StopB[i] - data$StartB[i]) *100 overlap_C_B = overlapBC / (data$StopC[i] - data$StartC[i]) *100 data$overlap_reciproq_BC[i] = min(overlap_B_C, overlap_C_B) } } # Output ############################# StartA StopA StartB StopB StartC StopC overlap_reciproq_AB overlap_reciproq_BC overlap_reciproq_AC 1 134000 181654 134023 181654 132065 191604 99.95174 79.99966 79.99966 2 765888 842465 765880 842465 NA NA 99.98955 NA NA 3 243634 244377 243634 244352 NA NA 96.63526 NA NA 4 576098 582626 576098 582626 592626 593391 100.00000 0.00000 0.00000 5 398776 399102 NA NA 398776 399102 NA NA 100.00000 6 128598 129893 128598 129893 128698 129993 100.00000 92.27799 92.27799 7 NA NA 849356 868654 867656 868654 NA 5.17152 NA
解决方案
一、重构数据结构(适配新增来源的核心)
原数据是宽格式,每个来源的Start/Stop单独成列,新增来源时需修改大量代码。建议转为长格式,按行分组存储每个来源的区间信息,新增来源仅需添加对应列,转换时会自动识别。
library(tidyr) library(dplyr) # 转换为长格式:每行对应一个来源的区间,保留原行号 data_long <- data %>% mutate(row_id = row_number()) %>% pivot_longer( cols = -row_id, names_to = c(".value", "source"), names_pattern = "(Start|Stop)(.*)" ) %>% drop_na(Start, Stop) %>% # 移除无有效区间的行 mutate(length = Stop - Start) # 预计算区间长度
二、向量化计算两两重叠百分比
利用分组和自连接替代循环,大幅提升效率,同时自动处理所有两两组合:
# 对每行的来源做两两组合,避免重复计算(如AB和BA) pairwise_overlap <- data_long %>% inner_join(data_long, by = "row_id", suffix = c("_x", "_y")) %>% filter(source_x < source_y) %>% rowwise() %>% mutate( overlap = max(0, min(Stop_x, Stop_y) - max(Start_x, Start_y)), pct_x = (overlap / length_x) * 100, pct_y = (overlap / length_y) * 100, reciprocal_overlap = min(pct_x, pct_y) ) %>% ungroup() %>% select(row_id, source_x, source_y, reciprocal_overlap) # 转换回宽格式,与原数据合并 result <- data %>% mutate(row_id = row_number()) %>% left_join( pairwise_overlap %>% pivot_wider( names_from = c(source_x, source_y), values_from = reciprocal_overlap, names_prefix = "overlap_reciproq_" ), by = "row_id" ) %>% select(-row_id)
三、适配新增来源的优势
当新增来源D(添加StartD和StopD列)时,仅需重新运行上述代码:
- 长格式转换会自动识别
StartD/StopD,生成source="D"的行 - 自连接会自动生成AD、BD、CD的两两组合,无需手动修改计算逻辑
- 最终结果会自动添加
overlap_reciproq_AD、overlap_reciproq_BD、overlap_reciproq_CD列
结果验证
运行上述代码后,得到的结果与原循环代码完全一致,且处理效率更高(大数据量下,向量化操作比循环快数个数量级)。
内容的提问来源于stack exchange,提问作者Av65
相关产品推荐
相关产品推荐

