Использование `numpy.vectorize` для создания многомерного массива приводит к ValueError: установка элемента массива с последовательностью

Эта проблема возникает только тогда, когда мой dummy Функция возвращает массив и, таким образом, создается многомерный массив.

Я сократил проблему до следующего примера:

def dummy(x):
    y = np.array([np.sin(x), np.cos(x)])
    return y

x = np.array([0, np.pi/2, np.pi])

Код, который я хочу оптимизировать, выглядит следующим образом:

y = []
for x_i in x:
    y_i = dummy(x_i)
    y.append(y_i)
y = np.array(y)

Поэтому я подумал, я мог бы использовать vectorize избавиться от медленной петли:

y = np.vectorize(dummy)(x)

Но это приводит к

ValueError: setting an array element with a sequence.

Где даже последовательность, о которой говорит ошибка?!

2 ответа

Решение

Ваша функция возвращает массив при наличии скаляра:

In [233]: def dummy(x):
     ...:     y = np.array([np.sin(x), np.cos(x)])
     ...:     return y
     ...: 
     ...: 
In [234]: dummy(1)
Out[234]: array([0.84147098, 0.54030231])



In [235]: f = np.vectorize(dummy)
In [236]: f([0,1,2])
...
ValueError: setting an array element with a sequence.

vectorize создает пустой массив результатов и пытается поместить в него результат каждого вычисления. Но ячейка целевого массива не может принять массив.

Если мы укажем otypes параметр, он работает:

In [237]: f = np.vectorize(dummy, otypes=[object])
In [238]: f([0,1,2])
Out[238]: 
array([array([0., 1.]), array([0.84147098, 0.54030231]),
       array([ 0.90929743, -0.41614684])], dtype=object)

То есть каждый dummy массив помещается в элемент массива результатов shape (3,).

Поскольку массивы компонентов имеют одинаковую форму, мы можем stack их:

In [239]: np.stack(_)
Out[239]: 
array([[ 0.        ,  1.        ],
       [ 0.84147098,  0.54030231],
       [ 0.90929743, -0.41614684]])

Но, как уже отмечалось, vectorize не обещает ускорение. Я подозреваю, что мы могли бы также использовать более новый signature параметр, но это еще медленнее.

vectorize Имеет смысл, если ваша функция принимает несколько скалярных аргументов, и вы хотели бы использовать бесполезную трансляцию при передаче наборов значений. Но в качестве замены простой итерации по 1d-массиву это не улучшение.

Я не очень понимаю ошибку, но с Python 3.6.3 вы можете просто написать:

y = dummy(x)

так что это автоматически векторизация.

Также в официальной документации написано следующее:

Функция векторизации предоставляется в первую очередь для удобства, а не для производительности. Реализация по сути является циклом for.

Я надеюсь, что это было хоть немного помощи.

Другие вопросы по тегам