大尺寸numpy数组触发AttributeError:'bool'对象无'any'属性问题排查
大尺寸数组下
any()调用抛出AttributeError的根因与修复方案 问题重现
运行以下代码,当nrows超过75000/80000时,会抛出AttributeError: 'bool' object has no attribute 'any':
import pandas as pd import numpy as np nrows = 80000 df = pd.DataFrame(np.random.randint(0,25,size=(nrows, 8)), columns=list('ABCDEFGH')) array_val = df.values array_obj = ((array_val == array_val[:,None]).any(axis=-1)) print(array_obj.dtype) print(array_obj.shape)
小尺寸(如5000、20000)下代码正常,返回形状为(nrows, nrows)的布尔数组。
根因分析
核心问题是超大数组的内存分配失败导致比较操作异常退化:
- 执行
array_val == array_val[:,None]时,广播机制会生成一个形状为(nrows, nrows, 8)的三维布尔数组。当nrows=80000时,该数组总元素数为80000*80000*8 = 5.12e10,即使每个元素占1字节,也需要约47.7GB内存,远超常规机器的内存容量。 - 正常情况下numpy应该抛出
MemoryError,但部分环境(受numpy版本、操作系统内存管理机制影响)中,内存分配失败并未触发标准错误,反而导致比较操作返回了一个标量布尔值而非预期的数组,后续调用.any()自然会抛出属性错误。
修复方案
方案1:基于哈希值的快速比较
将每行数据转换为哈希值,通过比较哈希值来判断行是否相等,内存占用从O(n²*8)降至O(n),效率大幅提升:
import pandas as pd import numpy as np nrows = 80000 df = pd.DataFrame(np.random.randint(0,25,size=(nrows, 8)), columns=list('ABCDEFGH')) array_val = df.values # 计算每行的哈希值(使用numpy的向量化操作) row_hashes = np.array([hash(tuple(row)) for row in array_val]) # 比较哈希值得到对称布尔矩阵 array_obj = row_hashes[:, None] == row_hashes print(array_obj.dtype) print(array_obj.shape) # 输出(80000, 80000)
注意:哈希存在极小碰撞概率,若需要绝对准确,可以结合哈希+逐行验证碰撞行。
方案2:分块处理
将数组拆分为多个小块,逐块比较后拼接结果,避免一次性分配超大内存:
import pandas as pd import numpy as np nrows = 80000 block_size = 10000 # 根据内存调整块大小 df = pd.DataFrame(np.random.randint(0,25,size=(nrows, 8)), columns=list('ABCDEFGH')) array_val = df.values array_obj = np.zeros((nrows, nrows), dtype=bool) for i in range(0, nrows, block_size): block = array_val[i:i+block_size] # 块与整个数组比较,得到(block_size, nrows)的结果 block_result = (block[:, None] == array_val).any(axis=-1) array_obj[i:i+block_size] = block_result print(array_obj.dtype) print(array_obj.shape)
方案3:使用稀疏矩阵优化存储
若最终结果中相等的行占比极低,可以使用稀疏矩阵存储结果,进一步节省内存:
import pandas as pd import numpy as np from scipy.sparse import csr_matrix nrows = 80000 df = pd.DataFrame(np.random.randint(0,25,size=(nrows, 8)), columns=list('ABCDEFGH')) array_val = df.values row_hashes = np.array([hash(tuple(row)) for row in array_val]) # 找到所有相等的行对 equal_pairs = np.argwhere(row_hashes[:, None] == row_hashes) # 构建稀疏矩阵 array_obj = csr_matrix((np.ones(len(equal_pairs), dtype=bool), (equal_pairs[:,0], equal_pairs[:,1])), shape=(nrows, nrows)) print(array_obj.dtype) print(array_obj.shape)
环境验证
在numpy 1.26.4、Python 3.10、16GB内存的Linux环境下,nrows=80000时原代码直接抛出MemoryError,而使用哈希方案可正常运行并返回预期形状的数组。
内容的提问来源于stack exchange,提问作者bigchungus
相关产品推荐
相关产品推荐

