Le moyen le plus efficace pour inverser un tableau numpy

276

Croyez-le ou non, après avoir profilé mon code actuel, l'opération répétitive de réversion du tableau numpy a mangé une partie géante du temps d'exécution. Ce que j'ai en ce moment, c'est la méthode commune basée sur la vue:

reversed_arr = arr[::-1]

Y a-t-il une autre façon de le faire plus efficacement, ou est-ce juste une illusion de mon obsession pour les performances numpy irréalistes?

nye17
la source
27
Euh ... arr[::-1]renvoie juste une vue inversée. C'est aussi rapide que possible et ne dépend pas du nombre d'éléments dans le tableau, car cela ne fait que changer les foulées. Est-ce que ce que vous inversez est en fait un tableau numpy?
Joe Kington
oui, en effet, arrest un tableau numpy.
nye17
12
Hmmm ... Eh bien, sur mon ordinateur portable, cela prend environ 670 nanosecondes quelle que soit la longueur de la baie. Si c'est votre goulot d'étranglement, vous devrez peut-être changer de langue ... Je suis sûr que vous ne trouverez pas un moyen plus rapide d'inverser un tableau numpy. Bonne chance en tout cas!
Joe Kington
6
Eh bien, devez-vous nécessairement l'exécuter dans une boucle? Dans certains cas, il est préférable de créer un tableau numpy avec des millions d'éléments, puis d'opérer sur l'ensemble du tableau. Même si vous utilisez une méthode de différence finie ou quelque chose de similaire où le résultat dépend du résultat précédent, vous pouvez parfois le faire. (Accent sur parfois ...) En tout cas, si la vitesse est l'objectif principal, fortran est toujours roi. f2pyest votre ami! Il est souvent utile d'écrire des parties critiques d'un algorithme (en particulier dans le calcul scientifique) dans un autre langage et de l'appeler à partir de python. Bonne chance!
Joe Kington
1
@berto. Il est plus lent car il s'agit d'un wrapper pour arr[::-1]: github.com/numpy/numpy/blob/master/numpy/lib/twodim_base.py . Recherchez def flipud. La fonction comporte littéralement quatre lignes.
Mad Physicist

Réponses:

240

Lorsque vous créez, reversed_arrvous créez une vue dans le tableau d'origine. Vous pouvez ensuite modifier le tableau d'origine et la vue sera mise à jour pour refléter les modifications.

Recréez-vous la vue plus souvent que nécessaire? Vous devriez pouvoir faire quelque chose comme ceci:

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

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

Je ne suis pas un expert numpy, mais cela semble être le moyen le plus rapide de faire les choses en numpy. Si c'est ce que vous faites déjà, je ne pense pas que vous puissiez l'améliorer.

PS Grande discussion des vues numpy ici:

Vue sur un tableau numpy?

steveha
la source
Cela aide-t-il à créer un objet tranche, puis à le réutiliser sur de nombreux tableaux?
endolith
1
En fait, je viens de le tester et je ne vois aucune différence avec l'objet tranche créé en dehors de la boucle. (Oh, attendez, c'est très légèrement plus rapide. De façon répétée 43,4 ms vs 44,3 ms pour une boucle 1000000)
endolith
Quelle est la look_atfonction supposée faire?
mrgloom
1
@mrgloom Il est censé représenter n'importe quelle tâche qui examine les données. Le but de l'exemple était de montrer que la vue reversed_arrest toujours utilisable après la modification des données sous-jacentes. L'écriture de nouvelles valeurs dans le tableau n'invalide pas la vue. En fait, vous pouvez également utiliser la vue pour écrire de nouvelles valeurs dans le tableau. reversed_arr[0] = 99définirait le dernier élément du tableau sur 99, comme arr[-1] = 99pour.
steveha
60

Comme mentionné ci-dessus, a[::-1]ne crée vraiment qu'une vue, c'est donc une opération à temps constant (et en tant que telle, ne prend pas plus de temps à mesure que le tableau grandit). Si vous avez besoin que le tableau soit contigu (par exemple parce que vous effectuez de nombreuses opérations vectorielles avec lui), ascontiguousarrayest à peu près aussi rapide que flipup/ fliplr:

entrez la description de l'image ici


Code pour générer le tracé:

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,
)
Nico Schlömer
la source
perfplot nécessite au moins Python 3.6 car il utilise des f-strings (Literal String Interpolation)
fivef
42

Parce que cela ne semble pas encore être marqué comme une réponse ... La réponse de Thomas Arildsen devrait être la bonne: utilisez simplement

np.flipud(your_array) 

s'il s'agit d'un tableau 1d (tableau de colonnes).

Avec matrizes faire

fliplr(matrix)

si vous voulez inverser les lignes et flipud(matrix)si vous voulez retourner les colonnes. Pas besoin de faire de votre tableau de colonnes 1d un tableau de lignes en 2 dimensions (matrice avec une couche None), puis de le retourner.

zauberfein
la source
38

np.fliplr() retourne le tableau de gauche à droite.

Notez que pour les tableaux 1d, vous devez le tromper un peu:

arr1d = np.array(some_sequence)
reversed_arr = np.fliplr([arr1d])[0]
tooty44
la source
34
reversed_arr = np.flipud(arr1d)semble fonctionner directement.
Thomas Arildsen
3

Je développerai la réponse précédente à propos de np.fliplr(). Voici du code qui montre comment construire un tableau 1d, le transformer en tableau 2d, le retourner, puis reconvertir en tableau 1d. time.clock()sera utilisé pour garder le temps, qui est présenté en termes de secondes.

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

Avec une déclaration imprimée non commentée:

[2 1 0]
0.00203907123594

Avec une déclaration imprimée commentée:

5.59799927506e-05

Donc, en termes d'efficacité, je pense que c'est décent. Pour ceux d'entre vous qui aiment le faire en une seule ligne, voici ce formulaire.

np.fliplr(np.atleast_2d(np.array(range(3))))[0]
M. Murphy
la source
3
Timing quelque chose avec un si petit tableau est assez inutile. Si vous voulez comparer des choses, il serait préférable d'utiliser quelque chose qui prend du temps, comme 3000 ou peut-être même plus d'éléments.
Barabas
0

En développant ce que les autres ont dit, je vais donner un court exemple.

Si vous avez un tableau 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])

Mais si vous travaillez avec un tableau 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]])

Cela n'inverse pas réellement la matrice.

Devrait utiliser np.flip pour inverser réellement les éléments

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

Si vous souhaitez imprimer les éléments d'une matrice un par un, utilisez-les à plat avec retournement

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

Pour le faire fonctionner avec des nombres négatifs et une longue liste, vous pouvez effectuer les opérations suivantes:

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

Où flipud est pour 1d arra

Boris Stoyanov
la source