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
相关产品推荐
相关产品推荐

