You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

psycopg2插入含NaN的numpy数组报错,求正确实现方法

含NaN的NumPy数组插入PostgreSQL表的问题与解决

问题背景

最初的适配器代码可以正常插入多维NumPy数组:

def adapt_numpy_array(numpy_array):
    return AsIs(numpy_array.tolist())

register_adapter(np.ndarray, adapt_numpy_array)
register_adapter(np.int32, AsIs)
register_adapter(np.int64, AsIs)
register_adapter(np.float64, AsIs)

但当数组包含NaN时,触发PostgreSQL错误:

psycopg2.errors.UndefinedColumn: FEHLER: Spalte »nan« existiert nicht
LINE 2: ...6000.0, 692000.0, 732000.0, 830000.0, 928000.0], [nan, nan, ..

修改适配器后:

def adapt_numpy_array(numpy_array):
    return numpy_array.tolist()

def nan_to_null(f,
        _NULL=psycopg2.extensions.AsIs('NULL'),
        _Float=psycopg2.extensions.Float):
    if not np.isnan(f):
        return _Float
    return _NULL

register_adapter(float, nan_to_null)

又出现新错误:

AttributeError: 'list' object has no attribute 'getquoted'

报错原因

  1. 第一个错误:用AsIs直接将NumPy数组转成列表插入时,PostgreSQL会把nan当成列名——因为它没有被转成SQL的NULL,而是作为字符串"nan"被拼接进SQL语句,数据库误以为这是一个不存在的列。
  2. 第二个错误:修改后的adapt_numpy_array直接返回原生Python列表,但psycopg2没有内置的列表适配器(除非是特定数组类型的适配逻辑),它期望适配器返回实现了getquoted方法的psycopg2扩展对象(比如AsIs、Float),而非原生列表。另外,注册的float适配器只处理单独的float对象,psycopg2不会自动递归遍历列表去应用元素适配器。

正确解决方案

要同时处理多维数组和NaN,需要递归处理数组结构,把NaN替换为SQL的NULL,同时确保返回psycopg2能识别的适配对象。以下是两种可行方案:

方案一:递归生成合法SQL数组字符串

import numpy as np
import psycopg2
from psycopg2.extensions import AsIs, register_adapter

def numpy_to_sql_array(arr):
    # 递归处理多维数组
    if isinstance(arr, np.ndarray):
        return "ARRAY[" + ", ".join(numpy_to_sql_array(x) for x in arr) + "]"
    elif isinstance(arr, float) and np.isnan(arr):
        return "NULL"
    elif isinstance(arr, (np.number, int, float)):
        return str(arr)
    else:
        return str(arr)

def adapt_numpy_array(numpy_array):
    return AsIs(numpy_to_sql_array(numpy_array))

# 注册各类适配器
register_adapter(np.ndarray, adapt_numpy_array)
register_adapter(np.int32, AsIs)
register_adapter(np.int64, AsIs)
register_adapter(np.float64, lambda x: AsIs(str(x)) if not np.isnan(x) else AsIs("NULL"))

该方案将NumPy数组递归转换为PostgreSQL可识别的ARRAY[]格式字符串,同时把NaN替换为NULL,再用AsIs直接传入SQL语句。

方案二:利用psycopg2的adapt函数处理列表

import numpy as np
import psycopg2
from psycopg2.extensions import register_adapter, AsIs, Float

def nan_to_null(f):
    if isinstance(f, float) and np.isnan(f):
        return AsIs("NULL")
    # 非NaN数值用默认Float适配器处理
    return Float(f)

# 注册各类数值类型适配器
register_adapter(float, nan_to_null)
register_adapter(np.int32, lambda x: AsIs(str(x)))
register_adapter(np.int64, lambda x: AsIs(str(x)))
register_adapter(np.float64, nan_to_null)

def adapt_numpy_array(numpy_array):
    # 转成Python列表后,用psycopg2的adapt处理整个列表
    return psycopg2.extensions.adapt(numpy_array.tolist())

register_adapter(np.ndarray, adapt_numpy_array)

该方案借助psycopg2的adapt函数处理列表,它会遍历列表元素并应用对应的适配器,将NaN转为NULL,同时把整个列表正确转成SQL数组。

关键注意点

  • psycopg2不会自动递归处理容器(如列表、数组)内的元素,必须确保容器本身被适配,或元素的适配器能被正确应用。
  • 不能直接返回原生列表给psycopg2,必须返回其支持的适配对象(如AsIs、adapt生成的对象)。
  • PostgreSQL没有NaN概念,必须将NumPy的NaN转换为SQL的NULL才能正确插入。

内容的提问来源于stack exchange,提问作者Tengrath

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 11:17:38