Numpy векторизация, используя списки в качестве аргументов - PullRequest
9 голосов
/ 21 декабря 2010

Функция 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 ее использования, но я быхотелось бы знать, как (в общем случае) векторизовать функцию, аргументы которой принимают в виде списков, а не скаляров.

Ответы [ 2 ]

11 голосов
/ 21 декабря 2010

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

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

Как и в случае np.array с обёрткой np.ndarray, приведение аргументов к массивам пытается быть полезным, автоматически преобразовывая список в массив, содержащий те же элементы, а не создавая массив с 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 (), но работает на несколько более низком уровне.

0 голосов
/ 26 ноября 2012

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

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 для работы.

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