Consider the following 3 x 4 matrix implemented as a list of 3 lists of length 4 in Python:
>>> matrix = [[1, 2, 3, 4],[5, 6, 7, 8],[9, 10, 11, 12]]
The following list comprehension will rearrange the matrix transposing rows and columns:
>>> [[row[i] for row in matrix] for i in range(4)]
[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]
BUT suppose I need this as result:
[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]
that is a 4 x 3 matrix rearranged so to obtain a sequential "scrolling" of the original matrix that "breaks" to a new row every 3 elements.
I know it is possible to draw out an algorithm to achieve the task but would it be possible to obtain that using only list comprehensions? (And, if yes, how?)
EDIT:
The accepted answer shall meet the following requirements:
2nd EDIT + accepted answer motivation:
Here's what I did when I had to find a solution for this (based upon the suggestions I gave in my own comments below):
mat = [[1,2,3,4],[5,6,7,8],[9,10,11,12]]
[[mat[(3*(i-1)+j -1)//4+1][(3*(i-1)+j -1)%4] for j in range(3)] for i in range(4)]
NOTE that the solution I wrote is specific to this case but, as Clodion noticed too, the "formula" can be "generalized" so to re-arrange the initial (list of lists) matrix to different "shapes".
How about something like this:
>>> matrix = [[1, 2, 3, 4],[5, 6, 7, 8],[9, 10, 11, 12]]
>>> it = (y for x in matrix for y in x)
>>> list(zip(*[it]*3))
[(1, 2, 3), (4, 5, 6), (7, 8, 9), (10, 11, 12)]
If you want list of lists:
>>> matrix = [[1, 2, 3, 4],[5, 6, 7, 8],[9, 10, 11, 12]]
>>> it = (y for x in matrix for y in x)
>>> list(map(list, zip(*[it]*3)))
[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]
Explanation:
First we create a generator that gives the elements of the list (as if it were flattened to a single list - that is 1,2,3,4,5,6,...,12) - we store that in it
.
Then we call zip()
giving it it
three times; since the generator is the same, it provides a next element in it
every time.
FURTHER NOTE: you can even re-arrange the initial matrix in different "shapes" than the 4 x 3, if you want, by simply changing the 3
to the desired number of columns. (For example, change it to 2
or to 6
and you'll obtain, respectively, a 6 x 2 or a 2 x 6 re-arranged matrix instead).
A method without using zip
, and only using list comprehension, but it requires two lines -
>>> matrix = [[1, 2, 3, 4],[5, 6, 7, 8],[9, 10, 11, 12]]
>>> it = (y for x in matrix for y in x)
>>> [[next(it) for _ in range(3)] for _ in range(4)]
[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With