Funkcja transpozycji/rozpakowania (odwrotność zip)?


342

Mam listę 2-elementowych krotek i chciałbym przekonwertować je na 2 listy, gdzie pierwsza zawiera pierwszy element w każdej krotce, a druga zawiera drugi element.

Na przykład:

original = [('a', 1), ('b', 2), ('c', 3), ('d', 4)] 
# and I want to become... 
result = (['a', 'b', 'c', 'd'], [1, 2, 3, 4]) 

Czy istnieje wbudowana funkcja, która to robi?

+3

Doskonałe odpowiedzi poniżej, ale zobacz też [transpozycja numpy] (http://docs.scipy.org/doc/numpy/reference/generated/numpy.transpose.html) 03 mar. 152015-03-03 13:17:26

+1

Zobacz tę fajną odpowiedź, aby zrobić to samo z generatory zamiast listy: [how-to-unzip-an-iterator] (http://stackoverflow.com/questions/30805000/how-to-unzip-an-iterator) 05 sty. 162016-01-05 20:31:56

546

zip jest odwrotnością! Pod warunkiem użycia specjalnego operatora *.

>>> zip(*[('a', 1), ('b', 2), ('c', 3), ('d', 4)]) 
[('a', 'b', 'c', 'd'), (1, 2, 3, 4)] 

Sposób ten działa to poprzez wywołanie zip z argumentami:

zip(('a', 1), ('b', 2), ('c', 3), ('d', 4)) 

... oprócz argumentów przekazywanych do zip bezpośrednio (po konwertowane na krotki), więc nie trzeba się martwić o liczbie argumentów, które stają się zbyt duże.

+6

Och, gdyby tylko to było takie proste. Rozpakowanie 'zip ([], [])' w ten sposób nie dostaniesz '[], []'. Dostaje cię '[]'. Jeśli tylko ... 24 lut. 142014-02-24 12:06:58

  0

@ user2357112 daje 'zip (* zip ([lista1], [lista2]))' daje '([lista1, lista2])'. 25 lut. 142014-02-25 21:50:13

  0

@cdhagmann: 'zip ([lista1], [lista2])' nigdy nie jest tym, czego potrzebujesz. To daje ci '[(list1, list2)]'. 25 lut. 142014-02-25 22:05:47

  0

@ user2357112 Użyłem '[list1]' do oznaczenia dowolnej listy o nazwie list1, a nie listy z listą zawierającą tylko jedną listę. Tak więc podane 'list1 = [1,2,3,4]' i 'list2 = [1,2,3,4]' następnie 'zip (* zip (list1, list2))' daje '([1 , 2,3,4], [1,2,3,4]) ' 25 lut. 142014-02-25 22:31:06

+1

@cdhagmann: Teraz spróbuj tego z' list1 = []; lista2 = [] '. 26 lut. 142014-02-26 02:53:43

  0

@cdhagmann otrzymujesz [(1, 2, 3, 4), (1, 2, 3, 4)] ze swoich poleceń. 03 lip. 142014-07-03 13:07:52

+1

To nie działa w języku Python3. Zobacz: http://stackoverflow.com/questions/24590614/python3-unzipping-a-list-of-tuples 05 lip. 142014-07-05 21:35:16

  0

zip nie zachowuje elementów w dłuższych iterabelach, dlatego wymagane jest dopełnienie 21 wrz. 142014-09-21 06:03:50

  0

'krotka (mapa (lista, zip (* original)))), aby uzyskać dokładnie wspomniany wynik. 30 sty. 152015-01-30 04:26:57

+4

@Tommy To jest nieprawidłowe. 'zip' działa dokładnie tak samo w Pythonie 3, z tym że zwraca iterator zamiast listy. Aby uzyskać takie same wyniki jak powyżej, wystarczy zawinąć wywołanie zip na liście: 'list (zip (* [('a', 1), ('b', 2), ('c', 3), ('d', 4)])) 'wypisze' [('a', 'b', 'c', 'd'), (1, 2, 3, 4)] ' 11 mar. 152015-03-11 14:11:12

+1

notice: you może napotkać problemy z pamięcią i wydajnością na bardzo długich listach. 14 paź. 162016-10-14 12:44:09

  0

To jest fantastyczna odpowiedź. :) 16 maj. 172017-05-16 19:00:59


22

Można również zrobić skalę

result = ([ a for a,b in original ], [ b for a,b in original ]) 

It powinien lepiej. Zwłaszcza jeśli Python radzi sobie dobrze, nie rozszerzając listy, chyba że jest to konieczne.

(Nawiasem mówiąc, to sprawia, że ​​2-krotki (para) list, zamiast listę krotek, jak zip robi.)

Jeśli generatory zamiast rzeczywistych list są ok, to byłoby to zrobić:

result = ((a for a,b in original), (b for a,b in original)) 

Generatory nie przeżuwają listy, dopóki nie poprosi o każdy element, ale z drugiej strony zachowują odniesienia do oryginalnej listy.

+6

"Zwłaszcza jeśli Python dobrze sobie radzi, nie rozszerzając listy, chyba że jest to konieczne." mmm ... normalnie, zrozumienie list jest natychmiast rozszerzane - czy coś złego? 15 sie. 112011-08-15 19:52:57

  0

@glglgl: Nie, prawdopodobnie masz rację. Miałem tylko nadzieję, że jakaś przyszła wersja może zacząć robić właściwe rzeczy. (Zmiana nie jest niemożliwa, semantyka efektów ubocznych, która wymaga zmian, prawdopodobnie już jest odradzana). 15 paź. 122012-10-15 12:54:03

+8

To, co masz nadzieję uzyskać, to ekspresja generatora - która już istnieje. 15 paź. 122012-10-15 13:12:19

  0

Nie, mam nadzieję, że otrzymam odwieczny ulubiony "wystarczająco inteligentny kompilator" (lub w tym przypadku tłumacz). Nie wydaje mi się, żeby było coś sensownego, co mogłoby zostać zerwane przez przeanalizowanie bejeebusa z kodu i zrobienie czegoś zupełnie innego. (jak robienie leniwego zbioru) Python nigdy nie obiecał tej funkcji i najprawdopodobniej nigdy jej nie będzie, ale widzę to marzenie w projekcie. 18 paź. 122012-10-18 07:11:03

  0

+1 dla skal lepiej. 19 lip. 132013-07-19 10:59:09

+11

To nie ma "większej skali" niż wersja 'zip (* x)'. 'zip (* x)' wymaga tylko jednego przejścia przez pętlę i nie używa elementów stosu. 17 lis. 132013-11-17 16:38:20

+1

To, czy "skaluje się lepiej" zależy od cyklu życia oryginalnych danych w porównaniu z transponowanymi danymi. Ta odpowiedź jest tylko lepsza niż użycie 'zip' jeśli przypadek użycia jest taki, że transponowane dane są używane i odrzucane natychmiast, podczas gdy oryginalne listy pozostają w pamięci przez znacznie dłużej. 15 lis. 152015-11-15 06:55:50


19

Jeśli masz listy, które nie mają tej samej długości, możesz nie chcieć używać zip zgodnie z odpowiedzią Patricks. To działa:

>>> zip(*[('a', 1), ('b', 2), ('c', 3), ('d', 4)]) 
[('a', 'b', 'c', 'd'), (1, 2, 3, 4)] 

Ale z różnych list długości, zip obcina każdą pozycję do długości najkrótszej listy:

>>> zip(*[('a', 1), ('b', 2), ('c', 3), ('d', 4), ('e',)]) 
[('a', 'b', 'c', 'd', 'e')] 

można wykorzystać mapę bez funkcji do wypełnienia pustych wyniki z None:

>>> map(None, *[('a', 1), ('b', 2), ('c', 3), ('d', 4), ('e',)]) 
[('a', 'b', 'c', 'd', 'e'), (1, 2, 3, 4, None)] 

zip() jest jednak marginalnie szybszy.

+3

ciekawe, czy możesz wyjaśnić, jak działa 'map'? 26 wrz. 132013-09-26 15:53:09

+4

Można również użyć 'izip_longest' 26 wrz. 132013-09-26 16:52:55

+2

Znany jako' zip_longest' dla użytkowników python3. 08 mar. 162016-03-08 09:02:13

  0

@GrijeshChauhan Wiem, że to jest naprawdę stare, ale jest to dziwna wbudowana funkcja: https://docs.python.org/2/library/functions.html#map "Jeśli funkcja ma wartość None, przyjęto funkcję identyfikacji; jeśli istnieje wiele argumentów, map() zwraca listę składającą się z krotek zawierających odpowiednie pozycje ze wszystkich iteracji (rodzaj operacji transpozycji) .Iterowalne argumenty mogą być sekwencją lub dowolnym obiektem iterowanym, wynikiem jest zawsze lista. " 14 lip. 172017-07-14 19:26:37

  0

@ cactus1 Dzięki, pomocne, tym razem uczyłem się Python 15 lip. 172017-07-15 04:42:25


11

Lubię używać zip(*iterable) (który jest kawałek kodu szukasz) w moich programach jak tak:

def unzip(iterable): 
    return zip(*iterable) 

znajdę unzip bardziej czytelny.


2

To tylko kolejny sposób, aby to zrobić, ale to bardzo mi pomogło więc piszę tutaj:

Mając tę ​​strukturę danych:

X=[1,2,3,4] 
Y=['a','b','c','d'] 
XY=zip(X,Y) 

skutkuje:

In: XY 
Out: [(1, 'a'), (2, 'b'), (3, 'c'), (4, 'd')] 

The bardziej pythonic sposób rozpakowania go i powrotu do oryginału to ta w mojej opinii:

x,y=zip(*XY) 

Ale ten powrót krotki więc jeśli trzeba tablicę można użyć:

xy=(list(x),list(y)) 

7
>>> original = [('a', 1), ('b', 2), ('c', 3), ('d', 4)] 
>>> tuple([list(tup) for tup in zip(*original)]) 
(['a', 'b', 'c', 'd'], [1, 2, 3, 4]) 

Daje krotki list jak w pytaniu.

list1, list2 = [list(tup) for tup in zip(*original)] 

Rozpakowuje dwie listy.