Sie ursprünglich über das tun dies mit einer einzigen Funktion gefragt, die über Reihen Schleifen und legt eine Liste von Filtern jede Reihe. Eine Herausforderung bei diesem Ansatz besteht darin, dass Numba die Eingabe/Ausgabe-Typen jeder Funktion kennen oder in der Lage sein muss, daraus abzuleiten. Ich bin mir nicht bewusst, wie man die Anforderungen von Numba in dieser Situation erfüllen kann (was nicht heißen soll, dass es keine gibt). Wenn es einen Weg gäbe, dies zu tun, könnte es eine bessere Lösung sein (und ich würde gerne wissen, was es ist).
Eine Alternative besteht darin, den Code, der die Zeilen durchläuft, in die Filter selbst zu verschieben. Da die Filter Numba-Funktionen sind, sollte dies die Geschwindigkeit beibehalten. Die Funktion, die die Filter anwendet, würde länger numba verwenden; Es würde einfach die Filterliste durchlaufen. Da jedoch die Anzahl der Filter im Verhältnis zur Größe der Datenmatrix klein ist, wird sich die Geschwindigkeit hoffentlich nicht zu sehr auf die Geschwindigkeit auswirken. Da diese Funktion nicht mehr Numba verwendet, ist das Problem der "heterogenen Liste" kein Problem mehr.
Dieser Ansatz funktionierte, als ich es getestet habe (nopython Modus ist in Ordnung). In Testfällen waren Filter, die als numba-Funktionen implementiert wurden, 10-18x schneller als Filter, die als Klassenmethoden implementiert wurden (obwohl Klassen als numba jitclasses implementiert wurden; nicht sicher, was dort vor sich ging). Um ein wenig Modularität zu erreichen, können Filter als Verschlüsse konstruiert werden, so dass ähnliche Filter unter Verwendung verschiedener Parameter definiert werden können.
Zum Beispiel, hier sind Filter, die Summen von Leistungen berechnen. Bei einer Matrix x
arbeitet der Filter über die Spalten von x und gibt eine Ausgabe für jede Zeile aus.Es gibt einen Vektor v
, wo v[i] = sum(x[i, :] ** power)
# filter constructor
def sumpow(power):
@numba.jit(nopython=True)
def run_filter(x):
(nrows, ncols) = x.shape
result = np.zeros(nrows)
for i in range(nrows):
for j in range(ncols):
result[i] += x[i,j] ** power
return result
return run_filter
# define filters
sum1 = sumpow(1) # sum of elements
sum2 = sumpow(2) # sum of elements squared
# apply a single filter
v = sum2(x)
Die Funktion mehrere Filter anwenden sieht wie folgt aus. Der Ausgang jedes Filters ist in einer Spalte des Ausgangs gestapelt.
def apply_filters(x, filters):
result = np.empty((x.shape[0], len(filters)))
for (i, f) in enumerate(filters):
result[:, i] = f(x)
return result
y = apply_filters(x, [sum1, sum2])
Zeitgebungsergebnisse
- Datenmatrix: zufällige Einträge aus Standardnormalverteilung gezogen, float64, 5 Millionen Zeilen x 10 Spalten. Alle Methoden wurden mit der gleichen Matrix getestet.
- Filters:
sum2
Filter oben, wiederholt 20x in einer Liste: [sum2, sum2, ...]
- Timed mit IPython der% timeit Funktion, am besten von 3 Läufen
- Numerische Ausgänge aller Methoden stimmen
- Numba Funktionsfilter (wie oben gezeigt): 2.25s
- Numba jitclass Filter: 28.3s
- Pure NumPy (Vektorisiert ops verwenden, keine Loops): 8.64s
Ich stelle mir vor Numba könnte für komplexere Filter zu NumPy relativ gewinnen.
Ich habe mir das kurz angeschaut und konnte nicht genau herausfinden, was das Problem ist. Die Aufzählung über 'source' und' filters' sollte schnell sein, da sie nur 10 oder 50 Elemente lang sind. Wie auch immer, 'src' ist 5M Elemente und so wird die eigentliche Arbeit von' filt.transform' erledigt (wenn ich das richtig verstehe)? Daher hatte ich Probleme mit einem anschaulichen Testfall, bei dem es einen Unterschied machte, wie man die äußeren Schleifen machte - vorausgesetzt, dass 'filt.transform' optimiert ist, ist alles sehr ähnlich ... – DavidW