6.9. Universella funktioner

En universell funktion (ufunc) är en matematisk funktion som tillämpas på varje element i en array i ett enda anrop. De aritmetiska operatorerna på föregående sida är universella funktioner i operatorsyntax; denna sida är katalogen över de namngivna som täcker trigonometri, exp / log, avrundning och några andra.

Varje ufunc accepterar en skalär, ett itererbart Python-objekt eller en ndarray, och returnerar antingen en enda float (när indata var skalär) eller en float-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. Katalogen

numpy exponerar de matematiska funktioner som en inbäddad applikation oftast behöver:

Varje funktion bearbetar hela arrayen i ett enda biblioteksanrop. Hastighetsökningen jämfört med en Python-listförståelse som anropar math.sin() element för element är 10-30x på en typisk buffert.

6.9.2. Nyckelordet out=

Varje ufunc-anrop allokerar normalt en ny resultatarray för att hålla sin utdata. I en loop som körs många gånger per sekund adderas dessa allokeringar och slösar RAM. Att skicka out= – en float-array som redan finns, med samma form som indata – skriver resultatet in i den arrayen i stället för att allokera en ny:

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

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

Om out har fel dtype eller form utlöser funktionen ett undantag. Nyckelordet stöds av varje ufunc på denna sida; det är det renaste sättet att hålla en strömmande signalbehandlingsloop allokeringsfri.

6.9.3. Ufunc med två argument

arctan2() är den enda äkta ufunc med två argument i listan ovan – den returnerar den kvadrantmedvetna arctangenten av y / x och broadcastar de två operanderna:

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. Komponera universella funktioner

Universella funktioner komponeras som vilket annat array-uttryck som helst. Några mönster som dyker upp på kameran:

Gammakorrigering (i float-rymd)

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

En enkel lågpassutjämnare (alpha nära 1.0 betyder långsam uppdatering):

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

Sigmoid

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

Effektspektrum i dB

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

6.9.5. np.vectorize

En vanlig Python-funktion kan befordras till en ufunc-formad funktion med vectorize(). Det resulterande anropsbara objektet accepterar skalärer, itererbara objekt eller ndarray-värden:

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])

Som standard är resultatets dtype float. otypes= åsidosätter det:

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

Python-funktionen måste ta ett enda argument och returnera ett enda tal.

vectorize() är mestadels syntaktisk – den inkapslade Python-funktionen måste fortfarande köras en gång per element, så det mesta av den per-element-tolkkostnad som en äkta ufunc undviker är tillbaka. Förvänta dig en blygsam hastighetsökning på 30 %-50 % jämfört med en listförståelse, inte de 30x som en äkta universell funktion ger. Rätt verktyg när en funktion måste fungera på skalärer, listor och arrayer under samma namn – inte när ren hastighet är målet.

För de fullständiga anropssignaturerna för varje universell funktion som listas ovan, se numpy — numpy-kompatibla arrayoperationer.