Эффективный способ применения функции для каждого двумерного среза 3D-массива numpy

Я хочу применить функцию, которая принимает 2D-массив (и возвращает одну и ту же форму) для каждого 2D-фрагмента 3D-массива. Какой эффективный способ сделать это? numpy.fromiter возвращает 1D-массив, а numpy.fromfunction необходимо применять к каждой координате индивидуально.

В настоящее время я делаю

foo = np.array([func(arg, bar2D) for bar2D in bar3D])

Это дает мне то, что я хочу, но понимание списка происходит очень медленно. Кроме того, func является одномерной производной с конкретными граничными условиями. numpy.gradient только, кажется, выполняет производные N-D с N размерностью массива, но, возможно, есть еще одна процедура, которая сделает все для меня?

Изменить: понимание списка работает, но я ищу более быстрый способ сделать это. bar3D может быть большим, до (500,500,1000). Все подпрограммы numpy, которые я нашел для применения функций к массивам, по-видимому, предполагают, что функция или массив 1D.

Ответ 1

Я не знаю какого-либо общего способа применения функций к N-D срезам массивов. Но есть два способа обойти это.

Если то, что вы хотите сделать, это применить производную 1D в каждой строке или столбце каждого 2D-фрагмента, это эквивалентно применению производной для каждого 1D-фрагмента, и вы можете использовать np.apply_along_axis:

values = np.arange(4)*np.arange(3)[:, None]+np.arange(2)[:, None, None]*2
>>> array([[[0, 0, 0, 0],
            [0, 1, 2, 3],
            [0, 2, 4, 6]],

       [[2, 2, 2, 2],
        [2, 3, 4, 5],
        [2, 4, 6, 8]]])

np.apply_along_axis(np.gradient, 2, values)
>>> array([[[ 0.,  0.,  0.,  0.],
            [ 1.,  1.,  1.,  1.],
            [ 2.,  2.,  2.,  2.]],

           [[ 0.,  0.,  0.,  0.],
            [ 1.,  1.,  1.,  1.],
            [ 2.,  2.,  2.,  2.]]])

Это отличает строки каждого двумерного среза. Чтобы различать каждый столбец, np.apply_along_axis(np.gradient, 2, values)

Если вы хотите сделать что-то, требующее двух измерений, вы можете получить его через параметры вещания и оси. Если, например, вы хотите V[i, j] = sqrt((V[i,j]-V[i, j-1])^2+V[i, j]-V[i-1, j])^2 для каждого фрагмента V, вы можете сделать:

xdiffs = np.zeros_like(values) 
xdiffs[:, 1:, :]= np.diff(values, axis=1) 

ydiffs = np.zeros_like(values)
ydiffs[:, :, 1:] = np.diff(values, axis=2)

diffnorms = np.linalg.norm(xdiffs, ydiffs)

>>> array(
  [[[ 0.        ,  0.        ,  0.        ,  0.        ],
    [ 0.        ,  1.41421356,  2.23606798,  3.16227766],
    [ 0.        ,  2.23606798,  2.82842712,  3.60555128]],

   [[ 0.        ,  0.        ,  0.        ,  0.        ],
    [ 0.        ,  1.41421356,  2.23606798,  3.16227766],
    [ 0.        ,  2.23606798,  2.82842712,  3.60555128]]])

Немного громоздко получить правильные размеры, но обычно это будет наиболее эффективное решение.

В этих примерах используются нули на границах, если вам нужно что-то еще, вам нужно установить normdiff[:, :, 0] и normdiff[:, 0, :] на правильные значения границ.

Ответ 2

Скажем, у вас есть массив, a:

>>> a=np.random.random((4,3,2))

array([[[ 0.27252091,  0.78545835],
        [ 0.83604934,  0.48509821],
        [ 0.77828735,  0.26630055]],

       [[ 0.98623474,  0.29839813],
        [ 0.15893604,  0.61870988],
        [ 0.62281607,  0.27193647]],

       [[ 0.47976331,  0.2471835 ],
        [ 0.77323041,  0.30137068],
        [ 0.52906156,  0.53950597]],

       [[ 0.59207654,  0.86355457],
        [ 0.50250812,  0.75688653],
        [ 0.91046136,  0.5785383 ]]])

Вы можете получить доступ к двумерным срезам следующим образом:

>>> for x in range(a.shape[0]):
        print a[x,:,:]

>>> for x in range(a.shape[1]):
        print a[:,x,:]

>>> for x in range(a.shape[2]):
        print a[:,:,x]