Векторизовать Numpy, используя списки в качестве аргументов

Функция numpy vectorize полезна, но она плохо себя ведет, когда аргументами функции являются списки, а не скаляры. В качестве примера:

import numpy as np

def f(x, A):
    print "type(A)=%s, A=%s"%(type(A),A)
    return sum(A)/x

X = np.linspace(1,2,10)
P = [1,2,3]

f2 = np.vectorize(f)

f(X,P)
f2(X,P)

Дает:

type(A)=<type 'list'>, A=[1, 2, 3]
type(A)=<type 'numpy.int64'>, A=1

Traceback (most recent call last):
  File "vectorize.py", line 14, in <module>
    f2(X,P)
  File "/usr/local/lib/python2.6/dist-packages/numpy/lib/function_base.py", line 1824, in __call__
    theout = self.thefunc(*newargs)
  File "vectorize.py", line 5, in f
    return sum(A)/x
TypeError: 'numpy.int64' object is not iterable

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


person Hooked    schedule 21.12.2010    source источник


Ответы (2)


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

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

Как и в случае с оболочкой np.ndarray np.array, приведение аргументов к массивам пытается быть полезным, автоматически преобразовывая список в массив, содержащий те же элементы, а не создавая массив с dtype=object, который содержит список как его единственный элемент. Большую часть времени это то, что мы хотим, но в вашем случае это «умное» поведение возвращается, чтобы укусить вас.

Хотя может быть способ указать numpy обрабатывать только определенные входные данные как векторы, есть два простых способа получить поведение, которое вам нужно:

  1. Вручную создайте массив с dtype=object для работы в рамках правил вещания.
  2. Curry значение перед векторизацией функции

<сильный>1. dtype=объект

Массивы Numpy получают свою эффективность за счет хранения только одного типа элементов, но они все же могут содержать произвольные объекты python, указав, что сохраненный тип данных является объектами python:

list_obj_array = np.ndarray((1,), dtype=object)
list_obj_array[0] = [1,2,3]
f2(X,list_obj_array)  # using your definition from above

печатает:

type(A)=<type 'list'>, A=[1, 2, 3]
type(A)=<type 'list'>, A=[1, 2, 3]
type(A)=<type 'list'>, A=[1, 2, 3]
type(A)=<type 'list'>, A=[1, 2, 3]
type(A)=<type 'list'>, A=[1, 2, 3]
type(A)=<type 'list'>, A=[1, 2, 3]
type(A)=<type 'list'>, A=[1, 2, 3]
type(A)=<type 'list'>, A=[1, 2, 3]
type(A)=<type 'list'>, A=[1, 2, 3]
type(A)=<type 'list'>, A=[1, 2, 3]
type(A)=<type 'list'>, A=[1, 2, 3]

и возвращает:

array([ 6.        ,  5.4       ,  4.90909091,  4.5       ,  4.15384615,
        3.85714286,  3.6       ,  3.375     ,  3.17647059,  3.        ])

<сильный>2. Карри

Поскольку вы передаете один и тот же список вызову функции для каждого элемента в массиве, вы можете сохранить список непосредственно с помощью функции путем каррирования перед применением векторизации:

def curry_f(A):
    def f_curried(x):
        return f(x, A)  # using your definition from above
    return f_curried

f2 = np.vectorize(curry_f(P))
f2(X)

печатает:

type(A)=<type 'list'>, A=[1, 2, 3]
type(A)=<type 'list'>, A=[1, 2, 3]
type(A)=<type 'list'>, A=[1, 2, 3]
type(A)=<type 'list'>, A=[1, 2, 3]
type(A)=<type 'list'>, A=[1, 2, 3]
type(A)=<type 'list'>, A=[1, 2, 3]
type(A)=<type 'list'>, A=[1, 2, 3]
type(A)=<type 'list'>, A=[1, 2, 3]
type(A)=<type 'list'>, A=[1, 2, 3]
type(A)=<type 'list'>, A=[1, 2, 3]

и возвращает:

array([ 6.        ,  5.4       ,  4.90909091,  4.5       ,  4.15384615,
        3.85714286,  3.6       ,  3.375     ,  3.17647059,  3.        ])

P.S. вы также можете взглянуть на np.frompyfunc — он похож на vectorize(), но работает на несколько более низком уровне.

person Gabriel Grant    schedule 21.12.2010

Это пример рекурсивного декоратора, который я сейчас использую для векторизации функции, которая принимает одномерный массив в качестве первого аргумента:

def broadcast(fvec):
    def inner(vec, *args, **kwargs):
        if len(vec.shape) > 1:
            return np.array([inner(row, *args, **kwargs) for row in vec])
        else:
            return fvec(vec, *args, **kwargs)
    return inner

Я предполагаю, что он делает почти то же самое, что и np.vectorize, но мне оказалось проще использовать его, чем пытаться адаптировать vectorize/frompyfunc для работы.

person Lev Levitsky    schedule 26.11.2012