Wie kann ich auf effiziente Weise auf die umgebenden 8 Zellen für ein 2D-Numpy-Array zugreifen und diese ändern?
Ich habe ein 2D-numpy-Array wie folgt:
arr = np.random.rand(720, 1440)
Für jede Gitterzelle möchte ich die mittlere Zelle um 10 % reduzieren, die umgebenden 8 Zellen (weniger für Eckzellen), aber nur, wenn der Wert der umgebenden Zelle 0,25 überschreitet. Ich vermute, dass dies nur mit einer for-Schleife möglich ist, würde aber gerne sehen, ob es bessere/schnellere Lösungen gibt.
- BEARBEITEN: Für schleifenbasierte Lösungen:
arr = np.random.rand(720, 1440)
for (x, y), value in np.ndenumerate(arr):
# Find 10% of current cell
reduce_by = value * 0.1
# Reduce the nearby 8 cells by 'reduce_by' but only if the cell value exceeds 0.25
# [0] [1] [2]
# [3] [*] [5]
# [6] [7] [8]
# * refers to current cell
# cell [0]
arr[x-1][y+1] = arr[x-1][y+1] * reduce_by if arr[x-1][y+1] > 0.25 else arr[x-1][y+1]
# cell [1]
arr[x][y+1] = arr[x][y+1] * reduce_by if arr[x][y+1] > 0.25 else arr[x][y+1]
# cell [2]
arr[x+1][y+1] = arr[x+1][y+1] * reduce_by if arr[x+1][y+1] > 0.25 else arr[x+1][y+1]
# cell [3]
arr[x-1][y] = arr[x-1][y] * reduce_by if arr[x-1][y] > 0.25 else arr[x-1][y]
# cell [4] or current cell
# do nothing
# cell [5]
arr[x+1][y] = arr[x+1][y] * reduce_by if arr[x+1][y] > 0.25 else arr[x+1][y]
# cell [6]
arr[x-1][y-1] = arr[x-1][y-1] * reduce_by if arr[x-1][y-1] > 0.25 else arr[x-1][y-1]
# cell [7]
arr[x][y-1] = arr[x][y-1] * reduce_by if arr[x][y-1] > 0.25 else arr[x][y-1]
# cell [8]
arr[x+1][y-1] = arr[x+1][y-1] * reduce_by if arr[x+1][y-1] > 0.25 else arr[x+1][y-1]
Lösung des Problems
Keine Notwendigkeit für Schleifen, vermeiden Sie die üblichen Python-Schleifen, sie sind sehr langsam. Verlassen Sie sich für mehr Effizienz auf die eingebaute Matrixoperation, "universelle" Funktionen, Filter, Masken und Bedingungen von numpy, wann immer Sie können. https://realpython.com/numpy-array-programmin Für komplizierte Berechnungen ist die Vektorisierung nicht schlecht, siehe einige Diagramme und Benchmarks Effizienteste Methode, um Funktionen über numpy-Arrays abzubilden (verwenden Sie sie nur nicht für einfachere Matrixoperationen wie das Quadrieren von Zellen, eingebaute Funktionen übertreffen)
Es ist leicht zu sehen, dass jede interne Zelle auf 0,9 bis zu 8-mal multipliziert würde, weil 8 Nachbarn (das ist um 0,1 reduziert) und zusätzlich eine zentrale Zelle ist, aber sie kann nicht unter 0,25/0,9 = reduziert werden 18.5. Für Rand- und Eckzellen verringert sich die Anzahl der Abnahmen auf das 6- und 3-fache.
Deshalb
x1 = 700 # for debugging use lesser arrays
x2 = 1400
neighbors = 8 # each internal cell has 8 neighbors
for i in range(neighbors):
view1 = arr[1:-1, 1:-1] # internal cells only
arr [1:x1, 1:-1] = np.multiply(view1,.9, where = view1 >.25)
arr [1:-1, 1:-1] *=.9
Ränder und Ecken werden gleich behandelt mit Nachbarn = 5 bzw. 3 und unterschiedlichen Ansichten. Ich denke, alle drei Fälle können in einer Formel mit kompliziertem Where-Case verbunden werden, aber die Beschleunigung wäre moderat, da Ränder und Ecken einen kleinen Bruchteil aller Zellen einnehmen.
Hier habe ich eine kleine Schleife verwendet, aber es sind nur 8 Wiederholungen. Es sollte auch möglich sein, die Schleife loszuwerden, indem man Power-, Log-, Integer-Teil- und Max-Funktionen verwendet, was zu einem etwas ungeschickten, aber etwas schnelleren Einzeiler führt, etwas herum
numpy.multiply( view1, x ** numpy.max( numpy.ceil( (numpy.log (* view1/x... / log(.9)
Wir können auch eine andere nützliche Technik ausprobieren, die Vektorisierung. Die Vektorisierung baut eine Funktion auf, die dann auf alle Elemente des Arrays angewendet werden kann.
Lassen Sie uns zur Abwechslung Ränder/Schwellenwerte voreinstellen, um den genauen Koeffizienten zu ermitteln, mit dem multipliziert werden soll. Hier ist, wie der Code aussehen soll
n = 8
decrease_by = numpy.logspace(1,N,num=n, base=x, endpoint=False)
margins = decrease_by *.25
# to do: save border rows for further analysis, skip this for simplicity now
view1 = a [1: -1, 1: -1]
def decrease(x):
k = numpy.searchsorted(margin, a)
return x * decrease_by[k]
f = numpy.vectorize(decrease)
f(view1)
Anmerkung 1 Man kann versuchen, verschiedene Kombinationen von Ansätzen zu verwenden, z. B. vorberechnete Ränder mit Matrixarithmetik statt Vektorisierung zu verwenden. Vielleicht gibt es noch mehr Tricks, um jede der oben genannten Lösungen oder Kombinationen davon etwas zu beschleunigen.
Anmerkung 2 PyTorch hat viele Ähnlichkeiten mit der Numpy-Funktionalität, kann aber stark von der GPU profitieren. Wenn Sie eine anständige GPU haben, sollten Sie PyTorch in Betracht ziehen. Es gab Versuche mit gpu-basiertem numpy (gluon, aufgegebenes gnumpy, minpy) Mehr auf gpu's
https://stsievert.com/blog/2016/07/01/numpy-gpu/
Keine Kommentare:
Kommentar veröffentlichen