К моему небольшому удивлению, я не могу превзойти вашу версию, используя типизированные представления памяти на языке Cython - итераторы выглядят довольно быстро.Однако я думаю, что смогу значительно увеличить читабельность, чтобы позволить вам использовать синтаксис нарезки Python.Единственное ограничение заключается в том, что входной массив должен быть смежным с C, чтобы его можно было легко изменять (я думаю, что смежный Fortran также может работать, но я не проверял)
Основной прием - выровнять все осидо и после выбранной оси, так что это известная трехмерная фигура, в которой вы можете использовать просмотры памяти Cython.
@cython.boundscheck(False)
def test4(a,order=2,axis=-1):
assert a.flags['C_CONTIGUOUS'] # otherwise the reshape doesn't work
before = np.product(a.shape[:axis])
after = np.product(a.shape[(axis+1):])
cdef double[:,:,::1] a_new = a.reshape((before, a.shape[axis], after)) # this should not involve copying memory - it's just a new view
cdef double[:] a_slice
cdef double[:,:,::1] out = np.empty_like(a_new)
assert a_new.shape[1] > 3
cdef int m,n,i
for m in range(a_new.shape[0]):
for n in range(a_new.shape[2]):
a_slice = a_new[m,:,n]
out[m,0,n] = -1.5*a_slice[0] + 2*a_slice[1] - 0.5*a_slice[2]
for i in range(a_slice.shape[0]-2):
out[m,i+1,n] = -0.5*a_slice[i] + 0.5*a_slice[i+2]
# last element
out[m,-1,n] = 1.5*a_slice[-1] - 2*a_slice[-2] + 0.5*a_slice[-3]
return np.asarray(out).reshape(a.shape)
Скорость, по-моему, немного медленнее, чем у вашей версии.
С точки зрения улучшения вашего кода, вы можете обработать шаг в двойных числах вместо байтов (a_axis_stride_dbl = a_axis_stride/sizeof(double)
) и затем индексировать как pt[i*a_axis_stride_dbl]
).Вероятно, он не наберет много скорости, но будет более читабельным.(Это то, о чем вы спрашиваете в пункте 1)