You need to enable JavaScript to run this app. 高效实现numpy数组匹配pandas DataFrame区间并获取对应行索引
优惠活动
大模型
产品
解决方案
定价
更多

高效实现numpy数组匹配pandas DataFrame区间并获取对应行索引

原实现性能瓶颈说明

原双层遍历方案存在三个核心性能问题:

  • 时间复杂度为O(N*M)(N为stats数组行数,M为DataFrame行数),没有利用两个输入已经提前完成升序排列的优势
  • 全量循环都运行在Python解释器层,没有借助numpy/pandas的底层C层计算能力
  • 每次循环用x in range(start, end)做边界判断,哪怕range对象本身是轻量的,单次判断的Python层开销远高于直接做数值大小比较
优化方案

方案1:纯Numpy向量化广播(代码极简,适合中小数据量)

你的stats数组仅352行,属于极小量级,直接通过numpy广播一次性计算所有匹配关系,所有运算都在C层完成,比原循环快两个数量级以上。
代码示例:

import numpy as np
import pandas as pd
# 拆分stats前两列(x,y)为列向量,形状为(352, 1)
x_col = stats[:, [0]]
y_col = stats[:, [1]]
# 拆分DataFrame四列边界为行向量,形状为(1, M)
hpos_row = df["hpos"].to_numpy()[None, :]
hpos_end_row = df["hpos_end"].to_numpy()[None, :]
vpos_row = df["vpos"].to_numpy()[None, :]
vpos_end_row = df["vpos_end"].to_numpy()[None, :]
# 广播计算全量匹配条件:x落在[hpos, hpos_end) 且 y落在[vpos, vpos_end)
# 若需要闭区间判断,将<替换为<=即可
x_match = (x_col >= hpos_row) & (x_col < hpos_end_row)
y_match = (y_col >= vpos_row) & (y_col < vpos_end_row)
full_match = x_match & y_match
# 提取每个stats行对应的匹配DataFrame索引
matched_candidate = df.index[full_match.argmax(axis=1)]
has_match = full_match.any(axis=1)
# 构建最终结果:key为stats数组行号,value为匹配到的DataFrame行索引
matched_index_map = {
    stats_row: matched_candidate[stats_row]
    for stats_row in range(len(stats))
    if has_match[stats_row]
}

对于352行的stats数组,哪怕DataFrame有上万行,整体计算耗时也在毫秒级。

方案2:结合二分查找的升序优化(复杂度更低,适合大数据量)

利用两个输入已经完成升序的前置条件,通过二分查找快速缩小候选行范围,不需要遍历全量DataFrame,整体时间复杂度仅为O(N log M),数据量越大优势越明显:

# 提前将DataFrame列转为numpy数组,减少pandas索引开销
hpos_arr = df["hpos"].to_numpy()
hpos_end_arr = df["hpos_end"].to_numpy()
vpos_arr = df["vpos"].to_numpy()
vpos_end_arr = df["vpos_end"].to_numpy()
df_idx_arr = df.index.to_numpy()
matched_index_map = {}
for stats_row_idx, (x, y) in enumerate(stats[:, :2]):
    # 二分定位所有hpos <=x的DataFrame行的截止位置
    h_upper_pos = hpos_arr.searchsorted(x, side="right")
    h_candidate_pos = np.arange(h_upper_pos)
    # 筛选满足hpos_end >x的行
    h_valid_mask = hpos_end_arr[h_candidate_pos] > x
    valid_candidate = h_candidate_pos[h_valid_mask]
    if len(valid_candidate) == 0:
        continue
    # 在候选行中二分定位所有vpos <=y的位置(因df已按vpos升序,候选行的vpos自然有序)
    v_upper_pos = np.searchsorted(vpos_arr[valid_candidate], y, side="right")
    v_candidate_pos = valid_candidate[:v_upper_pos]
    # 筛选满足vpos_end >y的最终匹配行
    v_valid_mask = vpos_end_arr[v_candidate_pos] > y
    if v_valid_mask.any():
        matched_pos = v_candidate_pos[v_valid_mask][0]
        matched_index_map[stats_row_idx] = df_idx_arr[matched_pos]
关键注意点
  • 原代码中x in range(a, b)等价于左闭右开区间a <=x <b,若实际业务为闭区间判断,直接将代码中的<替换为<=即可。
  • 因两个输入已经完成升序排序,上述方案不需要额外执行排序操作,直接复用现有顺序即可获得最高效率。
  • 若存在单个stats行匹配多个DataFrame行的情况,可根据业务需求调整取数逻辑(默认取第一个匹配行,符合升序排列下的区间最近邻匹配逻辑)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.11.01 01:06:04