6.9. Funcții universale¶
O funcție universală (ufunc) este o funcție matematică ce se aplică fiecărui element al unui tablou într-un singur apel. Operatorii aritmetici de pe pagina anterioară sunt funcții universale care poartă sintaxă de operator; această pagină este catalogul celor cu nume care acoperă trigonometria, exp / log, rotunjirea și câteva altele.
Fiecare ufunc acceptă un scalar, un iterabil Python sau un ndarray și returnează fie un singur float (când intrarea a fost scalară), fie un ndarray de tip float:
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. Catalogul¶
numpy expune funcțiile matematice la care o aplicație embedded apelează cel mai des:
Trigonometrie –
sin(),cos(),tan(),asin(),acos(),atan(),arctan2(),sinh(),cosh(),tanh(),asinh(),acosh(),atanh(),sinc().Exponențiale și logaritmi –
exp(),expm1(),log(),log10(),log2(),sqrt().
Fiecare funcție procesează întregul tablou într-un singur apel de bibliotecă. Accelerarea față de o list comprehension Python care apelează math.sin() element cu element este de 10-30x pe un tampon (buffer) tipic.
6.9.2. Cuvântul-cheie out=¶
Fiecare apel de ufunc alocă în mod normal un tablou de rezultat nou pentru a-și păstra ieșirea. Într-o buclă care rulează de multe ori pe secundă, aceste alocări se adună și irosesc RAM. Transmiterea out= – un tablou float care există deja, de aceeași formă ca intrarea – scrie rezultatul în acel tablou în loc să aloce unul nou:
x = np.linspace(0, 2 * np.pi, num=256)
y = np.zeros(256)
while True:
np.sin(x, out=y)
# use y ...
Dacă out are dtype-ul sau forma greșită, funcția ridică o excepție. Cuvântul-cheie este acceptat pe fiecare ufunc de pe această pagină; este modul cel mai curat de a menține fără alocări o buclă de procesare a semnalului în flux.
6.9.3. Funcții ufunc cu două argumente¶
arctan2() este singura ufunc adevărată cu două argumente din lista de mai sus – returnează arctangenta conștientă de cadran a y / x și difuzează cei doi operanzi:
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. Compunerea funcțiilor universale¶
Funcțiile universale se compun ca orice altă expresie de tablou. Câteva tipare care apar pe cameră:
Corecție gamma (în spațiu float)
gamma = 0.5
out = 255.0 * (frame / 255.0) ** gamma
Un netezitor trece-jos simplu (alpha apropiat de 1.0 înseamnă actualizare lentă):
alpha = 0.95
filtered = alpha * filtered + (1.0 - alpha) * sample
Sigmoid
sigmoid = 1.0 / (1.0 + np.exp(-x))
Spectru de putere în dB
spectrum = 20.0 * np.log10(np.abs(real) + 1e-12)
6.9.5. np.vectorize¶
O funcție Python obișnuită poate fi promovată la una cu formă de ufunc prin vectorize(). Obiectul apelabil rezultat acceptă scalari, iterabile sau valori 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])
În mod implicit, dtype-ul rezultatului este float. otypes= îl suprascrie:
vf_u8 = np.vectorize(f, otypes=np.uint8)
vf_u8([1, 2, 3, 4])
# array([1, 4, 9, 16], dtype=uint8)
Funcția Python trebuie să ia un singur argument și să returneze un singur număr.
vectorize() este în mare parte sintactică – funcția Python înfășurată tot trebuie să ruleze o dată per element, deci cea mai mare parte a costului de interpretor per element pe care o ufunc adevărată îl evită revine. Așteaptă o accelerare modestă de 30%-50% față de o list comprehension, nu cei 30x ai unei funcții universale adevărate. Instrumentul potrivit atunci când o funcție trebuie să funcționeze pe scalari, liste și tablouri sub același nume – nu atunci când viteza brută este scopul.
Pentru semnăturile complete de apel ale fiecărei funcții universale enumerate mai sus, vezi numpy — operații pe tablouri compatibile numpy.