6.9. Універсальні функції¶
Універсальна функція (ufunc) — це математична функція, що застосовується до кожного елемента масиву за один виклик. Арифметичні оператори з попередньої сторінки є універсальними функціями в синтаксисі операторів; ця сторінка містить каталог іменованих функцій, що охоплюють тригонометрію, exp/log, округлення та деякі інші.
Кожна функція ufunc приймає скаляр, ітерований об’єкт Python або ndarray і повертає або одне число з плаваючою крапкою (якщо вхідні дані — скаляр), або ndarray з плаваючою крапкою:
from ulab import numpy as np
np.exp(2.0) # 7.389...
np.sin(range(4)) # 1-D float ndarray
np.sqrt([1, 4, 9, 16]) # array([1.0, 2.0, 3.0, 4.0])
a = np.arange(9).reshape((3, 3))
np.exp(a) # 3x3 float ndarray
6.9.1. Каталог¶
numpy надає математичні функції, що найчастіше використовуються у вбудованих застосунках:
Тригонометрія –
sin(),cos(),tan(),asin(),acos(),atan(),arctan2(),sinh(),cosh(),tanh(),asinh(),acosh(),atanh(),sinc().Показникові функції та логарифми –
exp(),expm1(),log(),log10(),log2(),sqrt().
Кожна функція обробляє весь масив за один виклик бібліотеки. Приріст швидкості порівняно зі списковим включенням Python, що викликає math.sin() поелементно, становить 10–30 разів для типового буфера.
6.9.2. Ключове слово out=¶
Кожен виклик ufunc зазвичай виділяє новий масив результатів для зберігання виводу. У циклі, що виконується багато разів на секунду, ці виділення накопичуються та витрачають оперативну пам’ять. Передача out= – масиву з плаваючою крапкою, що вже існує, тієї самої форми, що й вхідний – записує результат у цей масив замість виділення нового:
x = np.linspace(0, 2 * np.pi, num=256)
y = np.zeros(256)
while True:
np.sin(x, out=y)
# use y ...
Якщо out має неправильний тип або форму, функція генерує виняток. Ключове слово підтримується у кожній ufunc на цій сторінці; це найчистіший спосіб підтримувати цикл обробки потокового сигналу без виділення пам’яті.
6.9.3. Двоаргументні ufunc¶
arctan2() є єдиною справжньою двоаргументною ufunc у переліку вище – вона повертає арктангенс y / x з урахуванням квадранта і транслює два операнди:
y = np.array([1, 2.2, 33.33, 444.444])
np.arctan2(y, 1.0) # against a scalar
np.arctan2(1.0, y) # the other way
np.arctan2(y, y) # against another array
6.9.4. Складання універсальних функцій¶
Універсальні функції комбінуються як будь-який інший вираз з масивами. Кілька шаблонів, що трапляються на камері:
Корекція гами (у просторі float)
gamma = 0.5
out = 255.0 * (frame / 255.0) ** gamma
Простий низькочастотний згладжувач (alpha близьке до 1.0 означає повільне оновлення):
alpha = 0.95
filtered = alpha * filtered + (1.0 - alpha) * sample
Сигмоїда
sigmoid = 1.0 / (1.0 + np.exp(-x))
Спектр потужності в дБ
spectrum = 20.0 * np.log10(np.abs(real) + 1e-12)
6.9.5. np.vectorize¶
Звичайну функцію Python можна перетворити на ufunc-подібну за допомогою vectorize(). Отриманий об’єкт для виклику приймає скаляри, ітеровані об’єкти або значення ndarray
def f(x):
return x * x
vf = np.vectorize(f)
vf(44.0) # array([1936.0])
vf(np.array([1, 2, 3, 4])) # array([1.0, 4.0, 9.0, 16.0])
vf([2, 3, 4]) # array([4.0, 9.0, 16.0])
За замовчуванням результуючий тип даних — float. otypes= замінює його:
vf_u8 = np.vectorize(f, otypes=np.uint8)
vf_u8([1, 2, 3, 4])
# array([1, 4, 9, 16], dtype=uint8)
Функція Python повинна приймати один аргумент і повертати одне число.
vectorize() здебільшого є синтаксичним засобом – загорнута функція Python все одно повинна виконуватися один раз для кожного елемента, тому більша частина витрат інтерпретатора на елемент, яких уникає справжня ufunc, повертається. Очікуйте скромного прискорення на 30–50% порівняно зі списковим включенням, а не 30-кратного, як у справжньої універсальної функції. Правильний інструмент, коли одна функція повинна працювати зі скалярами, списками і масивами під одним іменем, – але не коли метою є максимальна швидкість.
Повні сигнатури викликів усіх перелічених вище універсальних функцій див. у numpy — операції з масивами, сумісні з numpy.