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. Важливість розташування в пам’яті¶
У поєднанні з порядком рядків, розглянутим у Форма та кроки, редукція вздовж останньої осі є найдешевшою. Вона обходить блок даних у напрямку зберігання без стрибків між рядками:
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 один раз і передавайте його далі.