358

Believe it or not, after profiling my current code, the repetitive operation of numpy array reversion ate a giant chunk of the running time. What I have right now is the common view-based method:

reversed_arr = arr[::-1]

Is there any other way to do it more efficiently, or is it just an illusion from my obsession with unrealistic numpy performance?

nye17
  • 12,857
  • 11
  • 58
  • 68
  • 39
    Er... `arr[::-1]` just returns a reversed view. It's as fast as you can get, and doesn't depend on the number of items in the array, as it just changes the strides. Is what you're reversing actually a numpy array? – Joe Kington Jul 21 '11 at 05:14
  • yes, indeed, `arr` is a numpy array. – nye17 Jul 21 '11 at 05:15
  • 14
    Hmmm... Well, on my laptop it takes about 670 nanoseconds regardless of the length of the array. If that's your bottleneck, you may need to switch languages... I'm pretty sure you won't find a faster way of reversing a numpy array. Good luck, at any rate! – Joe Kington Jul 21 '11 at 05:18
  • 670 nanosec per hit is about the same number I got. The total time for running the whole function is about 2~3 seconds, in which the reversion takes about 1/3, i.e., 1 second. Since I'm gonna run this function for millions of times, I regard this as a bottleneck. If this is indeed the best I can get, presumably I can only decide to live with it. Thanks! – nye17 Jul 21 '11 at 05:24
  • 6
    Well, do you necessarily have to run it inside a loop? In some cases, it's better to make a numpy array with millions of items and then operate on the entire array. Even if you're doing a finite difference method or something similar where the result depends on the previous result, you can sometimes do this. (Emphasis on sometimes...) At any rate, if speed is the primary goal, fortran is still king. `f2py` is your friend! It's often worthwhile to write performance critical parts of an algorithm (especially in scientific computing) in another language and call it from python. Good luck! – Joe Kington Jul 21 '11 at 05:54
  • I just learned about `numpy.flipud()`, not sure how it compares to the performance of `arr[::-1]` – berto Feb 22 '16 at 19:14
  • 2
    @berto. It is slower since it's a wrapper for `arr[::-1]`: https://github.com/numpy/numpy/blob/master/numpy/lib/twodim_base.py. Search for `def flipud`. The function is literally four lines long. – Mad Physicist Apr 01 '16 at 05:14
  • Thanks @MadPhysicist (by the way, on github, click the line number and then copy the link to get: https://github.com/numpy/numpy/blob/master/numpy/lib/twodim_base.py#L85) – berto Apr 04 '16 at 11:35
  • @berto. That is not a good idea if you expect your link to be up for more than a couple of days. Line numbers in the master branch change *very* frequently. – Mad Physicist Apr 04 '16 at 14:53

8 Answers8

318
reversed_arr = arr[::-1]

gives a reversed view into the original array arr. Any changes made to the original array arr will also be immediately visible in reversed_arr. The underlying data buffers for arr and reversed_arr are shared, so creating this view is always instantaneous, and does not require any additional memory allocation or copying for the array contents.

See also, this discussion on NumPy views: How do I create a view onto a NumPy array?


Possible solutions to performance problems regarding views

Are you re-creating the view more often than you need to? You should be able to do something like this:

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

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

I'm not a numpy expert, but this seems like it would be the fastest way to do things in numpy. If this is what you are already doing, I don't think you can improve on it.

Mateen Ulhaq
  • 24,552
  • 19
  • 101
  • 135
steveha
  • 74,789
  • 21
  • 92
  • 117
  • Does it help to create a slice object and then reuse it on many arrays? – endolith May 16 '14 at 14:47
  • 1
    Actually I just tested it and don't see any difference with the slice object created outside of the loop. (Oh wait, it's very slightly faster. Repeatably 43.4 ms vs 44.3 ms for a 1000000 loop) – endolith May 16 '14 at 15:02
  • 1
    What is `look_at` function suppose to do? – mrgloom Mar 12 '19 at 18:13
  • 1
    @mrgloom It is supposed to represent any task that looks at the data. The point of the example was to show that the view `reversed_arr` is still usable after the underlying data was changed. Writing new values into the array does not invalidate the view. Actually you could also use the view for writing new values into the array. `reversed_arr[0] = 99` would set the last element in the array to 99, the same as `arr[-1] = 99` would. – steveha Mar 20 '19 at 08:33
  • Interestingly, though, using a view seems to cause scipy's `lombscargle` function to fail in some previous (but recent, in my case 1.8.1) versions. E.g., `pgram = signal.lombscargle(x[::-1], y, w)` gives an error: `TypeError: Invalid call to pythranized function \`_lombscargle(float64[:] (is a view), float64[:], float64[:])'`. This is a bug that should be solved in newer versions, though: [Github issue](https://github.com/scipy/scipy/issues/15964). – Lu Kas Aug 01 '23 at 09:35
88
a[::-1]

only creates a view, so it's a constant-time operation (and as such doesn't take longer as the array grows). If you need the array to be contiguous (for example because you're performing many vector operations with it), ascontiguousarray is about as fast as flipud/fliplr:

enter image description here


Code to generate the plot:

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)",
)
Mateen Ulhaq
  • 24,552
  • 19
  • 101
  • 135
Nico Schlömer
  • 53,797
  • 27
  • 201
  • 249
  • 1
    perfplot requires at least Python 3.6 because it uses f-strings (Literal String Interpolation) – fivef May 13 '20 at 08:52
49

Because this seems to not be marked as answered yet... The Answer of Thomas Arildsen should be the proper one: just use

np.flipud(your_array) 

if it is a 1d array (column array).

With matrizes do

fliplr(matrix)

if you want to reverse rows and flipud(matrix) if you want to flip columns. No need for making your 1d column array a 2dimensional row array (matrix with one None layer) and then flipping it.

Hobo
  • 7,536
  • 5
  • 40
  • 50
zauberfein
  • 491
  • 4
  • 5
41

np.fliplr() flips the array left to right.

Note that for 1d arrays, you need to trick it a bit:

arr1d = np.array(some_sequence)
reversed_arr = np.fliplr([arr1d])[0]
user
  • 5,370
  • 8
  • 47
  • 75
tooty44
  • 6,829
  • 9
  • 27
  • 39
  • 37
    `reversed_arr = np.flipud(arr1d)` seems to work directly. – Thomas Arildsen Jul 03 '15 at 11:43
  • 2
    A 1D array is deemed one column, not one row, therefore [np.flipud](https://numpy.org/doc/stable/reference/generated/numpy.flipud.html) (up-down) is to be used, not [fliplr](https://numpy.org/doc/stable/reference/generated/numpy.fliplr.html#numpy.fliplr) (left-right). – mins Apr 26 '21 at 09:07
4

I will expand on the earlier answer about np.fliplr(). Here is some code that demonstrates constructing a 1d array, transforming it into a 2d array, flipping it, then converting back into a 1d array. time.clock() will be used to keep time, which is presented in terms of seconds.

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

With print statement uncommented:

[2 1 0]
0.00203907123594

With print statement commented out:

5.59799927506e-05

So, in terms of efficiency, I think that's decent. For those of you that love to do it in one line, here is that form.

np.fliplr(np.atleast_2d(np.array(range(3))))[0]
user
  • 5,370
  • 8
  • 47
  • 75
  • 6
    Timing something with such a small array is pretty useless. If you want to compare things it'd be better to use something that takes a while, like 3000 or maybe even more elements. – Bas May 13 '16 at 15:09
2

The slice notation based analog to np.flip would be [::-1,::-1]

a = np.array([[1., 2.], [3., 4.], [5, 6]])
print(a)

out: [[1. 2.]
      [3. 4.]
      [5. 6.]]

b=a[::-1,::-1]
print(b)

out: [[1. 2.]
      [3. 4.]
      [5. 6.]]
Victoria
  • 247
  • 4
  • 8
1

Expanding on what others have said I will give a short example.

If you have a 1D array ...

>>> 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])

But if you are working with a 2D array ...

>>> 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]])

This does not actually reverse the Matrix.

Should use np.flip to actually reverse the elements

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

If you want to print the elements of a matrix one-by-one use flat along with flip

>>> for el in np.flip(x).flat:
>>>     print(el, end = ' ')
9 8 7 6 5 4 3 2 1 0
John Mil.
  • 11
  • 1
  • I think you could also use x [...,::-1] to create a reversed view of a multidimensional array without resorting to np.flip, as demonstrated here https://stackoverflow.com/questions/7416170/numpy-reverse-multidimensional-array – phntm Oct 17 '20 at 06:05
0

In order to have it working with negative numbers and a long list you can do the following:

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

Where flipud is for 1d arra

Farvardin
  • 5,336
  • 5
  • 33
  • 54