Я часто заканчиваю тем, что применяю подпрограммы, которые работают с одномерными массивами, а затем обобщаю их так, что, если они вызываются в матрицах, они обрабатывают каждую строку независимо. Например, предположим, что нам нужна функция, которая вычитает из нее среднее значение вектора, а затем делает это в каждой строке матрицы, если ее вход имеет более одного измерения. Это может быть реализовано следующим образом:
import numpy as np
def _sub_mean(a):
""" Subtract the mean from vector elements """
assert isinstance(a, np.ndarray) and a.ndim == 1
return a - np.mean(a)
def sub_mean(a):
""" Subtract the mean
If `a` is a 1D array it returns a 1D array (obtained by subtracting the
mean of `a` from each of its elements). If `a` has more than one dimension
each row is treated separately
"""
a = np.asanyarray(a)
if a.ndim <= 1:
a = np.atleast_1d(a)
return np.squeeze(_sub_mean(a))
retval = np.empty(a.shape)
for idx in np.ndindex(*a.shape[:-1]):
retval[idx] = _sub_mean(a[idx])
return retval
Вот несколько примеров вывода sub_mean
:
>>> sub_mean(5)
array(0.)
>>> sub_mean([1, 2])
array([-0.5, 0.5])
>>> sub_mean([[1, 2], [4, 6]])
array([[-0.5, 0.5],
[-1. , 1. ]])
>>>
Обратите внимание, что вычисление «ядра» происходит в приватная функция _sub_mean
. Действительно, тот же код в sub_mean
можно использовать для обобщения любой функции, работающей с одномерными массивами, до функции, работающей с произвольным числом измерений, заменив _sub_mean
. Также можно подумать о дальнейшем обобщении, например, добавив аргумент axis
, который определяет ось, на которой работает функция, и / или возможность работы со сглаженным входным массивом.
Мне было интересно, уже NumPy предоставляет декоратор для обобщения функций, которые работают с векторами, с функциями, которые работают с более чем одномерными массивами? Т.е., если я смогу заменить приведенный выше код на:
import numpy as np
@np.some_decorator
def sub_mean(a):
""" Subtract the mean
If `a` is a 1D array it returns a 1D array (obtained by subtracting the
mean of `a` from each of its elements). If `a` has more than one dimension
each row is treated separately """
assert isinstance(a, np.ndarray) and a.ndim == 1
return a - np.mean(a)
(и, очевидно, получу те же выходные данные.)
ОБНОВЛЕНИЕ: я закончил тем, что написал следующий декоратор:
class _Expand:
def __init__(self, func1d):
functools.update_wrapper(self, func1d)
self._func1d = func1d
def __call__(self, arr, *args, **kwargs):
arr = np.asanyarray(arr)
axis = kwargs.pop('axis', -1)
return np.apply_along_axis(self._func1d, axis, arr, *args, **kwargs)
, что позволило бы мне написать sub_mean
(или любую другую сложную функцию, которая работает с массивами 1D) как:
@_Expand
def sub_mean(a):
""" Subtract the mean
If `a` is a 1D array it returns a 1D array (obtained by subtracting the
mean of `a` from each of its elements). If `a` has more than one dimension
each row is treated separately """
assert isinstance(a, np.ndarray) and a.ndim == 1
return a - np.mean(a)
(обратите внимание, что _Expand
позволяет выберите ось, вдоль которой выполняется операция - это немного более обобщенно c, чем мне нужно.)
Тем не менее, мне все равно было бы интересно узнать, реализован ли такой декоратор в NumPy * * 1030