Получить среднее значение 2D-среза 3D-массива в numpy

У меня есть массив numpy в форме:

(11L, 5L, 5L)

Я хочу вычислить среднее значение по 25 элементам каждого «среза» массива [0,:,:], [1,:,:] и т. д., возвращая 11 значений.

Это кажется глупым, но я не могу понять, как это сделать. Я думал, что это сделает функция mean(axis=x), но я перепробовал все возможные комбинации осей, и ни одна из них не дала мне желаемого результата.

Очевидно, я могу сделать это, используя цикл for и нарезку, но наверняка есть лучший способ?


person robintw    schedule 21.08.2013    source источник


Ответы (3)


Используйте кортеж для оси:

>>> a = np.arange(11*5*5).reshape(11,5,5)
>>> a.mean(axis=(1,2))
array([  12.,   37.,   62.,   87.,  112.,  137.,  162.,  187.,  212.,
        237.,  262.])

Изменить: это работает только с numpy версии 1.7+.

person J. Martinot-Lagarde    schedule 21.08.2013
comment
Оно работает? Можно было бы так подумать для версии 1.7 и последующих, но в документах по-прежнему говорится только об одной оси. - person Jaime; 21.08.2013
comment
Не думал о версии numpy, у меня 1.7.1 и она работает. Этого нет в документации, но в журнале изменений говорится об ufuncs: /Numpy-Changelog-103892.html - person J. Martinot-Lagarde; 21.08.2013
comment
Круто, не знал, что это добавили! - person lmjohns3; 21.08.2013

Вы можете reshape(11, 25), а затем вызвать mean только один раз (быстрее):

a.reshape(11, 25).mean(axis=1)

Кроме того, вы можете вызвать np.mean дважды (на моем компьютере примерно в 2 раза медленнее):

a.mean(axis=2).mean(axis=1)
person Saullo G. P. Castro    schedule 21.08.2013
comment
Я думаю, что это самый простой ответ, хотя einsum кажется быстрее. - person lmjohns3; 21.08.2013

Всегда можно использовать np.einsum:

>>> a = np.arange(11*5*5).reshape(11,5,5)
>>> np.einsum('...ijk->...i',a)/(a.shape[-1]*a.shape[-2])
array([ 12,  37,  62,  87, 112, 137, 162, 187, 212, 237, 262])

Работает с массивами более высокой размерности (все эти методы будут работать, если метки осей будут изменены):

>>> a = np.arange(10*11*5*5).reshape(10,11,5,5)
>>> (np.einsum('...ijk->...i',a)/(a.shape[-1]*a.shape[-2])).shape
(10, 11)

Быстрее загружаться:

a = np.arange(11*5*5).reshape(11,5,5)

%timeit a.reshape(11, 25).mean(axis=1)
10000 loops, best of 3: 21.4 us per loop

%timeit a.mean(axis=(1,2))
10000 loops, best of 3: 19.4 us per loop

%timeit np.einsum('...ijk->...i',a)/(a.shape[-1]*a.shape[-2])
100000 loops, best of 3: 8.26 us per loop

Масштабируется немного лучше, чем другие методы, по мере увеличения размера массива.

Использование dtype=np.float64 не изменяет заметно вышеуказанные тайминги, так что просто перепроверьте:

a = np.arange(110*50*50,dtype=np.float64).reshape(110,50,50)

%timeit a.reshape(110,2500).mean(axis=1)
1000 loops, best of 3: 307 us per loop

%timeit a.mean(axis=(1,2))
1000 loops, best of 3: 308 us per loop

%timeit np.einsum('...ijk->...i',a)/(a.shape[-1]*a.shape[-2])
10000 loops, best of 3: 145 us per loop

Также кое-что интересное:

%timeit np.sum(a) #37812362500.0
100000 loops, best of 3: 293 us per loop

%timeit np.einsum('ijk->',a) #37812362500.0
100000 loops, best of 3: 144 us per loop
person Daniel    schedule 21.08.2013
comment
Я думаю, что скорость исходит от вашего вызова np.einsum с использованием аккумулятора int вместо float или double, не уверен, что np.mean использует. Вычислять статистику рискованно, так как вы можете переполнить аккумулятор и получить очень неправильные результаты. Предоставление np.einsum dtype=np.float или dtype=np.double сделало бы вычисление более надежным и (я предполагаю здесь) более похожим по производительности на стандартные функции. Но np.einsum по-прежнему очень крутая функция, так что вы получаете свой +1... - person Jaime; 21.08.2013
comment
@ Джейми. Я тоже так думал, но мое первоначальное тестирование показало, что einsum на самом деле быстрее для любого размера и dtype. Я обновил пост с np.double таймингами. - person Daniel; 21.08.2013
comment
@Ophion... странно, что sum() не дает той же скорости, что и einsum()... очень хорошо замечено... на самом деле вторым более быстрым методом вычисления среднего было бы: timeit a.sum(axis=(1,2))/a.shape[-1]/a.shape[-2] - person Saullo G. P. Castro; 21.08.2013
comment
@Ophion Я думаю, вам следует опубликовать вопрос, например, почему np.einsum() быстрее, чем np.sum()? открыть тему для более подробного обсуждения... - person Saullo G. P. Castro; 21.08.2013
comment
@SaulloCastro Я только что писал вопрос на этот счет. Использование a.sum(axis=(1,2)... эквивалентно по времени функции a.mean(axis=(1,2)). - person Daniel; 21.08.2013
comment
@ Офион, я только что видел! Отличный вопрос! - person Saullo G. P. Castro; 21.08.2013