Transponuj listę list


241

Weźmy:

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

Wynik, którego szukam, to

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

i nie

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

Bardzo mile widziane

Odpowiedzi:


336

Co powiesz na

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

W przypadku python 3.x użytkownicy mogą korzystać

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

Wyjaśnienie:

Są dwie rzeczy, które musimy wiedzieć, aby zrozumieć, co się dzieje:

  1. Podpis zip : zip(*iterables)Oznacza to, zipże oczekuje dowolnej liczby argumentów, z których każdy musi być iterowalny. Np zip([1, 2], [3, 4], [5, 6]).
  2. Rozpakowane listy argumentów : Biorąc pod uwagę sekwencję argumentów args, f(*args)wywoła ftakie, że każdy element argsjest osobnym argumentem pozycyjnym f.

Wracając do danych wejściowych z pytania l = [[1, 2, 3], [4, 5, 6], [7, 8, 9]], zip(*l)byłoby to równoważne z zip([1, 2, 3], [4, 5, 6], [7, 8, 9]). Reszta to tylko upewnienie się, że wynikiem będzie lista list zamiast listy krotek.


67
Uwaga: jeśli lnie jest równomiernie wielkości (powiedzmy, niektóre wiersze są krótsze niż w innych), zipbędzie nie zrekompensować to i zamiast posiekać z dala wiersze z wyjścia. Więc l=[[1,2],[3,4],[5]]daje [[1,3,5]].
badp

29
Ta itertoolsfunkcja zip_longest()działa z nierównymi listami. Zobacz DOCS
Oregano

13
Wyjaśnienie w odpowiedzi byłoby miłe :)
Boris Churzin

7
Myślę, że nawet list(zip(*l))działa poprawnie w Pythonie 3.
Stefano

3
@Stefano Działa (jak zip(*l)w Pythonie 2), ale dostajesz listę krotek, a nie listę list. Oczywiście list(list(it))zawsze jest to samo, co list(it).
Alex Shpilkin

62

Jednym ze sposobów jest transpozycja NumPy. W przypadku listy:

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

Lub inny bez zip:

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

8
Uwielbiam twój drugi - nie zdawałem sobie sprawy map, że mogę to zrobić. Oto niewielkie udoskonalenie, które nie wymaga 2 połączeń:map(lambda *a: list(a), *l)
Lee D

7
Czy nie powinna to być lepsza odpowiedź, ponieważ zajmuje się nierównymi listami?
Leon

15
map(None, ...)wydaje się nie działać dla Py3. Generator jest tworzony, ale next()natychmiast podnosi błąd: TypeError: 'NoneType' object is not callable.
Mad Physicist,

57

Równoważnie z rozwiązaniem Jeny:

>>> 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
Ponieważ preferowane jest teraz rozumienie listy map(), to rozwiązanie jest najbardziej w duchu Pythona ...
Perror

26

tylko dla zabawy, prawidłowe prostokąty i przy założeniu, że istnieje m [0]

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

tego właśnie szukałem i nie mogłem się rozejrzeć. Rozwiązanie @ @ Jenna jest naprawdę krótkie
Titus

3
Tak, zajęło to kilka opon, aby to naprawić. Dobra, wiele prób.
matchew

9
Nadal nie jest w porządku - dzieje się tak tylko wtedy, gdy wymiary są kwadratowe! Powinno być: [[j[i] for j in l] for i in range(len(l[0]))]. Oczywiście musisz upewnić się, że lista lnie jest pusta.
Lee D

@LeeD wciąż nie działa dla mnie na przykładzie Jenny l = [[1,2], [3,4], [5]]
płyty

3
@ hobs To był przykład badpa, odpowiadający na Jenę. Nie jestem jednak pewien, czy to ma dla mnie sens. IMO, transpozycja oznacza prostokątną matrycę - gdy jest reprezentowana jako lista list, oznacza to, że wszystkie listy wewnętrzne muszą być tej samej długości. Jaki wynik chciałbyś jako „transpozycja” tego przykładu?
Lee D

22

Metody 1 i 2 działają w języku Python 2 lub 3 i działają na nierównych, prostokątnych listach 2D. Oznacza to, że listy wewnętrzne nie muszą mieć takich samych długości (nierówne) ani zewnętrznych (prostokątne). Inne metody są skomplikowane.

ustawić

import itertools
import six

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

metoda 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() staje się

Domyślna wartość wypełnienia to None. Dzięki @ Jeny odpowiedź , gdzie map()zmienia wewnętrzne krotki do list. Tutaj zamienia iteratory w listy. Dzięki komentarzom @ Oregano i @ badp .

W Pythonie 3 przekaż wynik, list()aby uzyskać tę samą listę 2D co metoda 2.


metoda 2 - rozumienie listy, 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, '-']]

Alternatywa @ inspectorG4dget .


metoda 3 - map()of map()- broken w Pythonie 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]]

Ta niezwykle kompaktowa druga alternatywa @ SigigF działa z poszarpanymi listami 2D, w przeciwieństwie do jego pierwszego kodu, który używa numpy do transponowania i przechodzenia przez poszarpane listy. Ale Brak musi być wartością wypełnienia. (Nie, Brak przekazany do wewnętrznej mapy () nie jest wartością wypełnienia. Oznacza to, że nie ma funkcji przetwarzającej każdą kolumnę. Kolumny są właśnie przekazywane do zewnętrznej mapy (), która konwertuje je z krotek na listy.

Gdzieś w Pythonie 3 map()przestałem znosić to nadużycie: pierwszym parametrem nie może być None, a nierówne iteratory są po prostu skracane do najkrótszych. Inne metody nadal działają, ponieważ dotyczy to tylko wewnętrznej mapy ().


Metoda 4 - map()z map()revisited

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

Niestety poszarpane rzędy NIE stają się poszarpanymi kolumnami w Pythonie 3, są one po prostu obcięte. Postęp boo hoo.


7

Trzy opcje do wyboru:

1. Mapa z Zip

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

2. Zrozumienie listy

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

3. Do dołączania w pętli

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

I aby zobaczyć wyniki:

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

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

0

Może nie jest to najbardziej eleganckie rozwiązanie, ale oto rozwiązanie wykorzystujące zagnieżdżone pętle 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


0

more_itertools.unzip() jest łatwy do odczytania i działa również z generatorami.

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

lub równoważnie

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

Oto rozwiązanie dla transpozycji listy list, która niekoniecznie jest kwadratowa:

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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.