You can use NumPy
module that's good with arrays and matrices. It has a built-in for exactly that purpose -
import numpy as np
np.rot90(image).tolist()
With array manipulations, that's essentially same as performing matrix/array transpose and then flipping the rows -
np.asarray(image).T[::-1].tolist()
If the input is already an array, we can skip the array-conversion
. Also, if the output as an array is okay, it would be simply a view into the input and as such the entire operation would be virtually-free
.
Thus, with image_arr
as the input array, it would be -
np.rot90(image_arr)
With transpose and flipping rows -
image_arr.T[::-1]
Let's take the provided sample and check out outputs on an IPython console -
In [48]: image
Out[48]:
[[1, 1, 5, 9, 9],
[2, 2, 6, 0, 0],
[3, 3, 7, 1, 1],
[4, 4, 8, 2, 2],
[5, 5, 9, 3, 3]]
In [50]: np.asarray(image).T[::-1].tolist()
Out[50]:
[[9, 0, 1, 2, 3],
[9, 0, 1, 2, 3],
[5, 6, 7, 8, 9],
[1, 2, 3, 4, 5],
[1, 2, 3, 4, 5]]
Timings on a large 5000 x 5000
sized image
1) Image
as a list
:
In [53]: image = np.random.randint(0,256,(5000,5000)).tolist()
# @Dima Tisnek's soln
In [54]: %timeit list(reversed(list(zip(*image))))
1 loop, best of 3: 1.09 s per loop
In [55]: %timeit np.array(image).T[::-1].tolist()
1 loop, best of 3: 1.06 s per loop
Time-complexity
There's no time-complexity involved here (not on computation anyway) and the entire play is about array and list conversion, as shown below when we break down the steps -
In [72]: image_arr = np.array(image)
In [71]: %timeit np.array(image) # convert to array
1 loop, best of 3: 771 ms per loop
In [73]: %timeit image_arr.T[::-1] # perform 90deg rotation
1000000 loops, best of 3: 372 ns per loop
In [74]: %timeit image_arr.T[::-1].tolist() # convert back to list
1 loop, best of 3: 296 ms per loop
2) Image
and output as arrays
:
In [56]: image = np.random.randint(0,256,(5000,5000))
# @Dima Tisnek's soln
In [57]: %timeit list(reversed(list(zip(*image))))
1 loop, best of 3: 1.34 s per loop
In [58]: %timeit image.T[::-1]
1000000 loops, best of 3: 363 ns per loop
In [59]: %timeit np.rot90(image)
100000 loops, best of 3: 9.05 µs per loop
The last two NumPy based ones are virtually free as discussed earlier. This is because internally image.T[::-1]
is same as input image
, but with different stride pattern representation. Let's verify that they are same by checking their memory occupancy -
In [60]: np.shares_memory(image, image.T[::-1])
Out[60]: True
Conversion to list on own-data for slight perf. boost
Closer inspection on list conversion reveals that converting to list when the strided pattern isn't regular (row-order) might not be the most optimal scenario. So, one way would be create a copy of array data once we have the rotated one and then convert. This seems to give around 10%
improvement -
In [2]: image = np.random.randint(0,256,(5000,5000)).tolist()
In [8]: %timeit list(reversed(list(zip(*image))))
1 loop, best of 3: 1.12 s per loop
In [9]: %timeit np.asarray(image).T[::-1].tolist()
1 loop, best of 3: 1.11 s per loop
# Have own-data (copy) and then convert to list
In [10]: %timeit np.asarray(image).T[::-1].copy().tolist()
1 loop, best of 3: 1.01 s per loop
O(N^2)
wrt.N
being the number of pixels or side of the squre? \$\endgroup\$.__getitem__
for a rotation inO(1)
? \$\endgroup\$m = [[1, 2], [3, 4]]; m[1][0], m[0][0], m[1][1], m[0][1] = m[0][0], m[0][1], m[1][0], m[1][1]; m
\$\endgroup\$