numpy.unique() 默认不保留原始顺序是因为内部调用 np.sort() 导致排序,需用 return_index=true 获取首次出现索引再索引原数组以保持顺序。

为什么 numpy.unique() 默认不保留原始顺序?
numpy.unique() 会先对数组排序再提取唯一值,这是它内部调用 np.sort() 导致的。所以即使输入是 [3, 1, 2, 1, 3],输出也是 [1, 2, 3] —— 顺序完全重排。如果你依赖元素首次出现的位置(比如处理时间序列、日志行或用户行为流),这个默认行为就不可接受。
用 np.unique() + return_index=True 手动还原顺序
核心思路:不直接要值,而是要每个唯一值「第一次出现的索引」,再用这些索引从原数组取值。这样既利用了 np.unique() 的高效去重逻辑,又绕过了它的排序副作用。
实操要点:
- 必须传参
return_index=True,否则拿不到索引 - 拿到的
indices是升序排列的(对应唯一值在原数组中首次出现的位置),直接用它索引原数组即可保持原始顺序 - 注意:
np.unique()对浮点数或 NaN 的行为需额外验证,NaN 默认被视作相等,但return_index返回的是第一个 NaN 的位置
import numpy as np arr = np.array([3, 1, 2, 1, 3, 4, 2]) _, idx = np.unique(arr, return_index=True) result = arr[np.sort(idx)] # 注意:idx 已是升序,但保险起见可显式 sort # result 是 [3 1 2 4],顺序与首次出现一致
避免常见坑:别用 list(dict.fromkeys(...)) 替代
虽然 Python 3.7+ 的 dict 保持插入顺序,list(dict.fromkeys(arr.tolist())) 看似简单,但它有硬伤:
Python 3.14.2是Python编程语言在2025年12月5日发布的稳定版本,属于3.14系列的第二个维护更新。该版本包含了18项修复,重点解决了多进程、数据类及正则表达式等模块的回归问题,并修复了CVE-2025-12084等安全漏洞。此版本标志着自由线程模式(移除GIL)正式获得官方支持,是Python发展的重要里程碑。
- 把整个 NumPy 数组转成 Python list,失去向量化优势,大数组(百万级)性能暴跌
- 类型退化:
int64变成int,float32可能变成float64 - 无法处理结构化数组或对象数组,
dict的 hash 要求严格,可能抛TypeError
纯 NumPy 方案始终优先——尤其当你的 pipeline 全程基于 ndarray 时。
特殊情况:需要同时去重 + 保持顺序 + 获取计数
如果还要知道每个唯一值出现了几次(比如统计频次),别拼凑多个 np.unique() 调用。一次调用全解决:
arr = np.array(['a', 'b', 'a', 'c', 'b']) _, idx, inv, counts = np.unique(arr, return_index=True, return_inverse=True, return_counts=True) ordered_unique = arr[np.sort(idx)] # ordered_unique = ['a' 'b' 'c'] # counts 对应的是排序后顺序的频次:[2, 2, 1] # 要匹配 ordered_unique 的频次,得用 counts[np.argsort(idx)] 或直接重排
注意:counts 默认按唯一值升序排列,不是按 ordered_unique 的顺序。真正匹配的写法是:counts[np.argsort(idx)],因为 idx 是各唯一值首次出现位置,np.argsort(idx) 给出它们在 ordered_unique 中的自然序号映射。
顺序敏感的去重看似简单,但一旦混入 NaN、复数、自定义 dtype 或需要附带元信息(索引/计数/逆映射),就很容易掉进隐式排序或类型转换的坑里。动手前先确认你的数据形态,再决定是否加 return_index 或其他返回参数。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










