webentwicklung-frage-antwort-db.com.de

Zuordnung eines NumPy-Arrays

Ist es möglich, ein NumPy-Array zuzuordnen? Wenn ja, wie?

Gegeben a_values - 2D-Array - das ist der Code, der im Moment den Trick für mich macht:

for row in range(len(a_values)):
    for col in range(len(a_values[0])):
        a_values[row][col] = dim(a_values[row][col])

Aber es ist so hässlich, dass ich vermute, dass es irgendwo in NumPy eine Funktion geben muss, die dasselbe mit etwas tut, das so aussieht:

a_values.map_in_place(dim)

aber wenn so etwas existiert, konnte ich es nicht finden.

50
mac

Es lohnt sich nur, dies vor Ort zu versuchen, wenn Sie unter erheblichen Platzbeschränkungen stehen. In diesem Fall ist es möglich, den Code ein wenig zu beschleunigen, indem Sie über eine abgeflachte Ansicht des Arrays iterieren. Da reshape eine neue Ansicht zurückgibt wenn möglich , werden die Daten selbst nicht kopiert (es sei denn, das Original hat eine ungewöhnliche Struktur).

Ich kenne keinen besseren Weg, um eine echte direkte Anwendung einer willkürlichen Python Funktion zu erreichen.

>>> def flat_for(a, f):
...     a = a.reshape(-1)
...     for i, v in enumerate(a):
...         a[i] = f(v)
... 
>>> a = numpy.arange(25).reshape(5, 5)
>>> flat_for(a, lambda x: x + 5)
>>> a

array([[ 5,  6,  7,  8,  9],
       [10, 11, 12, 13, 14],
       [15, 16, 17, 18, 19],
       [20, 21, 22, 23, 24],
       [25, 26, 27, 28, 29]])

Einige Zeiten:

>>> a = numpy.arange(2500).reshape(50, 50)
>>> f = lambda x: x + 5
>>> %timeit flat_for(a, f)
1000 loops, best of 3: 1.86 ms per loop

Es ist ungefähr doppelt so schnell wie die Nested-Loop-Version:

>>> a = numpy.arange(2500).reshape(50, 50)
>>> def nested_for(a, f):
...     for i in range(len(a)):
...         for j in range(len(a[0])):
...             a[i][j] = f(a[i][j])
... 
>>> %timeit nested_for(a, f)
100 loops, best of 3: 3.79 ms per loop

Natürlich ist das Vektorisieren immer noch schneller. Wenn Sie also eine Kopie erstellen können, verwenden Sie Folgendes:

>>> a = numpy.arange(2500).reshape(50, 50)
>>> g = numpy.vectorize(lambda x: x + 5)
>>> %timeit g(a)
1000 loops, best of 3: 584 us per loop

Und wenn Sie dim mit eingebauten Ufuncs umschreiben können, dann bitte, bitte, nicht vectorize:

>>> a = numpy.arange(2500).reshape(50, 50)
>>> %timeit a + 5
100000 loops, best of 3: 4.66 us per loop

numpy führt Operationen aus wie += genau wie Sie es erwarten - so können Sie die Geschwindigkeit einer UFUN mit In-Place-Anwendung kostenlos nutzen. Manchmal geht es sogar noch schneller! Siehe hier für ein Beispiel.


Übrigens ist meine ursprüngliche Antwort auf diese Frage, die im Bearbeitungsverlauf zu sehen ist, lächerlich und beinhaltet das Vektorisieren von Indizes in a. Es musste nicht nur ein paar Dinge tun, um vectorizes Typ-Erkennungsmechanismus zu umgehen, es stellte sich auch heraus, dass es genauso langsam war wie die Version mit verschachtelten Schleifen. So viel zur Klugheit!

52
senderle

Dies ist eine Zusammenfassung von Beiträgen, die in Antworten und Kommentaren verstreut sind, die ich geschrieben habe, nachdem ich die Antwort auf die Frage akzeptiert habe. Upvotes sind immer willkommen, aber wenn Sie diese Antwort positiv bewerten, verpassen Sie bitte nicht, auch die von senderle und (falls er eine schreibt) eryksun zu bewerten schlug die folgenden Methoden vor.

F: Ist es möglich, ein Numpy-Array zuzuordnen?
A: Ja, aber nicht mit einer einzelnen Array-Methode. Sie müssen Ihren eigenen Code schreiben.

Unten ein Skript, das die verschiedenen im Thread diskutierten Implementierungen vergleicht:

import timeit
from numpy import array, arange, vectorize, rint

# SETUP
get_array = lambda side : arange(side**2).reshape(side, side) * 30
dim = lambda x : int(round(x * 0.67328))

# TIMER
def best(fname, reps, side):
    global a
    a = get_array(side)
        t = timeit.Timer('%s(a)' % fname,
                     setup='from __main__ import %s, a' % fname)
    return min(t.repeat(reps, 3))  #low num as in place --> converge to 1

# FUNCTIONS
def mac(array_):
    for row in range(len(array_)):
        for col in range(len(array_[0])):
            array_[row][col] = dim(array_[row][col])

def mac_two(array_):
    li = range(len(array_[0]))
    for row in range(len(array_)):
        for col in li:
            array_[row][col] = int(round(array_[row][col] * 0.67328))

def mac_three(array_):
    for i, row in enumerate(array_):
        array_[i][:] = [int(round(v * 0.67328)) for v in row]


def senderle(array_):
    array_ = array_.reshape(-1)
    for i, v in enumerate(array_):
        array_[i] = dim(v)

def eryksun(array_):
    array_[:] = vectorize(dim)(array_)

def ufunc_ed(array_):
    multiplied = array_ * 0.67328
    array_[:] = rint(multiplied)

# MAIN
r = []
for fname in ('mac', 'mac_two', 'mac_three', 'senderle', 'eryksun', 'ufunc_ed'):
    print('\nTesting `%s`...' % fname)
    r.append(best(fname, reps=50, side=50))
    # The following is for visually checking the functions returns same results
    tmp = get_array(3)
    eval('%s(tmp)' % fname)
    print tmp
tmp = min(r)/100
print('\n===== ...AND THE WINNER IS... =========================')
print('  mac (as in question)       :  %.4fms [%.0f%%]') % (r[0]*1000,r[0]/tmp)
print('  mac (optimised)            :  %.4fms [%.0f%%]') % (r[1]*1000,r[1]/tmp)
print('  mac (slice-assignment)     :  %.4fms [%.0f%%]') % (r[2]*1000,r[2]/tmp)
print('  senderle                   :  %.4fms [%.0f%%]') % (r[3]*1000,r[3]/tmp)
print('  eryksun                    :  %.4fms [%.0f%%]') % (r[4]*1000,r[4]/tmp)
print('  slice-assignment w/ ufunc  :  %.4fms [%.0f%%]') % (r[5]*1000,r[5]/tmp)
print('=======================================================\n')

Die Ausgabe des obigen Skripts - zumindest in meinem System - ist:

  mac (as in question)       :  88.7411ms [74591%]
  mac (optimised)            :  86.4639ms [72677%]
  mac (slice-assignment)     :  79.8671ms [67132%]
  senderle                   :  85.4590ms [71832%]
  eryksun                    :  13.8662ms [11655%]
  slice-assignment w/ ufunc  :  0.1190ms [100%]

Wie Sie sehen können, erhöht mit numpy's ufunc die Geschwindigkeit um mehr als 2 und fast 3 Größenordnungen im Vergleich zu den zweitbesten bzw. schlechtesten Alternativen.

Wenn die Verwendung von ufunc nicht möglich ist, werden hier nur die anderen Alternativen verglichen:

  mac (as in question)       :  91.5761ms [672%]
  mac (optimised)            :  88.9449ms [653%]
  mac (slice-assignment)     :  80.1032ms [588%]
  senderle                   :  86.3919ms [634%]
  eryksun                    :  13.6259ms [100%]

HTH!

44
mac

Warum nicht die numpy-Implementierung und den out_-Trick verwenden?

from numpy import array, arange, vectorize, rint, multiply, round as np_round 

def fmilo(array_):
    np_round(multiply(array_ ,0.67328, array_), out=array_)

bekam:

===== ...AND THE WINNER IS... =========================
  mac (as in question)       :  80.8470ms [130422%]
  mac (optimised)            :  80.2400ms [129443%]
  mac (slice-assignment)     :  75.5181ms [121825%]
  senderle                   :  78.9380ms [127342%]
  eryksun                    :  11.0800ms [17874%]
  slice-assignment w/ ufunc  :  0.0899ms [145%]
  fmilo                      :  0.0620ms [100%]
=======================================================
3
fabrizioM

wenn Ufuncs nicht möglich sind, sollten Sie möglicherweise die Verwendung von Cython in Betracht ziehen. Es ist einfach zu integrieren und beschleunigt die Verwendung von Numpy-Arrays erheblich.

2
LBarret

Dies ist nur eine aktualisierte Version von Macs Write-Up, die für Python 3.x aktualisiert und mit numba und numpy.frompyfunc ergänzt wurde .

numpy.frompyfunc verwendet eine beliebige python= Funktion und gibt eine Funktion zurück, die die Funktion elementweise anwendet, wenn sie auf ein numpy.array angewendet wird.
Es ändert jedoch den Datentyp des Arrays in object, sodass er nicht vorhanden ist und zukünftige Berechnungen für dieses Array langsamer sind.
Um diesen Nachteil zu vermeiden, wird im Test numpy.ndarray.astype aufgerufen, wobei der Datentyp auf int zurückgesetzt wird.

Als Randnotiz:
Numba ist nicht in Pythons Basisbibliotheken enthalten und muss extern heruntergeladen werden, wenn Sie es testen möchten. In diesem Test tut es eigentlich nichts, und wenn es mit @ jit (nopython = True) aufgerufen worden wäre, hätte es eine Fehlermeldung gegeben sagen, dass es dort nichts optimieren kann. Da numba jedoch häufig den Code beschleunigen kann, der in einem funktionalen Stil geschrieben wurde, wird es aus Gründen der Integrität einbezogen.

import timeit
from numpy import array, arange, vectorize, rint, frompyfunc
from numba import autojit

# SETUP
get_array = lambda side : arange(side**2).reshape(side, side) * 30
dim = lambda x : int(round(x * 0.67328))

# TIMER
def best(fname, reps, side):
    global a
    a = get_array(side)
    t = timeit.Timer('%s(a)' % fname,
                     setup='from __main__ import %s, a' % fname)
    return min(t.repeat(reps, 3))  #low num as in place --> converge to 1

# FUNCTIONS
def mac(array_):
    for row in range(len(array_)):
        for col in range(len(array_[0])):
            array_[row][col] = dim(array_[row][col])

def mac_two(array_):
    li = range(len(array_[0]))
    for row in range(len(array_)):
        for col in li:
            array_[row][col] = int(round(array_[row][col] * 0.67328))

def mac_three(array_):
    for i, row in enumerate(array_):
        array_[i][:] = [int(round(v * 0.67328)) for v in row]


def senderle(array_):
    array_ = array_.reshape(-1)
    for i, v in enumerate(array_):
        array_[i] = dim(v)

def eryksun(array_):
    array_[:] = vectorize(dim)(array_)

@autojit
def numba(array_):
    for row in range(len(array_)):
        for col in range(len(array_[0])):
            array_[row][col] = dim(array_[row][col])


def ufunc_ed(array_):
    multiplied = array_ * 0.67328
    array_[:] = rint(multiplied)

def ufunc_frompyfunc(array_):
    udim = frompyfunc(dim,1,1)
    array_ = udim(array_)
    array_.astype("int")

# MAIN
r = []
totest = ('mac', 'mac_two', 'mac_three', 'senderle', 'eryksun', 'numba','ufunc_ed','ufunc_frompyfunc')
for fname in totest:
    print('\nTesting `%s`...' % fname)
    r.append(best(fname, reps=50, side=50))
    # The following is for visually checking the functions returns same results
    tmp = get_array(3)
    eval('%s(tmp)' % fname)
    print (tmp)
tmp = min(r)/100
results = list(Zip(totest,r))
results.sort(key=lambda x: x[1])

print('\n===== ...AND THE WINNER IS... =========================')
for name,time in results:
    Out = '{:<34}: {:8.4f}ms [{:5.0f}%]'.format(name,time*1000,time/tmp)
    print(Out)
print('=======================================================\n')



Und schließlich die Ergebnisse:

===== ...AND THE WINNER IS... =========================
ufunc_ed                          :   0.3205ms [  100%]
ufunc_frompyfunc                  :   3.8280ms [ 1194%]
eryksun                           :   3.8989ms [ 1217%]
mac_three                         :  21.4538ms [ 6694%]
senderle                          :  22.6421ms [ 7065%]
mac_two                           :  24.6230ms [ 7683%]
mac                               :  26.1463ms [ 8158%]
numba                             :  27.5041ms [ 8582%]
=======================================================
1
Sanitiy