Funzione Transpose/Unzip (inversa di zip)?


342

Ho una lista di tuple di 2 voci e mi piacerebbe convertirle in 2 liste in cui la prima contiene il primo elemento in ciascuna tupla e la seconda lista contiene il secondo elemento.

Ad esempio:

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

Esiste una funzione built-in che lo fa?

+3

Grandi risposte qui sotto, ma guarda anche [numpy's transpose] (http://docs.scipy.org/doc/numpy/reference/generated/numpy.transpose.html) 03 mar. 152015-03-03 13:17:26

+1

Vedi questa bella risposta per fare lo stesso con generatori invece di lista: [how-to-unzip-an-iterator] (http://stackoverflow.com/questions/30805000/how-to-unzip-an-iterator) 05 gen. 162016-01-05 20:31:56

546

zip è il suo inverso! Se si utilizza l'operatore speciale *.

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

Il modo in cui funziona è chiamando zip con gli argomenti:

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

... tranne gli argomenti vengono passati a zip direttamente (dopo essere stato convertito in una tupla), quindi non c'è bisogno di preoccuparsi sul numero di argomenti che diventano troppo grandi.

+6

Oh, se solo fosse così semplice. Unzipping 'zip ([], [])' in questo modo non ti dà '[], []'. Ti dà '[]'. Se solo ... 24 feb. 142014-02-24 12:06:58

  0

@ user2357112 ti dà 'zip (* zip ([lista1], [lista2]))' ti dà '([lista1, lista2])'. 25 feb. 142014-02-25 21:50:13

  0

@cdhagmann: 'zip ([lista1], [lista2])' non è mai quello che vuoi, però. Questo ti dà solo '[(lista1, lista2)]'. 25 feb. 142014-02-25 22:05:47

  0

@ user2357112 Stavo usando '[lista1]' per indicare qualsiasi lista chiamata lista1 e non come lista con una lista con una sola lista come una voce. Quindi dato 'list1 = [1,2,3,4]' e 'list2 = [1,2,3,4]' poi 'zip (* zip (lista1, lista2))' ti dà '([1 , 2,3,4], [1,2,3,4]) ' 25 feb. 142014-02-25 22:31:06

+1

@cdhagmann: Ora prova con' list1 = []; list2 = [] '. 26 feb. 142014-02-26 02:53:43

  0

@cdhagmann ottieni [(1, 2, 3, 4), (1, 2, 3, 4)] dai tuoi comandi. 03 lug. 142014-07-03 13:07:52

+1

Questo non funziona in Python3. Vedi: http://stackoverflow.com/questions/24590614/python3-unzipping-a-list-of-tuples 05 lug. 142014-07-05 21:35:16

  0

zip non conserva gli elementi in iterabili più lunghi, quindi è necessario il padding 21 set. 142014-09-21 06:03:50

  0

'tupla (mappa (elenco, zip (* originale))) 'per ottenere esattamente il risultato indicato. 30 gen. 152015-01-30 04:26:57

+4

@Tommy Questo non è corretto. 'zip' funziona esattamente allo stesso modo in Python 3 tranne che restituisce un iteratore invece di una lista. Per ottenere lo stesso risultato di cui sopra devi solo racchiudere la chiamata zip in un elenco: 'lista (zip (* [('a', 1), ('b', 2), ('c', 3), ('d', 4)])) 'restituirà' [('a', 'b', 'c', 'd'), (1, 2, 3, 4)] ' 11 mar. 152015-03-11 14:11:12

+1

avviso: tu può incontrare problemi di memoria e di prestazioni con elenchi molto lunghi. 14 ott. 162016-10-14 12:44:09

  0

Questa è una risposta fantastica. :) 16 mag. 172017-05-16 19:00:59


22

Si potrebbe anche farlo scala

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

dovrebbe meglio. Soprattutto se Python fa bene a non espandere la comprensione delle liste se non necessario.

(Tra l'altro, fa una 2-tuple (coppia) di liste, piuttosto che una lista di tuple, come zip fa.)

Se generatori invece di liste reali sono ok, questo lo farebbe:

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

I generatori non sgranocchiano l'elenco finché non si chiede ciascun elemento, ma d'altro canto mantengono i riferimenti all'elenco originale.

+6

"Soprattutto se Python fa bene a non espandere la comprensione delle liste se non necessario." mmm ... normalmente, le comprensioni delle liste sono espanse immediatamente - o ottengo qualcosa di sbagliato? 15 ago. 112011-08-15 19:52:57

  0

@glglgl: No, probabilmente hai ragione. Speravo solo che qualche versione futura potesse iniziare a fare la cosa giusta. (Non è impossibile cambiare, la semantica dell'effetto collaterale che ha bisogno di modifiche è probabilmente già scoraggiata.) 15 ott. 122012-10-15 12:54:03

+8

Quello che speri di ottenere è un'espressione generatore - che esiste già. 15 ott. 122012-10-15 13:12:19

  0

No, quello che spero di ottenere è il preferito perenne "un compilatore sufficientemente intelligente" (o un interprete in questo caso). Non penso che ci sia qualcosa di sensato che sarebbe rotto analizzando il bejeebus dal codice e facendo qualcosa di completamente diverso. (come fare una collezione pigra) Python non ha mai promesso questa funzione, e molto probabilmente non lo avrà mai, ma posso vedere quel sogno nel design. 18 ott. 122012-10-18 07:11:03

  0

+1 per scale migliori. 19 lug. 132013-07-19 10:59:09

+11

Questo non 'scala meglio' rispetto alla versione 'zip (* x)'. 'zip (* x)' richiede solo un passaggio attraverso il ciclo e non utilizza gli elementi dello stack. 17 nov. 132013-11-17 16:38:20

+1

Se "scalare meglio" o meno dipende dal ciclo di vita dei dati originali rispetto ai dati trasposti. Questa risposta è migliore di usare 'zip' se il caso d'uso è che i dati trasposti sono usati e scartati immediatamente, mentre gli elenchi originali rimangono in memoria per molto più tempo. 15 nov. 152015-11-15 06:55:50


19

Se si dispone di elenchi che non hanno la stessa lunghezza, è possibile che non si desideri utilizzare zip come da risposta di Patricks. Questo funziona:

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

Ma con liste diverse di lunghezza, zip tronca ogni elemento per la lunghezza della lista più breve:

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

È possibile utilizzare carta con nessuna funzione di riempire i risultati vuote con nessuno:

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

zip() è leggermente più veloce però.

+3

interessante, puoi spiegare come funziona 'map'? 26 set. 132013-09-26 15:53:09

+4

Puoi anche usare 'izip_longest' 26 set. 132013-09-26 16:52:55

+2

Conosciuto come' zip_longest' per gli utenti python3. 08 mar. 162016-03-08 09:02:13

  0

@GrijeshChauhan So che questo è veramente vecchio, ma è una caratteristica incorporata strana: https://docs.python.org/2/library/functions.html#map "Se la funzione è Nessuno, si assume la funzione di identità; se ci sono più argomenti, map() restituisce una lista di tuple che contengono gli elementi corrispondenti da tutti gli iterabili (una sorta di operazione di trasposizione) Gli argomenti iterabili possono essere una sequenza o qualsiasi oggetto iterabile, il risultato è sempre una lista. " 14 lug. 172017-07-14 19:26:37

  0

@ cactus1 Grazie, utile, quella volta stavo imparando Python 15 lug. 172017-07-15 04:42:25


11

Mi piace usare zip(*iterable) (che è la parte di codice che stai cercando) nei miei programmi, come così:

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

trovo unzip più leggibile.


2

E 'solo un altro modo per farlo, ma mi ha aiutato molto in modo da scrivo qui:

Avere questa struttura dati:

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

Con conseguente:

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

Il il modo più poderoso di decomprimerlo e tornare all'originale è questo secondo me:

x,y=zip(*XY) 

Ma questa restituire una tupla quindi se avete bisogno di un array È possibile utilizzare:

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

Dà una tupla di liste come nella questione.

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

Spacchetta i due elenchi.