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])