Traspone elenco di elenchi


241

Prendiamo:

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

Il risultato che sto cercando è

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

e non

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

Molto apprezzato

Risposte:


337

Che ne dite di

map(list, zip(*l))
--> [[1, 4, 7], [2, 5, 8], [3, 6, 9]]

Per gli utenti di Python 3.x possono usare

list(map(list, zip(*l)))

Spiegazione:

Ci sono due cose che dobbiamo sapere per capire cosa sta succedendo:

  1. La firma di zip : zip(*iterables)questo significa che si zipaspetta un numero arbitrario di argomenti ciascuno dei quali deve essere iterabile. Es zip([1, 2], [3, 4], [5, 6]).
  2. Elenchi di argomenti non compressi : data una sequenza di argomenti args, f(*args)chiamerà in modo ftale che ogni elemento in argssia un argomento posizionale separato di f.

Tornando all'ingresso dalla questione l = [[1, 2, 3], [4, 5, 6], [7, 8, 9]], zip(*l)sarebbe equivalente a zip([1, 2, 3], [4, 5, 6], [7, 8, 9]). Il resto è solo assicurarsi che il risultato sia un elenco di elenchi anziché un elenco di tuple.


67
Attenzione: se lnon è uniformemente dimensioni (ad esempio, alcune righe sono brevi rispetto ad altri), zipsarà non compensare e invece tagliare via righe dall'output. Quindi l=[[1,2],[3,4],[5]]ti dà [[1,3,5]].
badp,

29
La itertoolsfunzione zip_longest()funziona con elenchi irregolari. Vedi DOCS
Origano

13
Una spiegazione in risposta sarebbe buona :)
Boris Churzin

7
Penso che funzioni anche list(zip(*l))correttamente in Python 3.
Stefano,

3
@Stefano Funziona (come zip(*l)in Python 2), ma ottieni un elenco di tuple, non un elenco di elenchi. Certo, list(list(it))è sempre la stessa cosa di list(it).
Alex Shpilkin,

62

Un modo per farlo è con la trasposizione NumPy. Per un elenco, a:

>>> import numpy as np
>>> np.array(a).T.tolist()
[[1, 4, 7], [2, 5, 8], [3, 6, 9]]

O un altro senza zip:

>>> map(list,map(None,*a))
[[1, 4, 7], [2, 5, 8], [3, 6, 9]]

8
Adoro il tuo secondo - non mi ero reso conto che mappotesse farlo. Ecco un leggero perfezionamento che non richiede 2 chiamate, però:map(lambda *a: list(a), *l)
Lee D

7
Non dovrebbe essere una risposta migliore in quanto si occupa di elenchi irregolari?
Leon,

15
map(None, ...)non sembra funzionare per Py3. Il generatore è stato creato, ma next()genera immediatamente un errore: TypeError: 'NoneType' object is not callable.
Fisico pazzo,

57

Equivalente alla soluzione di Jena:

>>> l=[[1,2,3],[4,5,6],[7,8,9]]
>>> [list(i) for i in zip(*l)]
... [[1, 4, 7], [2, 5, 8], [3, 6, 9]]

12
Poiché la comprensione dell'elenco è ora preferita a map(), questa soluzione è quella che è la più nello spirito di Python ...
perror

26

solo per divertimento, rettangoli validi e supponendo che m [0] esista

>>> m = [[1,2,3],[4,5,6],[7,8,9]]
>>> [[row[i] for row in m] for i in range(len(m[0]))]
[[1, 4, 7], [2, 5, 8], [3, 6, 9]]

questo è quello che cercavo e non riuscivo a farmi girare la testa. La soluzione di Still @ jena è davvero breve
titus

3
Sì, ci sono volute alcune gomme per farlo bene. Va bene, molti tentativi.
matchew

9
Non è ancora del tutto corretto - questo succede solo quando le dimensioni sono quadrate! Dovrebbe essere: [[j[i] for j in l] for i in range(len(l[0]))]. Certo, devi essere sicuro che l'elenco lnon sia vuoto.
Lee D,

@LeeD non funziona ancora per me sull'esempio di jena l = [[1,2], [3,4], [5]]
piani di cottura

3
@hobs Questo è stato l'esempio di badp, rispondendo a Jena. Tuttavia, non sono sicuro che abbia senso per me. IMO, la trasposizione implica una matrice rettangolare - quando rappresentata come un elenco di elenchi, ciò significa che tutti gli elenchi interni devono avere la stessa lunghezza. Quale risultato vorresti come "trasposizione" di questo esempio?
Lee D,

22

I metodi 1 e 2 funzionano in Python 2 o 3 e funzionano su elenchi 2D irregolari e rettangolari . Ciò significa che le liste interne non devono avere le stesse lunghezze tra loro (sfilacciate) o delle liste esterne (rettangolari). Gli altri metodi, beh, sono complicati.

il set up

import itertools
import six

list_list = [[1,2,3], [4,5,6, 6.1, 6.2, 6.3], [7,8,9]]

metodo 1 - map(),zip_longest()

>>> list(map(list, six.moves.zip_longest(*list_list, fillvalue='-')))
[[1, 4, 7], [2, 5, 8], [3, 6, 9], ['-', 6.1, '-'], ['-', 6.2, '-'], ['-', 6.3, '-']]

six.moves.zip_longest() diventa

Il valore di riempimento predefinito è None. Grazie alla risposta di @jena , dove map()sta cambiando le tuple interne in liste. Qui sta trasformando gli iteratori in liste. Grazie ai commenti di @ Oregano e @ badp .

In Python 3, passa il risultato attraverso list()per ottenere lo stesso elenco 2D del metodo 2.


metodo 2 - comprensione dell'elenco, zip_longest()

>>> [list(row) for row in six.moves.zip_longest(*list_list, fillvalue='-')]
[[1, 4, 7], [2, 5, 8], [3, 6, 9], ['-', 6.1, '-'], ['-', 6.2, '-'], ['-', 6.3, '-']]

L' alternativa @ inspectorG4dget .


metodo 3 - map()of map()- broken in Python 3.6

>>> map(list, map(None, *list_list))
[[1, 4, 7], [2, 5, 8], [3, 6, 9], [None, 6.1, None], [None, 6.2, None], [None, 6.3, None]]

Questa seconda alternativa straordinariamente compatta di @SiggyF funziona con elenchi 2D irregolari, a differenza del suo primo codice che usa numpy per trasporre e passare attraverso elenchi irregolari. Ma None deve essere il valore di riempimento. (No, Nessuno passato alla mappa interna () non è il valore di riempimento. Significa che non esiste alcuna funzione per elaborare ciascuna colonna. Le colonne vengono semplicemente passate alla mappa esterna () che le converte da tuple in elenchi.

Da qualche parte in Python 3, ha map()smesso di sopportare tutto questo abuso: il primo parametro non può essere Nessuno e gli iteratori irregolari sono solo troncati al più breve. Gli altri metodi funzionano ancora perché questo si applica solo alla mappa interna ().


metodo 4 - map()di map()rivisitato

>>> list(map(list, map(lambda *args: args, *list_list)))
[[1, 4, 7], [2, 5, 8], [3, 6, 9]]   // Python 2.7
[[1, 4, 7], [2, 5, 8], [3, 6, 9], [None, 6.1, None], [None, 6.2, None], [None, 6.3, None]] // 3.6+

Purtroppo le righe irregolari NON diventano colonne irregolari in Python 3, sono solo troncate. Boo hoo progress.


7

Tre opzioni tra cui scegliere:

1. Mappa con zip

solution1 = map(list, zip(*l))

2. Elenco comprensione

solution2 = [list(i) for i in zip(*l)]

3. Per l'aggiunta di loop

solution3 = []
for i in zip(*l):
    solution3.append((list(i)))

E per visualizzare i risultati:

print(*solution1)
print(*solution2)
print(*solution3)

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

0

Forse non è la soluzione più elegante, ma ecco una soluzione che utilizza i cicli while annidati:

def transpose(lst):
    newlist = []
    i = 0
    while i < len(lst):
        j = 0
        colvec = []
        while j < len(lst):
            colvec.append(lst[j][i])
            j = j + 1
        newlist.append(colvec)
        i = i + 1
    return newlist


0

more_itertools.unzip() è facile da leggere e funziona anche con generatori.

import more_itertools
l = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
r = more_itertools.unzip(l) # a tuple of generators.
r = list(map(list, r))      # a list of lists

o equivalentemente

import more_itertools
l = more_itertools.chunked(range(1,10), 3)
r = more_itertools.unzip(l) # a tuple of generators.
r = list(map(list, r))      # a list of lists

-1

Ecco una soluzione per trasporre un elenco di elenchi che non è necessariamente quadrato:

maxCol = len(l[0])
for row in l:
    rowLength = len(row)
    if rowLength > maxCol:
        maxCol = rowLength
lTrans = []
for colIndex in range(maxCol):
    lTrans.append([])
    for row in l:
        if colIndex < len(row):
            lTrans[colIndex].append(row[colIndex])

-2
    #Import functions from library
    from numpy import size, array
    #Transpose a 2D list
    def transpose_list_2d(list_in_mat):
        list_out_mat = []
        array_in_mat = array(list_in_mat)
        array_out_mat = array_in_mat.T
        nb_lines = size(array_out_mat, 0)
        for i_line_out in range(0, nb_lines):
            array_out_line = array_out_mat[i_line_out]
            list_out_line = list(array_out_line)
            list_out_mat.append(list_out_line)
        return list_out_mat
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.