Il modo più efficiente per invertire un array intorpidito


276

Che ci crediate o no, dopo aver profilato il mio codice attuale, l'operazione ripetitiva di inversione numerica dell'array ha mangiato un grosso pezzo del tempo di esecuzione. Quello che ho in questo momento è il metodo basato sulla vista comune:

reversed_arr = arr[::-1]

C'è un altro modo per farlo in modo più efficiente, o è solo un'illusione dalla mia ossessione per la prestazione insensibile irrealistica?


27
Ehm ... arr[::-1]restituisce solo una vista invertita. È il più veloce possibile e non dipende dal numero di elementi nell'array, poiché cambia solo i passi. Quello che stai invertendo è in realtà un array intorpidito?
Joe Kington,

sì, in effetti, arrè un array intorpidito.
nye17

12
Hmmm ... Beh, sul mio laptop ci vogliono circa 670 nanosecondi indipendentemente dalla lunghezza dell'array. Se questo è il tuo collo di bottiglia, potresti dover cambiare lingua ... Sono abbastanza sicuro che non troverai un modo più veloce di invertire un array intorpidito. Buona fortuna, in ogni caso!
Joe Kington,

6
Bene, devi necessariamente eseguirlo all'interno di un ciclo? In alcuni casi, è meglio creare un array intorpidito con milioni di elementi e quindi operare sull'intero array. Anche se stai utilizzando un metodo di differenza finita o qualcosa di simile in cui il risultato dipende dal risultato precedente, a volte puoi farlo. (Enfasi su a volte ...) Ad ogni modo, se la velocità è l'obiettivo principale, Fortran è ancora il re. f2pyÈ tuo amico! Spesso vale la pena scrivere parti critiche delle prestazioni di un algoritmo (specialmente nel calcolo scientifico) in un'altra lingua e chiamarlo da Python. In bocca al lupo!
Joe Kington,

1
@berto. È più lento poiché è un wrapper per arr[::-1]: github.com/numpy/numpy/blob/master/numpy/lib/twodim_base.py . Cerca def flipud. La funzione ha letteralmente quattro righe.
Fisico pazzo,

Risposte:


240

Quando crei, reversed_arrstai creando una vista nell'array originale. È quindi possibile modificare l'array originale e la vista verrà aggiornata per riflettere le modifiche.

Stai ricreando la vista più spesso del necessario? Dovresti essere in grado di fare qualcosa del genere:

arr = np.array(some_sequence)
reversed_arr = arr[::-1]

do_something(arr)
look_at(reversed_arr)
do_something_else(arr)
look_at(reversed_arr)

Non sono un esperto di intorpidimento, ma sembra che sarebbe il modo più veloce per fare le cose in intorpidimento. Se questo è quello che stai già facendo, non credo che tu possa migliorarlo.

PS Grande discussione su viste insensibili qui:

Visualizza su un array intorpidito?


Aiuta a creare un oggetto fetta e quindi riutilizzarlo su molte matrici?
endolito

1
In realtà l'ho appena provato e non vedo alcuna differenza con l'oggetto slice creato al di fuori del ciclo. (Oh aspetta, è leggermente più veloce. Ripetibilmente 43,4 ms contro 44,3 ms per un loop 1000000)
endolith,

Cosa look_atdovrebbe fare la funzione?
MrGloom,

1
@mrgloom Dovrebbe rappresentare qualsiasi attività che guardi i dati. Il punto dell'esempio era mostrare che la vista reversed_arrè ancora utilizzabile dopo la modifica dei dati sottostanti. La scrittura di nuovi valori nell'array non invalida la vista. In realtà è anche possibile utilizzare la vista per scrivere nuovi valori nell'array. reversed_arr[0] = 99imposterebbe l'ultimo elemento dell'array su 99, lo stesso che arr[-1] = 99farebbe.
Steveha,

60

Come accennato in precedenza, in a[::-1]realtà crea solo una vista, quindi è un'operazione a tempo costante (e come tale non richiede più tempo man mano che l'array cresce). Se hai bisogno che l'array sia contiguo (ad esempio perché stai eseguendo molte operazioni vettoriali con esso), ascontiguousarrayè veloce quanto flipup/ fliplr:

inserisci qui la descrizione dell'immagine


Codice per generare la trama:

import numpy
import perfplot


perfplot.show(
    setup=lambda n: numpy.random.randint(0, 1000, n),
    kernels=[
        lambda a: a[::-1],
        lambda a: numpy.ascontiguousarray(a[::-1]),
        lambda a: numpy.fliplr([a])[0],
    ],
    labels=["a[::-1]", "ascontiguousarray(a[::-1])", "fliplr"],
    n_range=[2 ** k for k in range(25)],
    xlabel="len(a)",
    logx=True,
    logy=True,
)

perfplot richiede almeno Python 3.6 perché utilizza f-string (interpolazione letterale di stringhe)
cinque

42

Perché questo sembra non essere ancora contrassegnato come risposta ... La risposta di Thomas Arildsen dovrebbe essere quella giusta: basta usare

np.flipud(your_array) 

se è un array 1d (array di colonne).

Con matrates fare

fliplr(matrix)

se si desidera invertire le righe e flipud(matrix)capovolgere le colonne. Non è necessario rendere l'array di colonne 1d un array di righe bidimensionali (matrice con un livello Nessuno) e quindi capovolgerlo.


38

np.fliplr() inverte l'array da sinistra a destra.

Si noti che per gli array 1d, è necessario ingannarlo un po ':

arr1d = np.array(some_sequence)
reversed_arr = np.fliplr([arr1d])[0]

34
reversed_arr = np.flipud(arr1d)sembra funzionare direttamente.
Thomas Arildsen,

3

Espanderò la risposta precedente su np.fliplr(). Ecco un po 'di codice che dimostra la costruzione di un array 1d, trasformandolo in un array 2d, capovolgendolo, quindi riconvertendolo in un array 1d. time.clock()verrà utilizzato per mantenere il tempo, che viene presentato in termini di secondi.

import time
import numpy as np

start = time.clock()
x = np.array(range(3))
#transform to 2d
x = np.atleast_2d(x)
#flip array
x = np.fliplr(x)
#take first (and only) element
x = x[0]
#print x
end = time.clock()
print end-start

Con dichiarazione di stampa non commentata:

[2 1 0]
0.00203907123594

Con la dichiarazione di stampa commentata:

5.59799927506e-05

Quindi, in termini di efficienza, penso che sia decente. Per quelli di voi che amano farlo in una riga, ecco quella forma.

np.fliplr(np.atleast_2d(np.array(range(3))))[0]

3
Cronometrare qualcosa con un array così piccolo è piuttosto inutile. Se vuoi confrontare le cose, sarebbe meglio usare qualcosa che richiede un po 'di tempo, come 3000 o forse anche più elementi.
Barabas,

0

Espandendo ciò che altri hanno detto, farò un breve esempio.

Se si dispone di un array 1D ...

>>> import numpy as np
>>> x = np.arange(4) # array([0, 1, 2, 3])
>>> x[::-1] # returns a view
Out[1]: 
array([3, 2, 1, 0])

Ma se stai lavorando con un array 2D ...

>>> x = np.arange(10).reshape(2, 5)
>>> x
Out[2]:
array([[0, 1, 2, 3, 4],
       [5, 6, 7, 8, 9]])

>>> x[::-1] # returns a view:
Out[3]: array([[5, 6, 7, 8, 9],
               [0, 1, 2, 3, 4]])

Questo in realtà non inverte Matrix.

Dovrebbe usare np.flip per invertire effettivamente gli elementi

>>> np.flip(x)
Out[4]: array([[9, 8, 7, 6, 5],
               [4, 3, 2, 1, 0]])

Se si desidera stampare gli elementi di una matrice uno a uno, utilizzare flat insieme a flip

>>> for el in np.flip(x).flat:
>>>     print(el, end = ' ')
9 8 7 6 5 4 3 2 1 0

-1

Per farlo funzionare con numeri negativi e un lungo elenco è possibile effettuare le seguenti operazioni:

b = numpy.flipud(numpy.array(a.split(),float))

Dove flipud è per 1d arra

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.