Весь курс мы заменяли циклы операциями над массивами: prices * 0.9 вместо цикла по ценам, маска вместо
if в цикле. Это называют векторизацией (vectorization): операция записывается для всего массива, а цикл
по элементам выполняет NumPy — внутри, в десятки и сотни раз быстрее цикла на Python. В этом уроке — последние недостающие
кусочки: математические функции и приёмы, которыми переписывают любой цикл.
Математика поэлементно
Функции модуля math из обычного Python работают с одним числом. У NumPy есть такие же, но для массивов —
применяются к каждому элементу:
np.sqrt— квадратный корень;np.abs— модуль числа (без знака);np.log10— десятичный логарифм: в какую степень нужно возвести 10, чтобы получить число (log10(1000) = 3);np.log— натуральный логарифм,np.exp— обратная ему экспонента.
import numpy as np
print(np.sqrt(np.array([4, 9, 2])))
print(np.abs(np.array([-3, 0, 2.5, -7])))[2. 3. 1.41421356] [3. 0. 2.5 7. ]
Курьер проехал от склада dx километров на восток и dy на север (пять заказов). Сохраните в dist
расстояние от склада до каждой точки по прямой — по теореме Пифагора: корень из dx² + dy².
dx ** 2.np.sqrt(dx ** 2 + dy ** 2).Заготовка упражнения обновилась с тех пор, как вы сохранили свой код.
Вернуть заготовку? Ваш код в этом упражнении будет удалён.
dx = np.array([3.0, 5.0, 1.5, 8.0, 6.0])
dy = np.array([4.0, 12.0, 2.0, 6.0, 2.5])
dist = ...Упражнение пока не решено, поэтому для ячеек ниже выполнено эталонное решение — ваш код не изменён.
dx = np.array([3.0, 5.0, 1.5, 8.0, 6.0])
dy = np.array([4.0, 12.0, 2.0, 6.0, 2.5])
dist = np.sqrt(dx ** 2 + dy ** 2)В actual — сколько кофе продали за неделю по дням, в forecast — сколько предсказывал прогноз. Сохраните в
mae среднюю ошибку прогноза без учёта знака: насколько в среднем прогноз промахивался — хоть вверх, хоть вниз.
forecast - actual; без знака — np.abs.mean от модулей промахов.Заготовка упражнения обновилась с тех пор, как вы сохранили свой код.
Вернуть заготовку? Ваш код в этом упражнении будет удалён.
actual = np.array([120, 135, 150, 128, 142, 160, 155])
forecast = np.array([118, 140, 145, 130, 150, 152, 158])
mae = ...Упражнение пока не решено, поэтому для ячеек ниже выполнено эталонное решение — ваш код не изменён.
actual = np.array([120, 135, 150, 128, 142, 160, 155])
forecast = np.array([118, 140, 145, 130, 150, 152, 158])
mae = np.abs(forecast - actual).mean()Логарифм и экспонента
Логарифм сжимает огромные разбросы: у числа из k цифр десятичный логарифм — между k - 1 и k, так что его
целая часть плюс один — число цифр. Он удобен,
когда рядом стоят деревня и мегаполис:
population = np.array([1200, 45000, 610000, 1100000, 12600000])
print(np.log10(population))
print(np.log10(population).astype(int) + 1) # число цифр
print(np.log(np.array([1, 10, 100]))) # натуральный логарифм[3.07918125 4.65321251 5.78532984 6.04139269 7.10037055] [4 5 6 7 8] [0. 2.30258509 4.60517019]
np.exp в анализе данных часто встречается внутри формул. Например, сигмоида 1 / (1 + np.exp(-x))
превращает любое число в долю от 0 до 1 — так модели машинного обучения выдают вероятность:
x = np.array([-4, -1, 0, 1, 4])
print(np.round(1 / (1 + np.exp(-x)), 3))[0.018 0.269 0.5 0.731 0.982]
Поэлементный максимум: np.maximum
np.maximum(a, b) сравнивает два массива поэлементно и берёт большее в каждой позиции. Вместо массива может
быть число — тогда оно работает как нижняя граница. np.minimum — то же с меньшим:
a = np.array([3, -2, 7, -5])
b = np.array([1, 4, 6, 0])
print(np.maximum(a, b))
print(np.maximum(a, 0)) # отрицательные → 0
print(np.minimum(a, 5)) # больше 5 → 5[3 4 7 0] [3 0 7 0] [ 3 -2 5 -5]
Что выведет print(np.max(np.array([-2, 5, -1]), 0))?
np.max — ось: np.max(a, 0) — это a.max(axis=0), самый большой элемент. Замену отрицательных нулём делает np.maximum(a, 0).В hours — сколько часов в неделю отработал каждый сотрудник. Всё, что больше 40, — переработка. Сохраните
в overtime часы переработки каждого (у кого нет — 0), а в total_overtime — сумму по всем.
hours - 40 даёт отрицательные числа тем, кто работал меньше.np.maximum(hours - 40, 0).Заготовка упражнения обновилась с тех пор, как вы сохранили свой код.
Вернуть заготовку? Ваш код в этом упражнении будет удалён.
hours = np.array([38, 45, 40, 52, 36, 41])
overtime = ...
total_overtime = ...Упражнение пока не решено, поэтому для ячеек ниже выполнено эталонное решение — ваш код не изменён.
hours = np.array([38, 45, 40, 52, 36, 41])
overtime = np.maximum(hours - 40, 0)
total_overtime = overtime.sum()Переписать цикл
Любой цикл «для каждого элемента посчитать» переписывается по одной схеме: вычисление — арифметикой над
массивом, if — маской, np.where, np.maximum или np.minimum. Вот цикл, который начисляет скидку 10 % на
дорогие товары, и та же логика одной строкой:
prices = np.array([80, 150, 95, 240])
result = []
for p in prices:
if p > 100:
result.append(p * 0.9)
else:
result.append(p)
print(np.array(result))
print(np.where(prices > 100, prices * 0.9, prices))[ 80. 135. 95. 216.] [ 80. 135. 95. 216.]
Теперь ваша очередь. Этот цикл начисляет бонусные баллы: за каждый балл сверх 50 — два бонусных, но не больше 60:
scores = np.array([35, 50, 62, 71, 88, 95, 49, 100])
result = []
for s in scores:
if s >= 50:
b = (s - 50) * 2
else:
b = 0
if b > 60:
b = 60
result.append(b)
print(np.array(result))[ 0 0 24 42 60 60 0 60]
Перепишите цикл из ячейки выше без цикла: сохраните в bonus массив бонусов для scores — те же числа, что
считает цикл.
scores - 50 меньше нуля у тех, кто набрал меньше 50: их бонус — ноль, np.maximum(…, 0).np.minimum(…, 60). (Обе границы сразу ставит и np.clip из модуля 4.)Заготовка упражнения обновилась с тех пор, как вы сохранили свой код.
Вернуть заготовку? Ваш код в этом упражнении будет удалён.
bonus = ...Упражнение пока не решено, поэтому для ячеек ниже выполнено эталонное решение — ваш код не изменён.
bonus = np.minimum(np.maximum(scores - 50, 0) * 2, 60)Итоги
- Векторизация — операция для всего массива сразу, цикл по элементам выполняет NumPy.
np.sqrt,np.abs,np.log10,np.log,np.expприменяются к каждому элементу.np.maximum(a, b)иnp.minimum(a, b)— поэлементно; с числом — граница снизу или сверху.np.max(a, 0)— не поэлементный максимум, аaxis=0.- Цикл с
ifпереписывается маской,np.where,np.maximum,np.minimum,np.clip.