6.9. Funkcje uniwersalne

Funkcja uniwersalna (ufunc) to funkcja matematyczna, która jednym wywołaniem działa na każdym elemencie tablicy. Operatory arytmetyczne z poprzedniej strony to funkcje uniwersalne ubrane w składnię operatorów; ta strona jest katalogiem tych nazwanych, obejmujących trygonometrię, exp / log, zaokrąglanie i kilka innych.

Każda ufunc przyjmuje skalar, iterowalny obiekt Pythona lub ndarray i zwraca albo pojedynczą liczbę zmiennoprzecinkową (gdy wejście było skalarem), albo zmiennoprzecinkową 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. Katalog

numpy udostępnia funkcje matematyczne, po które aplikacja wbudowana sięga najczęściej:

Każda funkcja przetwarza całą tablicę w jednym wywołaniu biblioteki. Przyspieszenie względem listy składanej w Pythonie wywołującej math.sin() element po elemencie wynosi 10-30x na typowym buforze.

6.9.2. Słowo kluczowe out=

Każde wywołanie ufunc normalnie alokuje świeżą tablicę wynikową, aby pomieścić jej wyjście. W pętli wykonywanej wiele razy na sekundę te alokacje narastają i marnują pamięć RAM. Przekazanie out= – już istniejącej tablicy zmiennoprzecinkowej o tym samym kształcie co dane wejściowe – zapisuje wynik do tej tablicy zamiast alokować nową:

x = np.linspace(0, 2 * np.pi, num=256)
y = np.zeros(256)

while True:
    np.sin(x, out=y)
    # use y ...

Jeśli out ma niewłaściwy dtype lub kształt, funkcja zgłasza wyjątek. To słowo kluczowe jest obsługiwane przez każdą ufunc na tej stronie; jest to najczystszy sposób, by strumieniowa pętla przetwarzania sygnału pozostała wolna od alokacji.

6.9.3. Funkcje uniwersalne dwuargumentowe

arctan2() to jedyna prawdziwa dwuargumentowa ufunc na powyższej liście – zwraca arcus tangens y / x ze świadomością ćwiartki i rozpropagowuje oba operandy:

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. Składanie funkcji uniwersalnych

Funkcje uniwersalne składają się jak każde inne wyrażenie tablicowe. Kilka wzorców, które pojawiają się na kamerze:

Korekcja gamma (w przestrzeni zmiennoprzecinkowej)

gamma = 0.5
out = 255.0 * (frame / 255.0) ** gamma

Prosty wygładzacz dolnoprzepustowy (alpha bliskie 1.0 oznacza wolną aktualizację):

alpha = 0.95
filtered = alpha * filtered + (1.0 - alpha) * sample

Sigmoida

sigmoid = 1.0 / (1.0 + np.exp(-x))

Widmo mocy w dB

spectrum = 20.0 * np.log10(np.abs(real) + 1e-12)

6.9.5. np.vectorize

Zwykła funkcja Pythona może zostać przekształcona w funkcję o kształcie ufunc za pomocą vectorize(). Powstały obiekt wywoływalny przyjmuje skalary, obiekty iterowalne lub wartości 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])

Domyślnie typem wynikowym jest float. otypes= go nadpisuje:

vf_u8 = np.vectorize(f, otypes=np.uint8)
vf_u8([1, 2, 3, 4])
# array([1, 4, 9, 16], dtype=uint8)

Funkcja Pythona musi przyjmować pojedynczy argument i zwracać pojedynczą liczbę.

vectorize() jest głównie składniowa – opakowana funkcja Pythona wciąż musi być uruchamiana raz na element, więc większość kosztu interpretera na element, którego prawdziwa ufunc unika, powraca. Spodziewaj się umiarkowanego przyspieszenia o 30%-50% względem listy składanej, a nie 30x prawdziwej funkcji uniwersalnej. Właściwe narzędzie, gdy jedna funkcja musi działać na skalarach, listach i tablicach pod tą samą nazwą – a nie wtedy, gdy celem jest surowa szybkość.

Pełne sygnatury wywołań każdej z wymienionych powyżej funkcji uniwersalnych znajdziesz w numpy — operacje na tablicach zgodne z numpy.