Использование Numpy Vectorize для функций, которые возвращают векторы - PullRequest
27 голосов
/ 31 июля 2010

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

Это прекрасно работает, когда a и b являются скалярами, но я не могу придумать причину, по которой он не будет работать с b как ndarray или списком, т.е. ] и 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 ]

34 голосов
/ 31 июля 2010

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

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

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

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

6 голосов
/ 21 октября 2017

Новый параметр 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 обобщенных универсальных функций .

3 голосов
/ 24 июля 2014
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 решает проблему более полно и красиво (я считаю). Надеюсь, это поможет.

1 голос
/ 02 августа 2016

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

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, чтобы избежать цикла в чистом Python(оба vectorize и amap являются последним регистром).

0 голосов
/ 02 августа 2016

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

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

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 транслировать массивы.Хитрость заключается в том, чтобы убедиться, что хотя бы одно измерение имеет одинаковую длину между массивами.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...