不能直接用 np.unique 做分组聚合,因其仅返回去重值和索引,缺乏内置聚合逻辑,易引发索引越界、NaN 处理不当、性能差及 dtype 陷阱等问题。

为什么不用 np.unique 直接做分组聚合?
因为 np.unique 默认只返回去重值和索引,不带聚合逻辑。你拿到 unique_vals 和 inverse 后,还得自己按组切片、计算,稍不注意就索引错位或漏掉空组。
常见错误现象:IndexError: index 5 is out of bounds for axis 0 with size 5 —— 多半是用 inverse 当下标直接索引原数组,但 inverse 最大值可能等于数组长度(即越界)。
- 正确做法:用
np.split或布尔索引配合np.where,而不是直接用inverse做下标 - 如果分组键含
NaN,np.unique会把它们全归为一组,但np.isnan判断失效,得单独处理 - 性能上,纯 NumPy 循环比
pandas.groupby慢 3–5 倍,除非你明确不能引入 pandas
用 np.unique + np.where 实现安全分组
核心思路:先用 np.unique 获取唯一键和首次出现位置,再用 np.where 逐个找出每组对应的数据下标,避免索引越界。
假设你有分组键 group_ids 和待聚合数组 values:
立即学习“Python免费学习笔记(深入)”;
unique_keys, first_idx = np.unique(group_ids, return_index=True) # 确保 first_idx 是升序,否则 np.where 可能乱序 sort_order = np.argsort(first_idx) unique_keys = unique_keys[sort_order] <p>results = [] for key in unique_keys: mask = group_ids == key group_vals = values[mask] results.append(np.mean(group_vals)) # 替换为你需要的聚合函数
- 别用
np.where(group_ids == key)[0]再取[0],多此一举;直接用布尔索引更稳 - 如果
group_ids是字符串或对象类型,==可能慢,改用np.array_equal配合循环不现实,建议先转成 category 编码 - 空组不会报错,但
group_vals会是空数组,np.mean([])返回nan,需提前判断len(group_vals) == 0
替代方案:用 scipy.ndimage.labeled_comprehension
当分组键是整数且从 0 开始连续时,这个函数比手写循环快得多,也自动跳过标签 0(可设 index=None 覆盖)。
适用场景:图像标签图聚合、区域统计、整数型离散分组。
from scipy.ndimage import labeled_comprehension
result = labeled_comprehension(
values, group_ids,
index=np.unique(group_ids),
func=np.sum,
out_dtype=float,
default=0
)-
index必须显式传入,不能省略,否则只对非零标签生效 -
func只接受标量函数,不支持lambda x: (x.min(), x.max())这种多返回值 - 如果
group_ids里有负数或 float,会静默失败,返回全 0 数组,务必先检查np.issubdtype(group_ids.dtype, np.integer)
容易被忽略的 dtype 陷阱
聚合结果的 dtype 不由输入决定,而由你调用的函数和 out_dtype(如有)共同控制。比如 np.sum 对 int32 数组默认返回 int64,但 np.mean 强制转 float64。
- 如果你要保持内存紧凑,聚合前先
values = values.astype(np.float32),再传给np.mean - 用
np.bincount替代循环求和时,只支持非负整数group_ids,且最大值不能超2**31-1,否则溢出不报错 - 混合类型(如 int + float)参与聚合,NumPy 会自动 upcast,但
np.unique在 object 数组上极慢,尽量避免
最麻烦的其实是缺失值传播方式不一致:有些函数(如 np.nansum)跳过 nan,有些(如 np.sum)直接返回 nan。别依赖默认行为,显式选带 nan 前缀的函数,或者先用 np.nan_to_num 填充。


















