mongodb不支持numpy类型,需将np.ndarray转list、np标量转python原生类型;插入前必须用.tolist()和.item()显式转换,否则因不可哈希报typeerror。

直接用 numpy.ndarray 存 MongoDB 会报错:MongoDB 只认 Python 原生类型(list、dict、float、int 等),不认 np.float64、np.int32 这类 NumPy 类型。必须显式转换。
为什么 collection.insert_many() 会抛 TypeError: unhashable type: 'numpy.ndarray'
这不是 MongoDB 驱动的 bug,而是 PyMongo 在序列化前做类型检查时,发现文档里混入了不可哈希(且非标准 JSON 兼容)的 NumPy 类型,比如 np.array([1, 2, 3]) 或嵌套的 np.float64(3.14)。即使你只传一个 dict,只要它某个 value 是 np.float64,就会卡住。
常见触发场景:
- 用
pandas.DataFrame.to_dict('records')后直接插入 —— DataFrame 的列若为float64,转出的 dict value 就是np.float64 - 从
np.loadtxt()或np.random.rand()生成数据后,拼成字典没做类型清洗 - 用
np.matrix(已弃用)或np.ndarray直接当字段值(如{'data': my_array})
批量写入前必须做 dtype 归一化:用 item() 和 tolist()
核心原则:把所有 NumPy 标量转成 Python 原生标量,把所有 NumPy 数组转成 Python list。别依赖自动转换,PyMongo 不做这件事。
实操建议:
- 单个标量(如
np.float64、np.int32):统一调用.item()方法 → 返回float或int - 一维/二维
ndarray:用.tolist()→ 返回嵌套list,可被 JSON 序列化 - 避免对整个大数组反复调用
tolist()(内存拷贝开销大),如果只是存元数据(如 shape/dtype),用arr.shape、str(arr.dtype)即可 - 不要用
json.dumps(arr, default=str)—— 它把数字变成字符串,丢失数值语义
示例:
import numpy as np
from pymongo import MongoClient
<h1>假设这是你的原始矩阵数据</h1><p>matrix = np.random.rand(3, 4).astype(np.float32)</p><h1>✅ 正确:转为 list of lists,每个元素是 float(非 np.float32)</h1><p>records = [
{
"id": i,
"values": row.tolist(), # ✅ 一维 ndarray → list
"max_val": row.max().item(), # ✅ np.float32 → float
"timestamp": "2024-06-01"
}
for i, row in enumerate(matrix)
]</p><p>client = MongoClient()
db = client["mydb"]
col = db["matrices"]
col.insert_many(records) # ✅ 成功
</p>
读取后还原为 numpy.ndarray 要注意 shape 信息丢失
MongoDB 存的是扁平化的 list,没有 shape 元数据。如果你存的是二维矩阵,只存 values: [[1,2],[3,4]],读出来是 list,得靠额外字段还原维度。
推荐做法:
- 写入时显式存
"shape": [3, 4]和"dtype": "float32" - 读取后用
np.array(doc["values"], dtype=doc["dtype"]).reshape(doc["shape"]) - 避免用
np.matrix—— 它在 NumPy 1.26+ 已彻底移除,且语义上不如ndarray清晰 - 如果矩阵很大(>1MB),考虑用 GridFS 存二进制(
arr.tobytes()),但需自己管理 dtype/shape/endianness
最易被忽略的一点:MongoDB 对单文档有 16MB 上限,而 ndarray.tolist() 后体积可能膨胀 2–3 倍(因字符串化和 JSON 开销)。存大矩阵前务必估算 len(json.dumps({"values": big_arr.tolist()})),超限就得切片或换 GridFS。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











