使用 np.lexsort 实现按索引分组的二维数组最大值提取

本文介绍如何在不使用显式循环的前提下,对二维 NumPy 数组按第三行(索引列)分组,并高效提取每组中第一行的最大值及其对应第二行的关联值。核心方法是利用 np.lexsort 对数据进行稳定排序与布尔掩码筛选。

本文介绍如何在不使用显式循环的前提下,对二维 numpy 数组按第三行(索引列)分组,并高效提取每组中第一行的最大值及其对应第二行的关联值。核心方法是利用 `np.lexsort` 对数据进行稳定排序与布尔掩码筛选。

在处理结构化数值数据时,常需按某列(如类别索引)分组,并提取每组中另一列(如置信度、得分)的最大值,同时保留其关联字段(如 ID、时间戳等)。对于形状为 (3, k) 的二维数组 data,其中 data[0] 为待比较数值、data[2] 为分组索引、data[1] 为需同步提取的关联值,目标是:对每个唯一索引,返回该索引下 data[0] 的最大值,以及同一位置的 data[1] 和 data[2] 值

直接使用 np.maximum.reduceat 仅适用于一维聚合,无法天然保持多行间的列对齐关系。而基于 np.unique(..., return_index=True) 的方案虽能获取首个出现位置,但 reduceat 本身不提供“取最大值所在原始索引”的能力,导致无法准确拉取 data[1] 和 data[2] 的对应元素。

此时,np.lexsort 成为更优解:它支持多关键字稳定排序(主键优先级由参数顺序决定),且排序后相同索引的元素将连续排列,最大值自然位于每组末尾(若按升序排)或开头(若按降序排)。我们采用“先按索引 data[2] 升序,再按数值 data[0] 升序”双关键字排序,确保每组内数值递增——这样每组最后一个元素即为该索引下的最大值。

关键技巧在于构造布尔掩码以提取每组末尾元素:

故事AI绘图神器

文本生成图文视频的AI工具,无需配音,无需剪辑,快速成片,角色固定。

下载

  • np.lexsort(data[[0, 2]]) 返回按 data[2](主)、data[0](次)升序排列的索引;
  • 排序后,相同 data[2] 的元素连续,只需检测 data[2] 的相邻变化点;
  • data[2,:-1] != data[2,1:] 标记组边界(前一个索引 ≠ 后一个索引的位置),再拼接 True(保留最后一组末尾)得到每组末尾索引掩码。

完整实现如下:

import numpy as np

data = np.array([
    [0.45114132, 0.31522008, 0.66176217, 0.45114132, 0.26872137],
    [11.        ,  6.        , 10.        ,  4.        ,  8.        ],
    [0.        ,  0.        ,  0.        ,  1.        ,  1.        ]
])

# 按索引(data[2])主序、数值(data[0])次序升序排序
sorted_idx = np.lexsort(data[[0, 2]])  # 注意:lexsort 以最后维度为第一关键字,故传入 [0,2] 表示先按 data[2] 再按 data[0]
sorted_data = data[:, sorted_idx]

# 构造掩码:标记每组(相同 data[2])的最后一个位置
mask = np.r_[sorted_data[2, :-1] != sorted_data[2, 1:], True]

# 提取结果
result = sorted_data[:, mask]

print(result)
# 输出:
# [[0.66176217 0.45114132]
#  [10.         4.        ]
#  [ 0.         1.        ]]

⚠️ 注意事项

  • np.lexsort(keys) 中 keys 是一个序列,越靠后的数组优先级越高。因此 data[[0, 2]] 表示先按 data[2](索引)分组,组内再按 data[0](数值)排序;
  • 若 data[2] 含非整数或浮点索引(如 0.0, 1.0),需确保其相等性判断稳定(浮点精度问题下建议先 np.round() 或转为整型);
  • 该方法时间复杂度为 O(k log k),远优于 Python 循环,且完全向量化,适合百万级数据;
  • 若需降序取最大值(即每组首元素),可改用 np.lexsort([-data[0], data[2]]) 并用 mask = np.r_[True, sorted_data[2, 1:] != sorted_data[2, :-1]]。

综上,np.lexsort + 掩码筛选是解决“分组取极值并保关联字段”问题的简洁、高效、纯 NumPy 方案,兼顾可读性与性能,是科学计算中值得掌握的核心技巧之一。

相关推荐:

Excel标准差该用哪个 用Excel求分组数据的标准差

品牌型号:联想小新13 pro 系统:Windows 11 软件版本:Microsoft 365 Excel 内置丰富的函数体系,能够高效完成各类数据运算与分析任务。在实际应用中,标准差作为衡量数据离散程度的重要统计指标,常被用于评估数据波动性与稳定性。那么,在 Excel 中计算标准差时,究竟该选用哪一个函数?面对分组数据又该如何准确求得其标准差?本文将围绕这两个关键问题展开详细说明。 一、Ex...

火狐浏览器多账号容器的颜色与专属图标怎么改_火狐浏览器标签分组视觉优化

可通过为容器设置专属颜色与图标、启用标签页顶部色带、绑定域名自动着色及用Firefox Color统一主题来强化视觉区分:一、在“管理容器”中编辑颜色和图标;二、about:config启用privacy.userContext.ui.colorfulTabs;三、将域名绑定至容器;四、用Firefox Color协调全局配色。 如果您在火狐浏览器中启用了多账户容器功能,但发现不同容器的标签页在视...

Edge浏览器标签页分组怎么整理_Edge浏览器窗口管理方法

Edge浏览器可通过标签页分组功能结构化管理大量标签页:一、启用edge://flags中TabGroups等实验功能;二、拖拽创建带名与色的分组;三、用AI自动整理;四、右键或拖拽将标签页加入已有分组;五、折叠/展开分组优化空间。 如果您在使用Edge浏览器时打开大量标签页,导致标签栏拥挤混乱、难以快速定位目标页面,则可通过标签页分组功能实现结构化整理。以下是多种可行的窗口管理方法: 一、启用标...

UC浏览器多窗口管理:标签页分组与快速切换术【技巧】

UC浏览器多窗口与标签页管理核心是任务分区:通过标签分组(如“工作资料”)、固定高频页、新建独立窗口实现物理隔离,并支持边缘滑动、鼠标滚轮、快捷键等零停顿切换方式。 UC浏览器的多窗口和标签页管理,核心是把“页面堆叠”变成“任务分区”。真正高效不是开更多标签,而是让每个标签或窗口都对应明确用途,切换时不用找、不犹豫。 标签页分组:按用途归类,一眼识别 分组不是简单合并,而是给网页打上语义标签。比如...

谷歌浏览器标签页分组怎么整理_谷歌浏览器窗口管理方法

可借助Chrome原生标签页分组功能及配套管理手段系统化整理大量标签页:一、创建新分组;二、批量多选创建分组;三、向现有分组添加标签页;四、跨窗口整合标签页;五、用扩展增强功能;六、启用垂直标签栏;七、用书签栏模拟静态分组。 如果您在谷歌浏览器中打开大量标签页,导致标签栏拥挤混乱、难以定位关键页面,则可借助原生标签页分组功能及配套管理手段进行系统化整理。以下是具体操作步骤: 一、创建新标签页分组 ...

为什么Figma的Variable变量管理很混乱_通过Collection自动化分组

Figma中Variables需通过Collection实现自动化分组与语义管理:一、创建Tokens/Core、Theme、State三个Collection;二、用color/primary/500等斜杠命名自动归类;三、用Tag跨Collection筛选;四、用Tokens Sync插件批量校验同步;五、按Collection导出独立JSON Schema文件。 ☞☞☞AI 智能聊天, 问答...
· AI · 180229

如何在Python中为测试用例添加自定义标记_通过pytest.mark进行分类执行

pytest.mark 必须作为装饰器直接作用于测试函数或类,不可写在函数外部;可叠加多个标记,执行时用 -m "mark1 and mark2" 等布尔表达式筛选;未注册标记会触发警告,需在 pytest.ini 或 pyproject.toml 中声明以避免。 pytest.mark 能不能直接用在函数外面 不能。所有 pytest.mark 必须装饰在测试函数或类上,写在函数定义之外会报 S...
· Python · 271593

如何在Python中构建树状结构的递归生成器_使用yield from简化递归代码

yield from 在树遍历中省去了手动展开子生成器的繁琐与风险,自动处理委托调用、异常传播、StopIteration 捕获及协程方法转发,确保扁平化输出和行为一致性。 yield from 在树遍历中到底省了什么 直接说结论:yield from 不是语法糖,它把递归生成器的“委托调用”从手动循环 + yield 拆解成一条语句,避免了中间迭代器被提前耗尽、异常传播断裂、或 StopIter...
· Python · 193784