Rotating a two-dimensional array in Python

In the program I am writing, I need to rotate a two-dimensional array. In search of the best solution, I found this impressive single-liner that does the job:

rotated = zip(*original[::-1]) 

I use it in my program now, and it works as intended. My problem is that I do not understand how this works.

I would appreciate it if someone could explain how the various functions are involved in achieving the desired result.

+68
python multidimensional-array
Dec 07 '11 at 19:31
source share
8 answers

Consider the following two-dimensional list:

 original = [[1, 2], [3, 4]] 

Step-by-Step Partitioning:

 >>> original[::-1] # elements of original are reversed [[3, 4], [1, 2]] 

This list is passed to zip() by unpacking the arguments , so the zip call ends up equivalent to this:

 zip([3, 4], [1, 2]) # ^ ^----column 2 # |-------column 1 # returns [(3, 1), (4, 2)], which is a original rotated clockwise 

Hopefully the comments make it clear what zip does, it will group elements from each input iterable based on the index, or, in other words, group the columns.

+49
Dec 07 '11 at 19:43
source share
โ€” -

This is a smart bit. Here's a breakdown:

  • [::-1] - makes a shallow copy of the original list in reverse order. You can also use reversed() , which will lead to a reverse iterator over the list, rather than actually copying the list (a more efficient amount of memory).
  • * - makes each subscription in the source list a separate argument to zip() (i.e. unpacks the list)
  • zip() - transfers one element from each argument and displays a list (well, a tuple) of them and repeats until all the sublists have been exhausted. This is where the transfer actually takes place.

So, suppose you have this:

 [ [1, 2, 3], [4, 5, 6], [7, 8, 9] ] 

First you get this (shallow, reverse copy):

 [ [7, 8, 9], [4, 5, 6], [1, 2, 3] ] 

Then each of the subscriptions is passed as a zip argument:

 zip([7, 8, 9], [4, 5, 6], [1, 2, 3]) 

zip() repeatedly consumes one element from the beginning of each of its arguments and makes a tuple from it until there are more elements, resulting in:

 [(7, 4, 1), (8, 5, 2), (9, 6, 3)] 

And Bob is your uncle.

To answer the @IkeMiguel question in the comment about rotating it in the other direction, this is pretty simple: you just need to undo both sequences that go into the zip and the result. The first can be achieved by removing [::-1] , and the second can be achieved by throwing reversed() around everything. Since reversed() returns an iterator over the list, we will need to put list() around it to convert it. So:

 rotated = list(zip(*reversed(original))) 

Of course, you can also just rotate the list clockwise three times. :-)

+50
Dec 07 '11 at 19:36
source share

There are three parts:

  • original [:: - 1] modifies the original array. This designation is slicing a Python list. This gives you a "sublist" of the original list described in [start: end: step], start is the first item, end is the last item to be used in the sublist. The step says that each element of the step is from the first to the last. Missing start and end means that the slice will be a complete list, and a negative step means that you will get the elements in the reverse order. So, for example, if the original was [x, y, z], the result would be [z, y, x]
  • * When a list / tuple goes into the argument list of a function call, it means "expand" the list / tuple so that each of its elements becomes a separate function argument, and not the list / tuple itself. So, if, say, args = [1,2,3], then zip (args) is the same as zip ([1,2,3]), but zip (* args) is the same as zip (1, 2,3) .
  • zip is a function that takes n arguments, each of which is of length m and creates a list of length m, elements of which are of length n and contain the corresponding elements of each of the source lists. For example, zip ([1,2], [a, b], [x, y]) is [[1, a, x], [2, b, y]]]. See Also Python Documentation.
+13
Dec 07 '11 at 19:49
source share

I had this problem and I found a large wikipedia page on this subject (in the "General Turns" section:
https://en.wikipedia.org/wiki/Rotation_matrix#Ambiguities

Then I wrote the following code, super detailed, to have a clear idea of โ€‹โ€‹what was going on.

I hope that it will be useful for you to dig more in the very beautiful and smart single-liner that you placed.

To quickly test it, you can copy / paste it here:
http://www.codeskulptor.org/

 triangle = [[0,0],[5,0],[5,2]] coordinates_a = triangle[0] coordinates_b = triangle[1] coordinates_c = triangle[2] def rotate90ccw(coordinates): print "Start coordinates:" print coordinates old_x = coordinates[0] old_y = coordinates[1] # Here we apply the matrix coming from Wikipedia # for 90 ccw it looks like: # 0,-1 # 1,0 # What does this mean? # # Basically this is how the calculation of the new_x and new_y is happening: # new_x = (0)(old_x)+(-1)(old_y) # new_y = (1)(old_x)+(0)(old_y) # # If you check the lonely numbers between parenthesis the Wikipedia matrix numbers # finally start making sense. # All the rest is standard formula, the same behaviour will apply to other rotations, just # remember to use the other rotation matrix values available on Wiki for 180ccw and 170ccw new_x = -old_y new_y = old_x print "End coordinates:" print [new_x, new_y] def rotate180ccw(coordinates): print "Start coordinates:" print coordinates old_x = coordinates[0] old_y = coordinates[1] new_x = -old_x new_y = -old_y print "End coordinates:" print [new_x, new_y] def rotate270ccw(coordinates): print "Start coordinates:" print coordinates old_x = coordinates[0] old_y = coordinates[1] new_x = -old_x new_y = -old_y print "End coordinates:" print [new_x, new_y] print "Let rotate point A 90 degrees ccw:" rotate90ccw(coordinates_a) print "Let rotate point B 90 degrees ccw:" rotate90ccw(coordinates_b) print "Let rotate point C 90 degrees ccw:" rotate90ccw(coordinates_c) print "=== === === === === === === === === " print "Let rotate point A 180 degrees ccw:" rotate180ccw(coordinates_a) print "Let rotate point B 180 degrees ccw:" rotate180ccw(coordinates_b) print "Let rotate point C 180 degrees ccw:" rotate180ccw(coordinates_c) print "=== === === === === === === === === " print "Let rotate point A 270 degrees ccw:" rotate270ccw(coordinates_a) print "Let rotate point B 270 degrees ccw:" rotate270ccw(coordinates_b) print "Let rotate point C 270 degrees ccw:" rotate270ccw(coordinates_c) print "=== === === === === === === === === " 
+1
Mar 14 '17 at 14:47
source share

Just an observation. The input is a list of lists, but the conclusion is from a very nice solution: rotated = zip (* original [:: - 1]) returns a list of tuples.

This may or may not be a problem.

This, however, is easily fixed:

 original = [[1, 2, 3], [4, 5, 6], [7, 8, 9] ] def rotated(array_2d): list_of_tuples = zip(*array_2d[::-1]) return [list(elem) for elem in list_of_tuples] # return map(list, list_of_tuples) print(list(rotated(original))) # [[7, 4, 1], [8, 5, 2], [9, 6, 3]] 

A comp list or map will convert internal tuples back to lists.

+1
Jan 25 '18 at 14:16
source share
 def ruota_antiorario(matrix): ruota=list(zip(*reversed(matrix))) return[list(elemento) for elemento in ruota] def ruota_orario(matrix): ruota=list(zip(*reversed(matrix))) return[list(elemento)[::-1] for elemento in ruota][::-1] 
0
Feb 23 '18 at 14:47
source share
 import copy def rotate(matrix): data = copy.deepcopy(matrix) for i in range(len(data)): for j in range(i+1,len(data[i])): temp=data[i][j] data[i][j]=data[j][i] data[j][i]=temp return data 
-one
Sep 08 '16 at 14:06
source share

I was implementing python nested lists and coded the following to return a list of columns in a matrix that is similar to matrix rotation:

 def getColumns(matrix): columns = [[row[col] for row in matrix] for col in range(len(matrix[1]))] return columns 
-2
Jun 10 '13 at 4:28
source share



All Articles