np.vectorize и np.apply_along_axis дважды передают один и тот же аргумент в функцию отображения - PullRequest
0 голосов
/ 21 января 2019

Я хочу отобразить функцию f на массив строк. Я создаю векторизованную версию f и применяю ее к моему массиву. Но первый элемент массива передается дважды:

import numpy as np

def f(string):
    print('called with', string)

a = np.array(['110', '012'])

fv = np.vectorize(f)
np.apply_along_axis(fv, axis=0, arr=a)


called with 110
called with 110
called with 012

Почему это? Я не ожидал, что 110 будет передан f два раза, и я не понимаю, почему это так.

Каково мое неправильное представление о np.vectorize или np.apply_along_axis?

Ответы [ 2 ]

0 голосов
/ 21 января 2019
In [145]: def f(string):
     ...:     print('called with', string)
     ...: 
     ...: a = np.array(['110', '012'])
     ...: 
     ...: fv = np.vectorize(f)
     ...: 
In [146]: fv(a)
called with 110
called with 110
called with 012
Out[146]: array([None, None], dtype=object)

Функция только с печатью возвращает None.vectorized вызвал его один раз, чтобы определить возвращаемый dtype - в этом случае он вывел object.

Если мы укажем otypes как int, мы получим ошибку:

In [147]: fv = np.vectorize(f, otypes=[int])
In [148]: fv(a)
called with 110
called with 012
---------------------------------------------------------------------------
...    
TypeError: int() argument must be a string, a bytes-like object or a number, not 'NoneType'

То, что otypes не было совместимо с возвращенным объектом

In [149]: fv = np.vectorize(f, otypes=[object])
In [150]: fv(a)
called with 110
called with 012
Out[150]: array([None, None], dtype=object)

Лучшая и немного более значимая функция:

In [151]: def f(string):
     ...:     print('called with', string)
     ...:     return len(string)
     ...: 
     ...: 
In [152]: fv = np.vectorize(f, otypes=[int])
In [153]: fv(a)
called with 110
called with 012
Out[153]: array([3, 3])

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

In [154]: fv(np.array([a,a,a]))
called with 110
called with 012
called with 110
called with 012
called with 110
called with 012
Out[154]: 
array([[3, 3],
       [3, 3],
       [3, 3]])

По сравнению с простой итерацией, например.np.array([f(i) for i in a]), это медленнее, но немного удобнее, если входной массив может иметь несколько измерений, и даже лучше, если есть несколько массивов, которые необходимо транслировать друг против друга.

Для простого одного массиванапример, a, np.vectorize является избыточным.


vectorize имеет другой параметр, cache, который может избежать этого двойного вызова, но в то же время допускает автоматическое определение типа d:

In [156]: fv = np.vectorize(f, cache=True)
In [157]: fv(a)
called with 110
called with 012
Out[157]: array([3, 3])

Автоматическое определение типа d иногда вызывало ошибки.Например, если пробный расчет возвращает другой тип d:

In [160]: def foo(var):
     ...:     if var<0:
     ...:         return -var
     ...:     elif var>0:
     ...:         return var
     ...:     else:
     ...:         return 0  

In [161]: np.vectorize(foo)([0,1.2, -1.2])
Out[161]: array([0, 1, 1])           # int dtype
In [162]: np.vectorize(foo)([0.1,1.2, -1.2])
Out[162]: array([0.1, 1.2, 1.2])     # float dtype

apply_along_axis принимает функцию, которая принимает массив 1d.Он перебирает все остальные измерения, передавая набор 1d-срезов в вашу функцию.Для 1d-массива, подобного вашему a, это не поможет.И даже если ваш a был, это не очень поможет.Ваш fv не ожидает ввода 1d.

Он также выполняет пробный расчет для определения формы возвращаемого массива и типа d.Это автоматически кеширует этот результат.

Как и vectorize, apply_along_axis - это удобный инструмент, а не инструмент производительности.

Сравните

np.apply_along_axis(fv, axis=0, arr=[a,a,a])
np.apply_along_axis(fv, axis=1, arr=[a,a,a])

, чтобы понять, как apply_along влияетпорядок оценки.

Или сделать что-то с целым row (или столбцом) с помощью:

np.apply_along_axis(lambda x: fv(x).mean(), axis=0, arr=[a,a,a])
0 голосов
/ 21 января 2019

Из документов :

Тип данных для вывода векторизация определяется путем вызова функции с первым элементом ввода. Этого можно избежать, указав аргумент otypes .

Дополнительный вызов выполняется для определения выходного dtype.

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