You need to enable JavaScript to run this app. Pandas按范围重叠百分比保留高分行及提取无重叠行的实现问题
优惠活动
大模型
产品
解决方案
定价
更多

Pandas按范围重叠百分比保留高分行及提取无重叠行的实现问题

现有代码的核心逻辑缺陷
  • 非重叠行判定逻辑完全反向:现有逻辑是只要当前行和某一个其他行未达到重叠阈值,就把当前行加入非重叠集合,完全没有校验「当前行与所有其他行都不满足重叠阈值」这个核心判定条件,只要有一行和它不重叠就算非重叠,最终自然所有行都被留存。
  • 变量作用域完全混乱:函数内部直接调用了外部的x、x_percentage变量,没有使用传入的df_overlap、threshold参数,每次递归进来都会把df_overlap、df_nonoverlap重置为空DataFrame,递归逻辑从根本上失效,根本没起到层层整合的作用。
  • 重叠计算效率极低:为了算重叠长度直接把连续区间展开为列表、转集合,当区间范围跨度稍大时会直接占用大量内存,完全没有必要——连续区间的重叠长度可以通过简单公式直接计算,不需要展开。
  • 重叠保留逻辑存在级联漏判:现有逻辑只会两两比对重叠关系,没有处理级联重叠的场景——比如A和B重叠、B和C重叠,哪怕A和C不重叠,三者也属于同一个重叠连通簇,需要在整个簇内选取最高分的行留存,现有逻辑会把这种场景下的多个行重复纳入待整合集合,最终选不出正确的高分行。
  • 重复行冗余:现有逻辑碰到重叠就把两个行都加入待整合DataFrame,会产生大量重复条目,进一步拉低运行效率。
修正后的实现思路
  • 首先基于重叠判定阈值,把所有存在连通重叠关系的行划分为不同的连通簇:只要两个行的重叠比例超过阈值,就算在同一个连通簇内,不管中间隔了几级重叠关联。
  • 对每个连通簇单独处理:
    • 簇内只有1行的:就是真正和所有其他行都没有达到重叠阈值的非重叠行,直接留存
    • 簇内有≥2行的:直接选取簇内score最高的那一行留存,其余同簇的行全部丢弃
  • 全程使用公式计算重叠长度,不展开大区间,避免无效的内存占用。
可直接运行的修正代码
import pandas as pd
import numpy as np
from scipy.sparse import csr_matrix
from scipy.sparse.csgraph import connected_components

def filter_overlap_ranges(input_df, overlap_threshold_pct, overlap_denominator="union"):
    """
    按重叠阈值筛选区间:同重叠连通簇内仅保留最高分,单成行归为非重叠行
    参数:
        input_df: 包含id, range_start, range_end, score列的输入DataFrame
        overlap_threshold_pct: 重叠判定的百分比阈值(0-100)
        overlap_denominator: 重叠比例计算方式,可选"union"(重叠长度/两区间并集长度)、"short"(重叠长度/较短区间长度)
    返回:
        筛选后的留存行DataFrame
    """
    df = input_df.copy().reset_index(drop=True)
    row_count = len(df)
    # 初始化邻接矩阵,标记两行是否满足重叠阈值
    adj_matrix = np.zeros((row_count, row_count), dtype=bool)

    for i in range(row_count):
        rs_i, re_i = df.loc[i, ["range_start", "range_end"]]
        len_i = re_i - rs_i
        for j in range(i+1, row_count):
            rs_j, re_j = df.loc[j, ["range_start", "range_end"]]
            len_j = re_j - rs_j
            # 直接通过公式计算重叠长度,无需展开区间
            overlap_len = max(0, min(re_i, re_j) - max(rs_i, rs_j))
            # 按指定规则计算重叠比例
            if overlap_denominator == "union":
                total_base = len_i + len_j - overlap_len
            else:
                total_base = min(len_i, len_j)
            overlap_pct = (overlap_len / total_base) * 100 if total_base > 0 else 0
            # 达到阈值则标记两行连通
            if overlap_pct >= overlap_threshold_pct:
                adj_matrix[i, j] = adj_matrix[j, i] = True

    # 识别所有重叠连通簇
    n_cluster, cluster_labels = connected_components(csr_matrix(adj_matrix), directed=False)
    keep_rows = []
    # 每个连通簇内仅取score最高的行留存
    for c_id in range(n_cluster):
        cluster_data = df[cluster_labels == c_id]
        best_row = cluster_data.sort_values("score", ascending=False).iloc[0]
        keep_rows.append(best_row)
    return pd.DataFrame(keep_rows).reset_index(drop=True)
测试验证

使用你提供的测试数据运行:

# 测试输入
data = {'id':['id1', 'id2', 'id3', 'id4', 'id5', 'id6'],
       'range_start':[1,12,11,1,20, 10],
       'range_end':[4,15,15,6,23,16],
       'score':[3,1,8,2,5,1]}
input_df = pd.DataFrame(data, columns=['id', 'range_start', 'range_end', 'score'])

# 运行筛选,阈值设为20%,重叠比例按并集长度计算
result = filter_overlap_ranges(input_df, overlap_threshold_pct=20, overlap_denominator="union")

运行结果完全匹配你给出的期望输出:

id  range_start  range_end  score
0  id1            1          4      3
1  id3           11         15      8
2  id5           20         23      5

结果对应的判定逻辑:

  • 第一连通簇:id2、id3、id6三者互相存在重叠,簇内最高分是id3(score=8),其余两个丢弃
  • 第二连通簇:id1和id4重叠,簇内最高分是id1(score=3),id4丢弃
  • 第三连通簇:id5没有和任何行达到重叠阈值,单独成簇直接留存

内容的提问来源于stack exchange,提问作者Cody Glickman

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.23 12:36:04