6.9. Universelle Funktionen¶
Eine universelle Funktion (ufunc) ist eine mathematische Funktion, die in einem einzigen Aufruf auf jedes Element eines Arrays angewendet wird. Die arithmetischen Operatoren auf der vorherigen Seite sind universelle Funktionen in Operatorsyntax; diese Seite ist der Katalog der benannten Funktionen, die Trigonometrie, Exp / Log, Rundung und einige andere abdecken.
Jede ufunc akzeptiert einen Skalar, ein Python-Iterable oder ein ndarray und gibt entweder einen einzelnen Float (wenn die Eingabe ein Skalar war) oder ein Float-ndarray zurück:
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. Der Katalog¶
numpy stellt die mathematischen Funktionen bereit, auf die eine eingebettete Anwendung am häufigsten zurückgreift:
Trigonometrie –
sin(),cos(),tan(),asin(),acos(),atan(),arctan2(),sinh(),cosh(),tanh(),asinh(),acosh(),atanh(),sinc().Exponentialfunktionen und Logarithmen –
exp(),expm1(),log(),log10(),log2(),sqrt().
Jede Funktion verarbeitet das gesamte Array in einem einzigen Bibliotheksaufruf. Die Beschleunigung gegenüber einer Python-List-Comprehension, die math.sin() Element für Element aufruft, beträgt bei einem typischen Puffer das 10- bis 30-Fache.
6.9.2. Das Schlüsselwort out=¶
Jeder ufunc-Aufruf allokiert normalerweise ein frisches Ergebnis-Array, um seine Ausgabe aufzunehmen. In einer Schleife, die viele Male pro Sekunde läuft, summieren sich diese Allokationen und verschwenden RAM. Die Übergabe von out= – ein bereits vorhandenes Float-Array mit derselben Form wie die Eingabe – schreibt das Ergebnis in dieses Array, anstatt ein neues zu allokieren:
x = np.linspace(0, 2 * np.pi, num=256)
y = np.zeros(256)
while True:
np.sin(x, out=y)
# use y ...
Wenn out den falschen Dtype oder die falsche Form hat, löst die Funktion eine Ausnahme aus. Das Schlüsselwort wird von jeder ufunc auf dieser Seite unterstützt; es ist der sauberste Weg, um eine streamende Signalverarbeitungsschleife allokationsfrei zu halten.
6.9.3. Ufuncs mit zwei Argumenten¶
arctan2() ist die einzige echte ufunc mit zwei Argumenten in der obigen Liste – sie gibt den quadrantenbewussten Arkustangens von y / x zurück und broadcastet die beiden Operanden:
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. Universelle Funktionen kombinieren¶
Universelle Funktionen lassen sich wie jeder andere Array-Ausdruck kombinieren. Einige Muster, die auf der Kamera vorkommen:
Gammakorrektur (im Float-Raum):
gamma = 0.5
out = 255.0 * (frame / 255.0) ** gamma
Ein einfacher Tiefpassglätter (alpha nahe 1.0 bedeutet langsame Aktualisierung):
alpha = 0.95
filtered = alpha * filtered + (1.0 - alpha) * sample
Sigmoid:
sigmoid = 1.0 / (1.0 + np.exp(-x))
Leistungsspektrum in dB:
spectrum = 20.0 * np.log10(np.abs(real) + 1e-12)
6.9.5. np.vectorize¶
Eine reguläre Python-Funktion kann durch vectorize() zu einer ufunc-förmigen Funktion erhoben werden. Das resultierende Callable akzeptiert Skalare, Iterables oder ndarray-Werte:
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])
Standardmäßig ist der Dtype des Ergebnisses float. otypes= überschreibt ihn:
vf_u8 = np.vectorize(f, otypes=np.uint8)
vf_u8([1, 2, 3, 4])
# array([1, 4, 9, 16], dtype=uint8)
Die Python-Funktion muss ein einzelnes Argument entgegennehmen und eine einzelne Zahl zurückgeben.
vectorize() ist größtenteils syntaktisch – die umhüllte Python-Funktion muss weiterhin einmal pro Element ausgeführt werden, sodass der Großteil der Per-Element-Interpreterkosten, die eine echte ufunc vermeidet, wieder anfällt. Erwarten Sie eine bescheidene Beschleunigung von 30 %-50 % gegenüber einer List-Comprehension, nicht das 30-Fache einer echten universellen Funktion. Das richtige Werkzeug, wenn eine Funktion unter demselben Namen auf Skalaren, Listen und Arrays arbeiten muss – nicht, wenn rohe Geschwindigkeit das Ziel ist.
Die vollständigen Aufrufsignaturen aller oben aufgeführten universellen Funktionen finden Sie unter numpy — numpy-kompatible Array-Operationen.