Использование Numpy Vectorize для функций, которые возвращают векторы

numpy.vectorize берет функцию f:a->b и превращает ее в g:a[]->b[].

Это прекрасно работает, когда a а также b скаляры, но я не могу придумать причину, почему это не будет работать с б как ndarray или список, то есть f:a->b[] и g:a[]->b[][]

Например:

import numpy as np
def f(x):
    return x * np.array([1,1,1,1,1], dtype=np.float32)
g = np.vectorize(f, otypes=[np.ndarray])
a = np.arange(4)
print(g(a))

Это дает:

array([[ 0.  0.  0.  0.  0.],
       [ 1.  1.  1.  1.  1.],
       [ 2.  2.  2.  2.  2.],
       [ 3.  3.  3.  3.  3.]], dtype=object)

Итак, это дает правильные значения, но неправильный dtype. И еще хуже

g(a).shape

выходы:

(4,)

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

np.array(map(list, a), dtype=np.float32)

дать мне то, что я хочу:

array([[ 0.,  0.,  0.,  0.,  0.],
       [ 1.,  1.,  1.,  1.,  1.],
       [ 2.,  2.,  2.,  2.,  2.],
       [ 3.,  3.,  3.,  3.,  3.]], dtype=float32)

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

Заранее спасибо!

5 ответов

np.vectorize это просто удобная функция. Это на самом деле не делает код работать быстрее. Если это не удобно использовать np.vectorizeПросто напишите свою собственную функцию, которая работает так, как вы хотите.

Цель np.vectorize заключается в преобразовании функций, не учитывающих numpy (например, принимает числа с плавающей точкой в ​​качестве входных и возвращаемых значений с плавающей точкой в ​​качестве выходных данных) в функции, которые могут работать (и возвращать) массивы с нулевыми значениями.

Ваша функция f уже осведомлен о NumPy - он использует массив NUMPY в своем определении и возвращает массив NUMPY. Так np.vectorize не подходит для вашего случая использования.

Поэтому решение состоит в том, чтобы просто свернуть свою собственную функцию f это работает так, как вы хотите.

Новый параметр signature в 1.12.0 делает именно то, что ты что.

def f(x):
    return x * np.array([1,1,1,1,1], dtype=np.float32)

g = np.vectorize(f, signature='()->(n)')

затем g(np.arange(4)).shape дам (4L, 5L),

Здесь подпись f указан. (n) это форма возвращаемого значения, а () это форма параметра, который является скалярным. И параметры тоже могут быть массивами. Для более сложных подписей см. API обобщенных универсальных функций.

import numpy as np
def f(x):
    return x * np.array([1,1,1,1,1], dtype=np.float32)
g = np.vectorize(f, otypes=[np.ndarray])
a = np.arange(4)
b = g(a)
b = np.array(b.tolist())
print(b)#b.shape = (4,5)
c = np.ones((2,3,4))
d = g(c)
d = np.array(d.tolist())
print(d)#d.shape = (2,3,4,5)

Это должно решить проблему, и она будет работать независимо от того, какого размера ваш ввод. "Карта" работает только для одного размерного ввода. Использование ".tolist()" и создание нового ndarray решает проблему более полно и красиво (я считаю). Надеюсь это поможет.

Вы хотите векторизовать функцию

import numpy as np
def f(x):
    return x * np.array([1,1,1,1,1], dtype=np.float32)

Предполагая, что вы хотите стать холостым np.float32 массивы как результат, вы должны указать это как otype. Однако в своем вопросе вы указалиotypes=[np.ndarray] что означает, что вы хотите, чтобы каждый элемент был np.ndarray. Таким образом, вы правильно получите результатdtype=object.

Правильный звонок был бы

np.vectorize(f, signature='()->(n)', otypes=[np.float32])

Однако для такой простой функции лучше использовать numpyфункции; np.vectorizeпросто зацикливается на нем. Поэтому в вашем случае просто перепишите свою функцию как

def f(x):
    return np.multiply.outer(x, np.array([1,1,1,1,1], dtype=np.float32))

Это быстрее и приводит к менее скрытым ошибкам (учтите, однако, что результаты dtype будет зависеть от x если вы передадите комплексное число или число с четырехкратной точностью, то будет результат).

Я написал функцию, кажется, соответствует вашим потребностям.

def amap(func, *args):
    '''array version of build-in map
    amap(function, sequence[, sequence, ...]) -> array
    Examples
    --------
    >>> amap(lambda x: x**2, 1)
    array(1)
    >>> amap(lambda x: x**2, [1, 2])
    array([1, 4])
    >>> amap(lambda x,y: y**2 + x**2, 1, [1, 2])
    array([2, 5])
    >>> amap(lambda x: (x, x), 1)
    array([1, 1])
    >>> amap(lambda x,y: [x**2, y**2], [1,2], [3,4])
    array([[1, 9], [4, 16]])
    '''
    args = np.broadcast(None, *args)
    res = np.array([func(*arg[1:]) for arg in args])
    shape = args.shape + res.shape[1:]
    return res.reshape(shape)

Давай попробуем

def f(x):
        return x * np.array([1,1,1,1,1], dtype=np.float32)
amap(f, np.arange(4))

Выходы

array([[ 0.,  0.,  0.,  0.,  0.],
       [ 1.,  1.,  1.,  1.,  1.],
       [ 2.,  2.,  2.,  2.,  2.],
       [ 3.,  3.,  3.,  3.,  3.]], dtype=float32)

Вы также можете обернуть его лямбда или частично для удобства

g = lambda x:amap(f, x)
g(np.arange(4))

Обратите внимание на строку документации vectorize говорит

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

Таким образом, мы ожидаем amap здесь имеют такую ​​же производительность, как vectorize, Я не проверял, приветствуются любые тесты производительности.

Если производительность действительно важна, вы должны рассмотреть что-то еще, например, прямой расчет массива с reshape а также broadcast чтобы избежать петли в чистом питоне (оба vectorize а также amap более поздний случай).

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

Вот как это может выглядеть в коде:

import numpy as np
def f(x):
    return x*np.array([1, 1, 1, 1, 1], dtype=np.float32)

a = np.arange(4).reshape((4, 1))
b = f(a)
# b is a 2-D array with shape (4, 5)
print(b)

Это гораздо более простой и менее подверженный ошибкам способ завершения операции. Вместо того, чтобы пытаться преобразовать функцию с помощью numpy.vectorize, этот метод опирается на естественную способность NumPy транслировать массивы. Хитрость заключается в том, чтобы убедиться, что хотя бы одно измерение имеет одинаковую длину между массивами.

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