@F.Wessels думает в правильном направлении, но ответа пока нет. Скорость может быть увеличена более чем в сто раз, если вы возьмете дело в свои руки, вместо того, чтобы использовать применение вдоль оси.
Прежде всего, когда вы делите пространство трехмерного массива на блоки, ваши размеры изменяются от От 128х128х128 до 32х4х32х4х32х4. Попробуйте интуитивно понять это: у вас есть блоки размером 32x32x32 размером 4x4x4. Вместо того, чтобы сохранять блоки размером 4х4х4, вы можете просто сжать их размером 64, откуда вы сможете найти наиболее частый предмет. Вот хитрость: также не имеет значения, если ваши блоки расположены не как 32x32x32x64, а как 32768x64. По сути, мы вернулись к двумерным измерениям, где все проще.
Теперь с вашим двумерным массивом размером 32768x64 вы можете выполнять подсчет бинов самостоятельно с использованием списка и numpy ops; это будет в 10 раз быстрее.
import time
import numpy as np
array = np.random.randint(0, 4, ((128, 128, 128)), dtype='uint8')
scale_factor = (4, 4, 4)
bincount = 4
def prev_func(array):
# Reshape to free dimension of size scale_factor to apply scaledown method to
m, n, r = np.array(array.shape) // scale_factor
arr = array.reshape((m, scale_factor[0], n, scale_factor[1], r, scale_factor[2]))
arr = np.swapaxes(arr, 1, 2).swapaxes(2, 4)
arr = arr.reshape((m, n, r, np.prod(scale_factor)))
# Obtain the element that occurred the most
arr = np.apply_along_axis(lambda x: max(set(x), key=lambda y: list(x).count(y)),
axis=3, arr=arr)
return arr
def new_func(array):
# Reshape to free dimension of size scale_factor to apply scaledown method to
m, n, r = np.array(array.shape) // scale_factor
arr = array.reshape((m, scale_factor[0], n, scale_factor[1], r, scale_factor[2]))
arr = np.swapaxes(arr, 1, 2).swapaxes(2, 4)
arr = arr.reshape((m, n, r, np.prod(scale_factor)))
# Collapse dimensions
arr = arr.reshape(-1,np.prod(scale_factor))
# Get blockwise frequencies -> Get most frequent items
arr = np.array([(arr==b).sum(axis=1) for b in range(bincount)]).argmax(axis=0)
arr = arr.reshape((m,n,r))
return arr
N = 10
start1 = time.time()
for i in range(N):
out1 = prev_func(array)
end1 = time.time()
print('Prev:',(end1-start1)/N)
start2 = time.time()
for i in range(N):
out2 = new_func(array)
end2 = time.time()
print('New:',(end2-start2)/N)
print('Difference:',(out1-out2).sum())
Out:
Prev: 1.4244404077529906
New: 0.01667332649230957
Difference: 0
Как вы можете видеть, нет никаких различий в результатах, даже если я манипулировал измерениями вокруг. Функция изменения формы Numpy поддерживала порядок значений, когда я перешел в 2D, так как последнее измерение 64 было сохранено. Этот порядок восстанавливается, когда я перехожу на m, n, r. Исходный метод, который вы дали, занял около 5 секунд для запуска на моей машине, так что эмпирически это улучшение скорости в пятьсот раз.