You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

不同来源区间重叠百分比计算的优化与扩展性需求问询

问题

我有一个包含不同来源(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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 19:34:55