6.9. Funzioni universali¶
Una funzione universale (ufunc) è una funzione matematica che si applica a ogni elemento di un array in un’unica chiamata. Gli operatori aritmetici della pagina precedente sono funzioni universali con la sintassi degli operatori; questa pagina è il catalogo di quelle con nome che coprono trigonometria, exp / log, arrotondamento e alcune altre.
Ogni ufunc accetta uno scalare, un iterabile Python o un ndarray e restituisce un singolo float (quando l’input era scalare) oppure un ndarray di 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. Il catalogo¶
numpy espone le funzioni matematiche a cui un’applicazione embedded ricorre più spesso:
Trigonometria –
sin(),cos(),tan(),asin(),acos(),atan(),arctan2(),sinh(),cosh(),tanh(),asinh(),acosh(),atanh(),sinc().Esponenziali e logaritmi –
exp(),expm1(),log(),log10(),log2(),sqrt().
Ogni funzione elabora l’intero array in un’unica chiamata di libreria. L’accelerazione rispetto a una list comprehension Python che chiama math.sin() elemento per elemento è di 10-30x su un buffer tipico.
6.9.2. La parola chiave out=¶
Ogni chiamata a una ufunc normalmente alloca un nuovo array di risultato per contenere il suo output. In un ciclo che viene eseguito molte volte al secondo, queste allocazioni si accumulano e sprecano RAM. Passare out= – un array float già esistente, della stessa forma dell’input – scrive il risultato in quell’array invece di allocarne uno nuovo:
x = np.linspace(0, 2 * np.pi, num=256)
y = np.zeros(256)
while True:
np.sin(x, out=y)
# use y ...
Se out ha un dtype o una forma errati, la funzione solleva un’eccezione. La parola chiave è supportata da ogni ufunc in questa pagina; è il modo più pulito per mantenere senza allocazioni un ciclo di elaborazione del segnale in streaming.
6.9.3. Ufunc a due argomenti¶
arctan2() è l’unica vera ufunc a due argomenti nell’elenco precedente – restituisce l’arcotangente di y / x con consapevolezza del quadrante e propaga i due operandi tramite broadcasting:
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. Comporre funzioni universali¶
Le funzioni universali si compongono come qualsiasi altra espressione su array. Alcuni schemi che ricorrono sulla camera:
Correzione gamma (nello spazio float)
gamma = 0.5
out = 255.0 * (frame / 255.0) ** gamma
Un semplice smoother passa-basso (alpha vicino a 1.0 significa aggiornamento lento):
alpha = 0.95
filtered = alpha * filtered + (1.0 - alpha) * sample
Sigmoide
sigmoid = 1.0 / (1.0 + np.exp(-x))
Spettro di potenza in dB
spectrum = 20.0 * np.log10(np.abs(real) + 1e-12)
6.9.5. np.vectorize¶
Una normale funzione Python può essere promossa a una con forma di ufunc tramite vectorize(). Il callable risultante accetta scalari, iterabili o 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])
Per impostazione predefinita il dtype del risultato è float. otypes= lo sovrascrive:
vf_u8 = np.vectorize(f, otypes=np.uint8)
vf_u8([1, 2, 3, 4])
# array([1, 4, 9, 16], dtype=uint8)
La funzione Python deve accettare un singolo argomento e restituire un singolo numero.
vectorize() è per lo più sintattica – la funzione Python avvolta deve comunque essere eseguita una volta per elemento, quindi gran parte del costo per elemento dell’interprete che una vera ufunc evita è di nuovo presente. Aspettati una modesta accelerazione del 30%-50% rispetto a una list comprehension, non il 30x di una vera funzione universale. È lo strumento giusto quando una funzione deve operare su scalari, liste e array sotto lo stesso nome – non quando l’obiettivo è la pura velocità.
Per le firme di chiamata complete di ogni funzione universale elencata sopra, consulta numpy — operazioni su array compatibili con numpy.