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

Tytus
źródło

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.

jena
źródło
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]]
SiggyF
źródło
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]]
inspectorG4dget
źródło
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]]
Matchew
źródło
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.

Bob Stein
źródło
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]
jasonleonhard
źródło
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
piłkarz2399
źródło
0
import numpy as np
r = list(map(list, np.transpose(l)))
reza.cse08
źródło
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
Bóg
źródło
-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])
1man
źródło
-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
źródło