Transponer lista de listas

241

Echemos:

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

El resultado que estoy buscando es

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

y no

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

Muy apreciado

titus
fuente

Respuestas:

337

Qué tal si

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

Para python 3.x los usuarios pueden usar

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

Explicación:

Hay dos cosas que necesitamos saber para entender lo que está sucediendo:

  1. La firma de zip : zip(*iterables)Esto significa que zipespera un número arbitrario de argumentos, cada uno de los cuales debe ser iterable. Por ej zip([1, 2], [3, 4], [5, 6]).
  2. Listas de argumentos desempaquetados : Dada una secuencia de argumentos args, f(*args)llamará de ftal manera que cada elemento en argssea ​​un argumento posicional separado de f.

Volviendo a la entrada de la pregunta l = [[1, 2, 3], [4, 5, 6], [7, 8, 9]], zip(*l)sería equivalente a zip([1, 2, 3], [4, 5, 6], [7, 8, 9]). El resto es solo asegurarse de que el resultado sea una lista de listas en lugar de una lista de tuplas.

jena
fuente
67
Cuidado: si lno está dimensionado de manera uniforme (por ejemplo, algunas filas son más cortas que otros), zipserá no compensar por ello y en lugar de cortar distancia filas de la salida. Entonces l=[[1,2],[3,4],[5]]te da [[1,3,5]].
badp
29
La itertoolsfunción zip_longest()funciona con listas desiguales. Ver DOCS
Orégano
13
Una explicación en respuesta sería agradable :)
Boris Churzin
77
Creo que incluso list(zip(*l))funciona correctamente en Python 3.
Stefano
3
@Stefano Funciona (como lo hace zip(*l)en Python 2), pero obtienes una lista de tuplas, no una lista de listas. Por supuesto, list(list(it))siempre es lo mismo que list(it).
Alex Shpilkin
62

Una forma de hacerlo es con la transposición NumPy. Para una lista, un:

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

U otro sin cremallera:

>>> map(list,map(None,*a))
[[1, 4, 7], [2, 5, 8], [3, 6, 9]]
SiggyF
fuente
8
Me encanta tu segundo, no me di cuenta de que mappodía hacer eso. Sin embargo, aquí hay un ligero refinamiento que no requiere 2 llamadas:map(lambda *a: list(a), *l)
Lee D
77
¿No debería ser esta una mejor respuesta, ya que se ocupa de listas desiguales?
Leon
15
map(None, ...)no parece funcionar para Py3. El generador se crea, pero next()genera un error de inmediato: TypeError: 'NoneType' object is not callable.
Físico loco
57

Equivalente a la solución 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
fuente
12
Como ahora se prefiere la comprensión de la lista map(), esta solución es la que tiene más espíritu Python ...
Perror
26

solo por diversión, rectángulos válidos y suponiendo 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
fuente
esto es lo que estaba buscando y no podía entenderlo. Aún así, la solución de @ jena es realmente corta
titus
3
Sí, tomó algunas llantas para hacer esto bien. De acuerdo, muchos intentos.
matchew
99
Todavía no está del todo bien: ¡esto solo funciona cuando las dimensiones son cuadradas! Debe ser: [[j[i] for j in l] for i in range(len(l[0]))]. Por supuesto, debe asegurarse de que la lista lno esté vacía.
Lee D
@LeeD todavía no funciona para mí en el ejemplo de jena l = [[1,2], [3,4], [5]]
hobs
3
@hobs Ese fue el ejemplo de badp, respondiendo a jena. Sin embargo, no estoy seguro de que tenga sentido para mí. OMI, la transposición implica una matriz rectangular: cuando se representa como una lista de listas, eso significa que todas las listas internas deben tener la misma longitud. ¿Qué resultado desearías como la "transposición" de este ejemplo?
Lee D
22

Los métodos 1 y 2 funcionan en Python 2 o 3, y funcionan en irregular, rectangular listas 2D. Eso significa que las listas internas no necesitan tener las mismas longitudes entre sí (desiguales) o que las listas externas (rectangulares). Los otros métodos, bueno, son complicados.

la puesta en marcha

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() se convierte

El valor de relleno predeterminado es None. Gracias a la respuesta de @ jena , ¿dónde map()está cambiando las tuplas internas a listas? Aquí está convirtiendo iteradores en listas. Gracias a los comentarios de @ Oregano y @ badp .

En Python 3, pase el resultado list()para obtener la misma lista 2D que el método 2.


método 2 - comprensión de la 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, '-']]

La alternativa @ inspectorG4dget .


método 3 - map()de map()- roto en 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]]

Esta segunda alternativa extraordinariamente compacta de @SiggyF funciona con listas 2D irregulares, a diferencia de su primer código que usa numpy para transponer y pasar por listas irregulares. Pero Ninguno tiene que ser el valor de relleno. (No, el Ninguno pasado al mapa interno () no es el valor de relleno. Significa que no hay ninguna función para procesar cada columna. Las columnas simplemente se pasan al mapa externo () que las convierte de tuplas a listas.

En algún lugar de Python 3, map()dejé de soportar todo este abuso: el primer parámetro no puede ser None, y los iteradores irregulares se truncan al más corto. Los otros métodos aún funcionan porque esto solo se aplica al 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+

Por desgracia, las filas irregulares NO se convierten en columnas irregulares en Python 3, solo se truncan. Boo hoo progreso.

Bob Stein
fuente
7

Tres opciones para elegir:

1. Mapa con Zip

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

2. Lista de comprensión

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

3. Para anexar bucles

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

Y para ver los resultados:

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

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

Quizás no sea la solución más elegante, pero aquí hay una solución que usa bucles while anidados:

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

more_itertools.unzip() es fácil de leer y también funciona con generadores.

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 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
Dios
fuente
-1

Aquí hay una solución para transponer una lista de listas que no es necesariamente cuadrada:

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 hombre
fuente
-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
fuente