Transpor lista de listas

241

Vamos levar:

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

O resultado que estou procurando é

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

e não

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

Muito apreciado

titus
fonte

Respostas:

336

E se

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

Para python 3.x, os usuários podem usar

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

Explicação:

Há duas coisas que precisamos saber para entender o que está acontecendo:

  1. A assinatura do zip : zip(*iterables)isso significa que zipespera um número arbitrário de argumentos, cada um dos quais deve ser iterável. Por exemplo zip([1, 2], [3, 4], [5, 6]).
  2. Listas de argumentos não compactadas : Dada uma sequência de argumentos args, f(*args)chamará ftal que cada elemento em argsseja um argumento posicional separado de f.

Voltando à entrada da pergunta l = [[1, 2, 3], [4, 5, 6], [7, 8, 9]], zip(*l)seria equivalente a zip([1, 2, 3], [4, 5, 6], [7, 8, 9]). O resto é apenas garantir que o resultado seja uma lista de listas em vez de uma lista de tuplas.

jena
fonte
67
Cuidado: se lnão é dimensionada de maneira uniforme (por exemplo, algumas linhas são mais curtas do que outros), zipserá não compensá-lo e, em vez corta fora as linhas da saída. Então l=[[1,2],[3,4],[5]]te dá [[1,3,5]].
badp 13/09/13
29
A itertoolsfunção zip_longest()funciona com listas desiguais. Veja DOCS
Oregano
13
Uma explicação em resposta seria bom :)
Boris Churzin
7
Acho que até list(zip(*l))funciona corretamente em Python 3.
Stefano
3
@ Stefano Funciona (como zip(*l)no Python 2), mas você obtém uma lista de tuplas, não uma lista de listas. Claro, list(list(it))é sempre a mesma coisa que list(it).
Alex Shpilkin
62

Uma maneira de fazer isso é com a transposição do NumPy. Para uma lista, a:

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

Ou outro sem zip:

>>> map(list,map(None,*a))
[[1, 4, 7], [2, 5, 8], [3, 6, 9]]
SiggyF
fonte
8
Ame o seu segundo - eu não sabia que mappoderia fazer isso. Aqui está um leve refinamento que não requer duas chamadas, no entanto:map(lambda *a: list(a), *l)
Lee D
7
Não deveria ser uma resposta melhor, pois trata de listas desiguais?
Leon
15
map(None, ...)parece não funcionar para o Py3. O gerador é criada, mas next()gera um erro imediatamente: TypeError: 'NoneType' object is not callable.
Mad físico
57

Equivalentemente à solução de 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]]
inspectorG4dget
fonte
12
Como compreensão da lista está agora preferiu map(), esta solução é aquela que é a mais no espírito Python ...
perror
26

apenas por diversão, retângulos válidos e assumindo que m [0] existe

>>> 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]]
matchew
fonte
era isso que eu estava procurando e não conseguia entender. Ainda @ solução de Jena é muito curto
titus
3
Sim, foram necessários alguns pneus para acertar. Ok, muitas tentativas.
matchew
9
Ainda não está certo - isso só funciona quando as dimensões são quadradas! Ele deve ser: [[j[i] for j in l] for i in range(len(l[0]))]. Obviamente, você precisa ter certeza de que a lista lnão está vazia.
Lee D
@LeeD ainda não funciona para mim no exemplo de jena l = [[1,2], [3,4], [5]]
hobs
3
@ hobs Esse foi o exemplo do badp, respondendo a jena. No entanto, não tenho certeza de que faça sentido para mim. Na IMO, a transposição implica uma matriz retangular - quando representada como uma lista de listas, significa que todas as listas internas devem ter o mesmo comprimento. Qual resultado você deseja como a "transposição" deste exemplo?
Lee D
22

Os métodos 1 e 2 funcionam em Python 2 ou 3 e funcionam em retângulos irregulares listas 2D . Isso significa que as listas internas não precisam ter os mesmos comprimentos entre si (irregulares) ou como as listas externas (retangulares). Os outros métodos, bem, são complicados.

a configuração

import itertools
import six

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

método 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() torna-se

O valor de preenchimento padrão é None. Graças à resposta de @ jena , onde map()está alterando as tuplas internas para listas. Aqui está transformando iteradores em listas. Graças a @ e do Oregano de @ badp comentários .

No Python 3, passe o resultado list()para obter a mesma lista 2D do método 2.


método 2 - compreensão da lista, 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, '-']]

A alternativa @ inspectorG4dget .


método 3 - map()of map()- quebrado em 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]]

Essa segunda alternativa extraordinariamente compacta do @SiggyF funciona com listas 2D irregulares, ao contrário de seu primeiro código, que usa numpy para transpor e passar por listas irregulares. Mas Nenhum deve ser o valor de preenchimento. (Não, o None passado para o mapa interno () não é o valor de preenchimento. Isso significa que não há função para processar cada coluna. As colunas são passadas apenas para o mapa externo () que as converte de tuplas em listas.

Em algum lugar do Python 3, map()parou de tolerar todo esse abuso: o primeiro parâmetro não pode ser None e os iteradores irregulares são truncados no menor tempo possível. Os outros métodos ainda funcionam porque isso se aplica apenas ao mapa interno ().


método 4 - map()de map()revisitado

>>> 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+

Infelizmente, as linhas irregulares NÃO se tornam colunas irregulares no Python 3, elas são apenas truncadas. Boo hoo progresso.

Bob Stein
fonte
7

Três opções para escolher:

1. Mapa com Zip

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

2. Compreensão da lista

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

3. Para loop anexado

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

E para visualizar os resultados:

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

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

Talvez não seja a solução mais elegante, mas aqui está uma solução usando loops while aninhados:

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
footballman2399
fonte
0
import numpy as np
r = list(map(list, np.transpose(l)))
reza.cse08
fonte
0

more_itertools.unzip() é fácil de ler e também funciona com geradores.

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

ou equivalente

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
Deus
fonte
-1

Aqui está uma solução para transpor uma lista de listas que não é necessariamente quadrada:

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])
1 homem
fonte
-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
SolarJonathan
fonte