How to I invert a 2d list in python [duplicate]
Asked Answered
T

3

11

I have a 2d list like this:

   1   2   3

   4   5   6

and I want to make this:

   1   4

   2   5

   3   6

I've tried to do a for loop and switch each value but I keep getting an index out of bound error. Here's what I have:

for i in results:
    for j in range(numCenturies):
        rotated[i][j] = results [j][i]
Tael answered 29/11, 2013 at 5:44 Comment(1)
This is called "transposing", not "inverting". Inverting a list just means turning it upside down, not rotating it by 90 degrees.Fulk
D
15

From python documentation on zip function:

This function returns a list of tuples, where the i-th tuple contains the i-th element from each of the argument sequences or iterables. The returned list is truncated in length to the length of the shortest argument sequence. When there are multiple arguments which are all of the same length, zip() is similar to map() with an initial argument of None. With a single sequence argument, it returns a list of 1-tuples. With no arguments, it returns an empty list.

Example:

zip([1, 2, 3], [4, 5, 6]) # returns [(1, 4), (2, 5), (3, 6)]

If you need the result to be the list of lists, not the list of tuples, you can use list comprehension:

[list(x) for x  in zip([1, 2, 3], [4, 5, 6], [7, 8, 9])] # returns [[1, 4, 7], [2, 5, 8], [3, 6, 9]]

If all your variables are stored in one 2d list, and you want it pass it into zip function, you can use the following (I'll call it the star notation, because I can't remember the proper English term for it):

results = [[1, 2, 3], [4, 5, 6]]
zip(*results) # returns [(1, 4), (2, 5), (3, 6)]
Dimerous answered 29/11, 2013 at 5:46 Comment(5)
But what if there are more than two rows?Photogenic
@AswinMurugesh it works with any number of rows provided: zip([1,2,3],[4,5,6],[7,8,9]) returns [(1, 4, 7), (2, 5, 8), (3, 6, 9)]Dimerous
is there a way I could use this if everything was stored in a variable? For example, if I had a 2d list called results, and I wasn't sure how big it was, how do I pass it to the zip function?Tael
@RDoolabh I've updated my answer with the information you're looking for.Dimerous
The proper English term for it is Unpacking Argument Lists. (The documentation carefully avoids ever giving a name to the actual operator, always using "the * operator", I believe partly because everyone calls it the "splat" operator, and nobody wants to enshrine that in the docs, and partly to avoid having to explain how * is a different operator in different contexts—which it really isn't, but until you're ready to explain dunder methods that's a hard point to make.)Fulk
P
6

http://docs.scipy.org/doc/numpy/reference/generated/numpy.transpose.html

>>> from numpy import transpose
>>> transpose([[1,2,3],[4,5,6]])
array([[1, 4],
       [2, 5],
       [3, 6]])
Precede answered 29/11, 2013 at 5:48 Comment(0)
F
3

zip is the right way to do this, as shown by aga.

But if you want to know why your original code wasn't working:

for i in results:
    for j in range(numCenturies):
        rotated[i][j] = results [j][i]

There are two clear problems here, and likely two others. (Since you didn't show us enough of the code or data to be sure, I can't guarantee the two likely ones.)

Presumably results looks something like this:

results = [[1, 2, 3], [4, 5, 6]]

When you do for i in results, that means i will be each element in results—that is, it will be [1, 2, 3], and then [4, 5, 6]. You can't use a list as an index into a list, so this is guaranteed to give you a TypeError: list indices must be integers, not list.

To fix this, you need:

for i in range(len(results)):

… or …

for i, row in enumerate(results):

Next, results[j][i] is guaranteed to raise IndexError: list index out of range, because i is each row number, but you're trying to use it as a column number. If you're iterating over the rows and columns of results, you want this:

rotated[j][i] = results[i][j]

Next, unless you pre-filled rotated with 3 lists, each of which was pre-filled with 2 objects of some kind, you're going to get an IndexError: list assignment index out of range.

To fix this, you need to pre-fill rotated, something like this:

rotated = [[None for j in range(2)] for i in range(3)]

… or …

rotated = [[None, None], [None, None], [None, None]]

Finally, I'll bet numCenturies is 3, in which case you'll get another IndexError: list index out of range as soon as j reaches 2. The simplest thing to do here is to just use the length of the row; there's no chance of an off-by-one error that way.

Putting it all together:

rotated = [[None for j in range(2)] for i in range(3)]
for i, row in enumerate(results):
    for j, value in enumerate(row):
        rotated[j][i] = value

But in general, Python gives you easier ways to do things than pre-creating arrays and looping over indices to fill in the values. You can use append—or, better, a list comprehension. Or, even better, find a higher-level way to write your use, like a single call to zip.

Fulk answered 29/11, 2013 at 7:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.