Просто для проверки моих прошлых наблюдений, что apply_along_axis
удобно, но не быстро (er):
определяет простую 1d функцию:
In [683]: def foo(X):
...: assert(X.ndim==1)
...: return X
...:
...:
In [684]: foo(np.arange(3))
Out[684]: array([0, 1, 2])
In [685]: foo(np.ones((3,2)))
---------------------------------------------------------------------------
AssertionError Traceback (most recent call last)
Создайте многомерный массив (> 2d):
In [686]: arr = np.ones((2,3,4,5))
применить foo вдоль первого (ie массивов с размером прохода 2 60 раз):
In [687]: np.apply_along_axis(foo, 0, arr);
In [688]: timeit np.apply_along_axis(foo, 0, arr);
293 µs ± 406 ns per loop (mean ± std. dev. of 7 runs, 1000 loops each)
сделать эквивалент с изменением формы на (2,60) и перенести в (60,2). Выполните итерацию по первой оси:
In [689]: np.array([foo(x) for x in arr.reshape(2,-1).transpose()]).shape
Out[689]: (60, 2)
In [690]: np.array([foo(x) for x in arr.reshape(2,-1).transpose()]).transpose().reshape(arr.shape);
In [691]: timeit np.array([foo(x) for x in arr.reshape(2,-1).transpose()]).transpose().reshape(arr.shape);
49.4 µs ± 20.4 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
, что значительно быстрее, чем apply
.
Сделайте то же самое, но по последней оси, поэтому мне не требуется транспонирование (только 24 итерации):
In [692]: np.array([foo(x) for x in arr.reshape(-1,5)]).reshape(arr.shape);
In [693]: timeit np.array([foo(x) for x in arr.reshape(-1,5)]).reshape(arr.shape);
23.6 µs ± 23.2 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
и эквивалент применения:
In [694]: timeit np.apply_along_axis(foo, 3, arr);
156 µs ± 85.1 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
и 3-уровневая итерация (немного медленнее, чем изменение формы, но все же быстрее, чем apply
:
In [695]: np.array([foo(arr[i,j,k,:]) for i in range(2) for j in range(3) for k in range(4)]);
In [696]: timeit np.array([foo(arr[i,j,k,:]) for i in range(2) for j in range(3) for k in range(4)]);
32.5 µs ± 864 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
Использование ndindex
для генерации индексного кортежа (i,j,k)
:
In [701]: timeit np.array([foo(arr[i,j,k]) for i,j,k in np.ndindex((2,3,4))]).reshape(arr.shape);
87.3 µs ± 218 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
Это ближе к логике c, используемой в apply
, хотя по некоторым причинам все еще довольно немного быстрее. apply
, будучи более общим, должен иметь больше накладных расходов, включая тестовую оценку для определения размера возвращаемого массива.
Те же логики c могут быть применены к foo
, который требуется 2d массив.