Transposer la liste des listes

241

Prenons:

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

Le résultat que je recherche est

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

et pas

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

Très appréciée

titus
la source

Réponses:

337

Que diriez-vous

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

Pour python 3.x, les utilisateurs peuvent utiliser

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

Explication:

Il y a deux choses que nous devons savoir pour comprendre ce qui se passe:

  1. La signature de zip : zip(*iterables)Cela signifie qu'il zipattend un nombre arbitraire d'arguments dont chacun doit être itérable. Par exemple zip([1, 2], [3, 4], [5, 6]).
  2. Listes d'arguments décompressées : étant donné une séquence d'arguments args, f(*args)appellera de ftelle sorte que chaque élément dans argsest un argument positionnel distinct de f.

Revenir à l'apport de la question l = [[1, 2, 3], [4, 5, 6], [7, 8, 9]], zip(*l)serait équivalent à zip([1, 2, 3], [4, 5, 6], [7, 8, 9]). Le reste s'assure simplement que le résultat est une liste de listes au lieu d'une liste de tuples.

Iéna
la source
67
Attention: s'il ln'est pas de taille égale (par exemple, certaines lignes sont plus courtes que d'autres), zipil ne le compensera pas et découpera les lignes de la sortie. l=[[1,2],[3,4],[5]]Vous donne donc [[1,3,5]].
badp
29
La itertoolsfonction zip_longest()fonctionne avec des listes inégales. Voir DOCS
Origan
13
Une explication en réponse serait bien :)
Boris Churzin
7
Je pense que même list(zip(*l))fonctionne correctement en Python 3.
Stefano
3
@Stefano Cela fonctionne (comme zip(*l)dans Python 2), mais vous obtenez une liste de tuples, pas une liste de listes. Bien sûr, list(list(it))c'est toujours la même chose que list(it).
Alex Shpilkin
62

Une façon de le faire est avec la transposition NumPy. Pour une liste, un:

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

Ou un autre sans zip:

>>> map(list,map(None,*a))
[[1, 4, 7], [2, 5, 8], [3, 6, 9]]
SiggyF
la source
8
J'adore votre deuxième - je ne savais pas que mapje pouvais faire ça. Voici un léger raffinement qui ne nécessite pas 2 appels, cependant:map(lambda *a: list(a), *l)
Lee D
7
Cela ne devrait-il pas être une meilleure réponse car il prend en charge les listes inégales?
Leon
15
map(None, ...)ne semble pas fonctionner pour Py3. Le générateur est créé , mais next()déclenche une erreur immédiatement: TypeError: 'NoneType' object is not callable.
Mad Physicist
57

De manière équivalente à la solution d'Iéna:

>>> 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]]
inspecteurG4dget
la source
12
La compréhension de liste étant désormais préférée map(), cette solution est celle qui est la plus dans l'esprit Python ...
perror
26

juste pour le plaisir, des rectangles valides et en supposant 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
la source
c'est ce que je cherchais et je n'ai pas pu me repérer. La solution de Still @ jena est vraiment courte
titus
3
Oui, il a fallu quelques pneus pour bien faire les choses. D'accord, beaucoup d'essais.
matchew
9
Ce n'est toujours pas tout à fait vrai - cela ne fonctionne que lorsque les dimensions sont carrées! Il devrait être: [[j[i] for j in l] for i in range(len(l[0]))]. Bien sûr, vous devez vous assurer que la liste ln'est pas vide.
Lee D
@LeeD ne fonctionne toujours pas pour moi sur l'exemple de jena l = [[1,2], [3,4], [5]]
plaques de cuisson du
3
@hobs C'était l'exemple de badp, répondant à jena. Cependant, je ne suis pas sûr que cela ait du sens pour moi. OMI, la transposition implique une matrice rectangulaire - lorsqu'elle est représentée sous forme de liste de listes, cela signifie que toutes les listes internes doivent avoir la même longueur. Quel résultat souhaiteriez-vous comme "transposition" de cet exemple?
Lee D
22

Les méthodes 1 et 2 fonctionnent en Python 2 ou 3, et elles fonctionnent sur des listes 2D rectangulaires irrégulières . Cela signifie que les listes internes n'ont pas besoin d'avoir les mêmes longueurs les unes par rapport aux autres (irrégulières) ou que les listes externes (rectangulaires). Les autres méthodes, eh bien, c'est compliqué.

La mise en place

import itertools
import six

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

méthode 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() devient

La valeur de remplissage par défaut est None. Merci à la réponse de @ jena , où map()change les tuples internes en listes. Ici, il transforme les itérateurs en listes. Merci aux commentaires de @ Oregano et @ badp .

En Python 3, transmettez le résultat list()pour obtenir la même liste 2D que la méthode 2.


méthode 2 - liste de compréhension, 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' alternative @ inspectorG4dget .


méthode 3 - map()of map()- broken 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]]

Cette deuxième alternative extraordinairement compacte @SiggyF fonctionne avec des listes 2D déchiquetées, contrairement à son premier code qui utilise numpy pour transposer et passer à travers des listes déchiquetées. Mais aucun ne doit être la valeur de remplissage. (Non, le Aucun transmis à la carte interne () n'est pas la valeur de remplissage. Cela signifie qu'il n'y a pas de fonction pour traiter chaque colonne. Les colonnes sont simplement transmises à la carte externe () qui les convertit des tuples en listes.

Quelque part dans Python 3, a map()cessé de supporter tous ces abus: le premier paramètre ne peut pas être None, et les itérateurs irréguliers sont simplement tronqués au plus court. Les autres méthodes fonctionnent toujours car cela ne s'applique qu'à la carte intérieure ().


méthode 4 - map()de map()revisité

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

Hélas, les lignes irrégulières ne deviennent PAS des colonnes irrégulières en Python 3, elles sont juste tronquées. Boo hoo progress.

Bob Stein
la source
7

Trois options au choix:

1. Carte avec Zip

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

2. Compréhension des listes

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

3. Pour l'ajout d'une boucle

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

Et pour voir les résultats:

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

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

Peut-être pas la solution la plus élégante, mais voici une solution utilisant des boucles imbriquées while:

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

more_itertools.unzip() est facile à lire et fonctionne également avec les générateurs.

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 équivalent

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
Dieu
la source
-1

Voici une solution pour transposer une liste de listes qui n'est pas forcément carrée:

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 homme
la source
-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
la source