6.11. Редукции¶
Редукция сворачивает массив вдоль одной или нескольких осей путём суммирования, усреднения, взятия минимума и так далее. Каждая редукция – это один вызов библиотеки для всего массива, что гораздо быстрее эквивалентного цикла Python. numpy покрывает наиболее распространённые из них:
sum()– сумма всех элементовmean()– среднее арифметическое (сумма, делённая на количество элементов)std()– стандартное отклонение,ddof=корректирует делитель (N - ddof)median()– срединное значение при сортировке элементов (50-й процентиль)argmin()/argmax()– индекс минимального или максимального элементаall()/any()– редукции истинностного значения для булевых массивов
6.11.1. Без ключевого слова axis¶
Вызванная без axis=, редукция возвращает скаляр, охватывающий весь массив:
a = np.array([1, 2, 3, 4], dtype=np.float)
np.sum(a) # 10.0
np.mean(a) # 2.5
np.std(a) # 1.118...
np.median(a) # 2.5
b = np.array([40, 10, 30, 20], dtype=np.float)
np.max(b) # 40.0
np.argmax(b) # 0 (index of the maximum)
6.11.2. С ключевым словом axis¶
axis= сжимает одну указанную ось и оставляет остальные неизменными. Результат – массив на один ранг ниже входного:
m = np.arange(12, dtype=np.float).reshape((3, 4))
np.sum(m) # 66.0 - scalar
np.sum(m, axis=0) # length-4 - column sums
np.sum(m, axis=1) # length-3 - row sums
То же правило формы применяется к каждой редукции: axis=0 сворачивает первую ось, axis=1 сворачивает вторую и так далее. Среднее / стандартное отклонение вдоль строки, например, записываются как np.mean(m, axis=1) и np.std(m, axis=1). Длина результата соответствует другой оси.
Ключевое слово keepdims=True сохраняет сжатую ось на месте с длиной 1 вместо её отбрасывания. Это различие важно, когда сокращённый результат нужно транслировать обратно к исходному массиву: keepdims сохраняет ранг, что поддерживает выравнивание правил трансляции ось к оси.
Канонический пример использования – вычитание среднего каждой строки из этой строки:
m = np.arange(12, dtype=np.float).reshape((3, 4))
row_means = np.mean(m, axis=1, keepdims=True)
# row_means has shape (3, 1)
centred = m - row_means
# (3, 4) - (3, 1) -> (3, 4), each row centred on its own mean
Без keepdims функция np.mean(m, axis=1) возвращает одномерный результат формы (3,). Трансляция (3, 4) - (3,) выстраивает (3,) как (1, 3) после добавления ранга, что несовместимо с (3, 4): последние оси не совпадают (4 против 3) и ни одна из них не равна 1, поэтому numpy вызывает ValueError. Именно keepdims=True сохраняет корректность вычитания.
6.11.3. Расположение данных имеет значение¶
В сочетании со строчно-ориентированным расположением, описанным в Форма и шаги (strides), редукция вдоль последней оси – самый дешёвый случай. Редукция проходит блок данных в том направлении, в котором он хранится, без переходов от строки к строке:
m = np.arange(2000, dtype=np.float).reshape((2, 1000))
np.sum(m, axis=1) # cheap - long axis is the inner one
np.sum(m, axis=0) # has to jump rows on every step
Когда у приложения есть выбор, как разместить буфер, поместите длинную ось последней, чтобы редукции вдоль неё выполнялись в быстром направлении.
6.11.4. Итерируемые объекты на входе¶
Большинство редукций принимают итерируемый объект Python (list, range, кортеж) вместо ndarray. Это удобство стоит нескольких микросекунд на неявное преобразование – что быстро накапливается в цикле. Когда одни и те же данные редуцируются несколько раз, создайте ndarray один раз и передавайте его.