numpy.argsort()会大幅增加数组内存吗?如何低内存高效排序?
我有一个名为combinations的numpy数组(维度为37,933,056行×3列,类型为np.float32),还有一个一维数组err_np(共37,933,056个元素,类型为np.float16),用来存储每组3值组合的百分比误差。
我需要按绝对误差对combinations排序,执行了如下代码:
from sys import getsizeof as size import numpy as np # 此处省略其他操作 print(size(combinations)) # 120 - **数值过低** print(len(combinations)) # 37,933,056 print(combinations) # 数组内容正常 combinations = combinations[abs(err_np).argsort()] print(size(combinations)) # 455,196,792 - **数值过高** print(len(combinations)) # 37,933,056 print(combinations) # 数组已正确排序
我原本以为排序前的内存数值有误,但用的是sys.getsizeof(),且数组内容显示正常。
请问有没有方法能用更少内存对这个numpy数组排序?同时要尽可能高效,因为数组规模很大。排序后我会删除err_np,不需要保留它。
先理清内存显示异常的原因
sys.getsizeof()仅返回numpy数组对象本身的大小,而非存储数据的总内存。排序前的数组如果是视图(view)而非独立副本,getsizeof()只会返回对象头的大小(比如120字节);而combinations[abs(err_np).argsort()]会创建数组的完整副本,此时getsizeof()才会返回数据+对象头的总大小,这就是数值突变的原因。
低内存排序的可行方案
1. 原地修改数组(最优内存效率)
如果内存能容纳原数组的大小,用np.take()配合out参数直接将排序结果写入原数组,避免额外内存开销:
# 生成排序索引 sorted_indices = abs(err_np).argsort() # 原地替换,不创建新数组 np.take(combinations, sorted_indices, axis=0, out=combinations)
操作完成后,combinations直接变为排序后的结果,不会额外占用一倍内存。
2. 分块排序(内存紧张时)
若内存不足以容纳完整数组副本,可将数组拆分为小块分别排序后写入原数组:
block_size = 1_000_000 # 根据自身内存情况调整块大小 sorted_indices = abs(err_np).argsort() # 逐块处理 for i in range(0, len(sorted_indices), block_size): idx_block = sorted_indices[i:i+block_size] combinations[i:i+block_size] = combinations[idx_block]
这种方法每次仅处理一小块数据,内存占用大幅降低,仅牺牲少量排序效率。
3. 结构化数组合并排序(逻辑直观)
将combinations与err_np合并为结构化数组,按误差列排序后拆分回原数组:
# 合并为带字段名的结构化数组 combined = np.rec.fromarrays( [combinations[:,0], combinations[:,1], combinations[:,2], abs(err_np)], names='col0,col1,col2,err' ) # 按误差字段排序 combined.sort(order='err') # 拆分回原数组格式 combinations = np.column_stack([combined.col0, combined.col1, combined.col2]) # 立即释放临时变量内存 del err_np, combined
内存开销与直接排序接近,但逻辑更清晰,适合关联数据排序场景。
额外优化建议
- 排序后调用
gc.collect()手动触发垃圾回收,释放err_np和临时索引数组的内存。 - 生成排序索引后立即删除
err_np,提前释放内存:sorted_indices = abs(err_np).argsort() del err_np # 再执行排序操作
内容的提问来源于stack exchange,提问作者LucianoSaldivia

