Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does this code snippet rotating a matrix work?

Tags:

python

While looking for a pythonic way to rotate a matrix, I came across this answer. However there is no explanation attached to it. I copied the snippet here:

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

How does it work?

like image 950
Tamás Szelei Avatar asked Jun 18 '13 08:06

Tamás Szelei


Video Answer


3 Answers

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

[::-1] reverses the list :

>>> rev = lis[::-1]
>>> rev
[[7, 8, 9], [4, 5, 6], [1, 2, 3]]

now we use zip on all items of the rev, and append each returned tuple to rotated:

>>> rotated = []
>>> for item in zip(rev[0],rev[1],rev[2]):
...     rotated.append(item)
...     
>>> rotated
[(7, 4, 1), (8, 5, 2), (9, 6, 3)]

zip picks items from the same index from each of the iterable passed to it(it runs only up to the item with minimum length) and returns them as a tuple.

what is *:

* is used for unpacking all the items of rev to zip, so instead of manually typing rev[0], rev[1], rev[2], we can simply do zip(*rev).

The above zip loop could also be written as:

>>> rev = [[7, 8, 9], [4, 5, 6], [1, 2, 3]]
>>> min_length = min(len(x) for x in rev)  # find the min length among all items
>>> rotated = []

for i in xrange(min_length):        
    items = tuple(x[i] for x in rev)  # collect items on the same index from each
                                      # list inside `rev`  
    rotated.append(items)
...     
>>> rotated
[(7, 4, 1), (8, 5, 2), (9, 6, 3)]
like image 166
Ashwini Chaudhary Avatar answered Oct 26 '22 06:10

Ashwini Chaudhary


Complementary to the explanations by Ashwini and HennyH, here's a little figure to illustrate the process.

enter image description here

  1. First, the [::-1] slice operator reverses the list of list, taking the entire list (thus the first two arguments can be omitted) and using a step of -1.
  2. Second, the zip function takes a number of lists and effectively returns a new list with rows and columns reversed. The * says that the list of lists is unpacked into several lists.

As can be seen, these two operations combined will rotate the matrix.

like image 24
tobias_k Avatar answered Oct 26 '22 06:10

tobias_k


For my explination:

>>> m = [['a','b','c'],[1,2,3]]

Which when pretty-printed would be:

>>> pprint(m)
['a', 'b', 'c']
[1, 2, 3]

Firstly, zip(*m) will create a list of all the columns in m. As demonstrated by:

>>> zip(*m)
[('a', 1), ('b', 2), ('c', 3)]

The way this works is zip takes n sequences and get's the i-th element of each one and adds it to a tuple. So translated to our matrix m where each row is represented by a list contained within m, we essentially pass in each row to zip, which then gets the 1st element from each row puts all of them into a tuple, then gets every 2nd element from each row etc... Ultimately getting every column in m i.e:

>>> zip(['row1column1','row1column2'],['row2column1','row2column2'])
[('row1column1', 'row2column1'), ('row1column2', 'row2column2')]
Notice that each tuple contains all the elements in a specific column

Now that would look like:

>>> pprint(zip(*m))
('a', 1)
('b', 2)
('c', 3)

So effectively, each column in m is now a row. Hoever it isn't in the correct order (try imagine in your head rotating m to get the matrix above, it can't be done). This why it's necessary to 'flip' the original matrix:

>>> pprint(zip(*m[::-1]))
(1, 'a')
(2, 'b')
(3, 'c')

Which results in a matrix which is the equivalent of m rotated - 90 degrees.

like image 25
HennyH Avatar answered Oct 26 '22 07:10

HennyH