Вставить массив numpy, содержащий подмассив, в PostgreSQL

#python #postgresql #numpy #psycopg2

#python #postgresql #numpy #psycopg2

Вопрос:

У меня есть массив numpy, который необходимо вставить в PostgreSQL. Массив numpy — это массив, содержащий подмассив, который уже находится в кортеже. Кортеж выглядит примерно так:

((1,0., -0.5, [0, 0], 1, 5)). — Я сократил массив, чтобы показать подмассив.

Кортеж содержит один элемент для вставки одной строки. Если бы я хотел вставить несколько строк, кортеж содержал бы несколько элементов.

Я зарегистрировал адаптеры PostgreSQL для numpy.uint32, numpy.float32 и numpy.ndarray.:

 from psycopg2.extensions import register_adapter, AsIs

def numpy2pyUInt32(self,npUInt32):
    return AsIs(npUInt32.item())
def numpy2pyFloat32(self,npFloat32):
    return AsIs(npFloat32.item())
def numpy2pyndarray(self,npndarray):
    return AsIs(npndarray.tolist())

register_adapter(numpy.uint32, self.numpy2pyUInt32)
register_adapter(numpy.float32, self.numpy2pyFloat32)
register_adapter(numpy.ndarray, self.numpy2pyndarray)
  

Чтобы вставить данные, я использую execute_values() следующую команду sql:
INSERT INTO mytable VALUES %s . Но когда я выполняю это, я получаю сообщение об ошибке от psycopg2 со словами:

 Traceback (most recent call last):  
  File "...", line 132, in <module>  
    ...
  File "...", line 113, in ...
    psycopg2.extras.execute_values (cur, sql_command, col_values)
  File "/.../lib/python3.8/site-packages/psycopg2/extras.py", line 1292, in execute_values
    cur.execute(b''.join(parts))
psycopg2.errors.SyntaxError: syntax error at or near "["
LINE 1: ...06346473842859,0.0,0.0,0.0,14.284889221191406,4,0,[0, 0],540...
                                                             ^
  

Дополнительная информация:

 data = ((1,0., -0.5, ..., [0, 0], 1, 5))
print(type(data)) # <class 'tuple'>
print(type(data[0])) # <class 'numpy.void'>
print(data[0].dtype) #
[('..', '<u4'), ('..', '<f4'), ('..', '<f4'), ('..', '<f4'), ('..', '<f4'),
 ('..', '<f4'), ('..', '<f4'), ('..', '<f4'), ('..', '<f8'), ('..', '<f4'),
 ('..', '<f4'), ('..', '<f4'), ('..', 'u1'), ('..', 'u1'),
 ('..', 'u1',(2,)), # sub-array
 ('..', '<u4'), ('..', '<u4'), ('..', '<u4'), ('..', '<u4'), ('..', '<u4'), 
 ('..', '<u4')]
print (data[0].shape) # ()
print (type(data[0][0])) # <class 'numpy.uint32'>
...
print (type(data[0][14])) # <class 'numpy.ndarray'>
  

Комментарии:

1. Отправьте полную трассировку обратно. Сейчас похоже, что ошибка в вашей AsIs функции

2. (примечание, это список в вашем кортеже. не массив numpy)

3. @PaulH функция AsIs из psycopg2 : from psycopg2.extensions import register_adapter, AsIs

4. @PaulH Я получил массив numpy из файла HDF5, используя h5py. Я открыл dataset (ds) и вызвал tuple(ds[0:1])

5. просто чтобы уточнить, что говорил Пол: кортеж, содержащий массив numpy, будет выглядеть следующим образом: (1,0., -0.5, array([0, 0]), 1, 5) but ((1,0., -0.5, [0, 0], 1, 5)) является кортежем кортежа, содержащего список . это может быть просто обозначение, которое вы написали для представления примера. распечатайте его и, возможно, запросите type(list_or_array_element)

Ответ №1:

Определите dtype:

 In [16]: dt=np.dtype([('x','f'),('y','i',2)])
  

и структурированный массив:

 In [17]: arr = np.zeros(3,dt)
In [18]: arr
Out[18]: 
array([(0., [0, 0]), (0., [0, 0]), (0., [0, 0])],
      dtype=[('x', '<f4'), ('y', '<i4', (2,))])
  

Просмотр одной записи:

 In [19]: arr[0]
Out[19]: (0., [0, 0])
  

перенос этого в кортеж не преобразует этот внутренний массив:

 In [20]: tuple(arr[0])
Out[20]: (0.0, array([0, 0], dtype=int32))
In [21]: print(tuple(arr[0]))
(0.0, array([0, 0], dtype=int32))
In [22]: arr[0].tolist()
Out[22]: (0.0, array([0, 0], dtype=int32))
  

Сосредоточение внимания на этом одном поле:

 In [23]: arr['y']
Out[23]: 
array([[0, 0],
       [0, 0],
       [0, 0]], dtype=int32)
In [24]: arr['y'][0]
Out[24]: array([0, 0], dtype=int32)
In [25]: arr['y'].tolist()
Out[25]: [[0, 0], [0, 0], [0, 0]]
  

Индексирование массива с использованием списка имен полей за вычетом проблемного:

 In [26]: arr[['x']]
Out[26]: 
array([(0.,), (0.,), (0.,)],
      dtype={'names':['x'], 'formats':['<f4'], 'offsets':[0], 'itemsize':12})
In [27]: tuple(arr[['x']][0])
Out[27]: (0.0,)
In [29]: arr[['x']].tolist()
Out[29]: [(0.0,), (0.0,), (0.0,)]
  

Комментарии:

1. Я вызвал .tolist() метод в своем адаптере для PostgreSQL, как указано в коде в вопросе, но, похоже, psycopg2 неправильно его обработал?

2. Обратите OUT[22] внимание, что tolist он не касается этого внутреннего массива.

3. Я смотрел на out OUT[25] . Адаптер, зарегистрированный в PostgreSQL, предназначен для типа numpy.ndarray . И я помещаю оператор печати в зарегистрированный мной адаптер, и он вызывается с правильными [0, 0] данными, которые совпадают с данными, на которые указывает сообщение об ошибке PostgreSQL.