Как предотвратить трансляцию numpy при создании массива объектов из массивов различной формы

Я пытаюсь сохранить список массивов различной формы в виде массива dtype=object, используя np.save (я знаю, что могу просто замариновать список, но мне действительно любопытно, как это сделать). Если я сделаю это:

import numpy as np
np.save('test.npy', [np.zeros((2, 2)), np.zeros((3,3))])

оно работает. Но это:

np.save('test.npy', [np.zeros((2, 2)), np.zeros((2,3))])

Выдает мне ошибку:

ValueError: could not broadcast input array from shape (2,2) into shape (2)

Я предполагаю, что np.save сначала преобразует список в массив, поэтому я попробовал:

x=np.array([np.zeros((2, 2)), np.zeros((3,3))])
y=np.array([np.zeros((2, 2)), np.zeros((2,3))])

Что имеет тот же эффект (первый работает, второй нет. Полученный x ведет себя так, как ожидалось:

>>> x.shape
(2,)
>>> x.dtype
dtype('O')
>>> x[0].shape
(2, 2)
>>> x[0].dtype
dtype('float64')

Я также пытался принудительно использовать dtype объекта:

np.array([np.zeros((2, 2)), np.zeros((2,3))], dtype=object)

Безуспешно. Кажется, numpy пытается передать массив с одинаковым первым измерением в новый массив и слишком поздно понимает, что их форма отличается. Как ни странно, в какой-то момент это сработало, поэтому мне действительно любопытно, в чем разница и как это сделать правильно.


РЕДАКТИРОВАТЬ: я понял, что это работало раньше: единственная разница, похоже, заключается в том, что массивы numpy в списке имеют другой тип данных. С dtype('<f8') работает, а с dtype('float64') нет, я даже не уверен, в чем разница.


РЕДАКТИРОВАТЬ 2: я нашел очень непитоновский способ решения моей проблемы, я добавляю его сюда, возможно, это поможет понять, что я хотел сделать:

array_list=np.array([np.zeros((2, 2)), np.zeros((2,3))])
save_array = np.empty((len(array_list),), dtype=object)
for idx, arr in enumerate(array_list):
    save_array[idx] = arr
np.save('test.npy', save_array)

person rich    schedule 02.04.2017    source источник


Ответы (1)


Одна из первых вещей, которые делает np.save, это

arr = np.asanyarray(arr)

Так что да, он пытается превратить ваш список в массив.

Построение массива объектов из массивов или списков произвольного размера является сложной задачей. np.array(...) пытается создать как можно более многомерный массив, даже пытаясь объединить входные данные, если это возможно. Самый верный способ — сделать то, что сделали вы — сделать массив empty и заполнить его.

Чуть более компактный способ построения массива объектов:

In [21]: alist = [np.zeros((2, 2)), np.zeros((2,3))]
In [22]: arr = np.empty(len(alist), dtype=object)
In [23]: arr[:] = alist
In [24]: arr
Out[24]: 
array([array([[ 0.,  0.],
       [ 0.,  0.]]),
       array([[ 0.,  0.,  0.],
       [ 0.,  0.,  0.]])], dtype=object)

Вот 3 сценария:

Массивы, совпадающие по форме, объединяются в трехмерный массив:

In [27]: np.array([np.zeros((2, 2)), np.zeros((2,2))])
Out[27]: 
array([[[ 0.,  0.],
        [ 0.,  0.]],

       [[ 0.,  0.],
        [ 0.,  0.]]])
In [28]: _.shape
Out[28]: (2, 2, 2)

Массивы, не совпадающие по первому измерению — создать массив объектов

In [29]: np.array([np.zeros((2, 2)), np.zeros((3,2))])
Out[29]: 
array([array([[ 0.,  0.],
       [ 0.,  0.]]),
       array([[ 0.,  0.],
       [ 0.,  0.],
       [ 0.,  0.]])], dtype=object)
In [30]: _.shape
Out[30]: (2,)

И неудобный промежуточный случай (который можно даже назвать багом). Первые размеры совпадают, а вторые нет):

In [31]: np.array([np.zeros((2, 2)), np.zeros((2,3))])
...
ValueError: could not broadcast input array from shape (2,2) into shape (2)
       [ 0.,  0.]])], dtype=object)

Это как если бы он инициализировал массив (2,2,2), а затем обнаружил, что (2,3) не подходит. И текущая логика не позволяет делать резервную копию и создавать массив объектов, как это было в предыдущем сценарии.

Если вы хотите поместить два массива (2,2) в массив объектов, вам придется использовать логику создания и заполнения.

person hpaulj    schedule 02.04.2017
comment
Спасибо, да, я полагаю, что со всей стратегией «не тестировать и не терпеть неудачу» в питоне кажется, что это предполагаемое поведение. Я не понимал, что могу просто сделать arr[:] = alist без разрушения массива объектов, что делает это решение более приемлемым :) - person rich; 03.04.2017