3

so I have this list in python,

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

and want to turn the list reading horizontally to vertically.

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

what is the best way to do it, and most efficient way to do it? i'm pretty new to programming, sorry for being noob. thanks.

Matthew Trevor
  • 14,354
  • 6
  • 37
  • 50
tipsywacky
  • 3,374
  • 5
  • 43
  • 75
  • see also http://stackoverflow.com/questions/10169919/python-matrix-transpose-and-zip, http://stackoverflow.com/questions/2921681/fast-matrix-transposition-in-python – georg Sep 22 '12 at 06:30

4 Answers4

10

You can do it like that:

zip(*your_list)

Proof:

>>> a = [[1, 2, 3, 4], [2, 4, 5, 1], [3, 4, 6, 2], [2, 3, 4, 5]]
>>> zip(*a)
[(1, 2, 3, 2), (2, 4, 4, 3), (3, 5, 6, 4), (4, 1, 2, 5)]
Tadeck
  • 132,510
  • 28
  • 152
  • 198
  • 1
    If you need `list`s (you have list of tuples), you can get them like that: `map(list, a)` (where `a` is already transposed). – Tadeck Sep 22 '12 at 05:51
  • That's a good one. Spend a couple of minutes to understand why `zip(*lst)` transposes `lst`. :) – aga Sep 22 '12 at 05:54
  • Your proof shows us that it is correct, but not that it is *efficient*. – jmilloy Sep 22 '12 at 06:32
  • 1
    To make this answer more useful (and to justify too easy earned upvotes:) could you provide an explanation of how exactly this idiom works? There are 100+ answers here mentioning `zip(*x)`, but nobody has explained it yet. – georg Sep 22 '12 at 06:36
  • Also note that this won't work if the sublists are of different lengths. In this case, use `map(None, a)` – jmilloy Sep 22 '12 at 06:56
  • @thg435: aga (below) provided pretty good explanation. First x is unpacked (so every nested list is kind of different positional argument in the function), then they are "zipped" (see docs for zip() for details). Result is as expected, and zip(*x) is widely used idiom for transposing. – Tadeck Sep 22 '12 at 08:27
  • @jmilloy: That is right, I did not prove it is efficient, but based on the fact it is widely used (maybe not exactly widely known), I assume this is also done with performance in mind. zip(*x) transposes list without making a copy of it or its elements, because lists are mutable (and zip()'s unpacked arguments are still the elements of the original list). It then returns list of lists (or rather list of tuples), which are new objects (they must be). If you have performance in mind, then you should give up lists and allow iterators eg. (see itertools.izip() for one of the example replacements). – Tadeck Sep 22 '12 at 08:32
8

Check out numpy library. You can put your list into an array and transpose it like this:

a = array ([[1,2,3,4],
       [2,4,5,1],
       [3,4,6,2],
       [2,3,4,5]])
a.transpose()

P.S.: Explanation of Tadeck's solution is very easy. zip has the following signature:

zip(seq1 [, seq2 [...]]) -> [(seq1[0], seq2[0] ...), (...)]

So, it takes a number of sequences (we don't know how much exactly) and then builds tuples in the following order: takes first element of every sequence ant puts them in the tuple, then takes second element of every sequence and puts them in the second tuple and so on. It returns list of all tuples it build during its execution.

*lst - is, in fact, unpacking of arguments list. You can read more about it in the following note.

I hope, now everyone understands how this pretty piece of code works. :)

Glorfindel
  • 21,988
  • 13
  • 81
  • 109
aga
  • 27,954
  • 13
  • 86
  • 121
5

You asked about efficiency. You can use timeit for that.

>python -m timeit -s "a = [[1,2,3,4],[2,4,5,1],[3,4,6,2],[2,3,4,5]]" "zip(*a)"
1000000 loops, best of 3: 0.569 usec per loop

>python -m timeit -s "a = [[1,2,3,4],[2,4,5,1],[3,4,6,2],[2,3,4,5]]" "map(None, *a)"
1000000 loops, best of 3: 0.644 usec per loop    

>python -m timeit -s "a = [[1,2,3,4],[2,4,5,1],[3,4,6,2],[2,3,4,5]]" "[[row[i] for row in a] for i in xrange(len(a[0]))]"
1000000 loops, best of 3: 1.43 usec per loop    

>python -m timeit -s "from numpy import array; a = array([[1,2,3,4],[2,4,5,1],[3,4,6,2],[2,3,4,5]])" "a.transpose()"
1000000 loops, best of 3: 0.249 usec per loop

For a large data set of [[1,2,3,4],[2,4,5,1],[3,4,6,2],[2,3,4,5]]*1000000

>python -m timeit -s "a = [[1,2,3,4],[2,4,5,1],[3,4,6,2],[2,3,4,5]]*1000000" "zip(*a)"
10 loops, best of 3: 400 msec per loop

>python -m timeit -s "a = [[1,2,3,4],[2,4,5,1],[3,4,6,2],[2,3,4,5]]*1000000" "map(None, *a)"
10 loops, best of 3: 458 msec per loop

>python -m timeit -s "a = [[1,2,3,4],[2,4,5,1],[3,4,6,2],[2,3,4,5]]*1000000" "[[row[i] for row in a] for i in xrange(len(a[0]))]"
10 loops, best of 3: 770 msec per loop

>python -m timeit -s "from numpy import array; a = array([[1,2,3,4],[2,4,5,1],[3,4,6,2],[2,3,4,5]]*1000000)" "a.transpose()"
1000000 loops, best of 3: 0.251 usec per loop

If your lists are of different lengths, zip truncates to the shortest length. You can use 'map' or itertools.izip_longest to instead fill the missing values with None.

jmilloy
  • 7,875
  • 11
  • 53
  • 86
  • Efficiency doesn't necessarily mean the *fastest* solution, usually it refers to the algorithm. – jamylak Sep 22 '12 at 10:36
  • @jamylak: Right. Also I believe import statements should be part of setup in timeit. Otherwise the test would not be fair (unless any subsequent import will actually use previously imported modules, which I doubt). – Tadeck Sep 22 '12 at 10:41
  • @Tadeck Correct, this needs to be fixed by adding `-s` for the setups. I'll post my results in the meantime. – jamylak Sep 22 '12 at 10:45
  • Also fixed a couple of timings, turns out numpy is way faster. – jamylak Sep 22 '12 at 10:59
  • Thanks guys! Among other things, very lazy of me to include the import in the numpy. As far as efficiency vs fastest, I would agree if this were not a programming language specific question. – jmilloy Sep 22 '12 at 13:41
  • @jamylak Probably not necessary, but can you add the itertools.izip_longest times? – jmilloy Sep 22 '12 at 13:43
1

Another way is:

a=  [[1,2,3,4],
     [2,4,5,1],
     [3,4,6,2],
     [2,3,4,5]]
a = [[row[i] for row in a] for i in range(len(a[0]))]
Sufian Latif
  • 13,086
  • 3
  • 33
  • 70
  • 3
    `[[row[i] for row in a] for i in range(len(a[i]))]` is not only incorrect (due to `NameError`, but also is probably meant to be nothing more than `[[item for item in sublist] for sublist in a]`. – Tadeck Sep 22 '12 at 06:20
  • i actually like this answer the most – tipsywacky Sep 22 '12 at 09:19
  • @tipsywacky In particular, it's a great one someone who is learning python. – jmilloy Sep 22 '12 at 13:48