Функция транспонирования/разворачивания (инверсия zip)?


342

У меня есть список кортежей из 2 предметов, и я бы хотел их преобразовать в 2 списка, где первый содержит первый элемент в каждом кортеже, а второй список содержит второй элемент.

Например:

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

Есть встроенная функция, которая делает это?

+3

Отличные ответы ниже, но также посмотрите на [numpy's transpose] (http://docs.scipy.org/doc/numpy/reference/generated/numpy.transpose.html) 03 мар. 152015-03-03 13:17:26

+1

Посмотрите на этот хороший ответ, чтобы сделать то же самое с генераторы вместо списка: [how-to-unzip-an-iterator] (http://stackoverflow.com/questions/30805000/how-to-unzip-an-iterator) 05 янв. 162016-01-05 20:31:56

546

zip - его собственный инверсный! Если вы используете специальный * оператор.

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

Как это работает путем вызова zip с аргументами:

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

... кроме аргументов, переданных zip непосредственно (после преобразования в кортеж), поэтому нет необходимости беспокоиться о количестве аргументов, слишком больших.

+6

О, если бы все было так просто. Разархивировать 'zip ([], [])' этот путь не дает вам '[], []'. Он получает вас '[]'. Если только ... 24 фев. 142014-02-24 12:06:58

  0

@ user2357112 он дает вам 'zip (* zip ([list1], [list2]))' дает вам '([list1, list2])'. 25 фев. 142014-02-25 21:50:13

  0

@cdhagmann: 'zip ([list1], [list2])' никогда не то, что вы хотите. Это просто дает вам '[(list1, list2)]'. 25 фев. 142014-02-25 22:05:47

  0

@ user2357112 Я использовал '[list1]' для обозначения любого списка с именем list1, а не как список со списком с одним списком в качестве записи. Поэтому задано 'list1 = [1,2,3,4]' и 'list2 = [1,2,3,4]' then 'zip (* zip (list1, list2))' дает вам '([1 , 2,3,4], [1,2,3,4]) ' 25 фев. 142014-02-25 22:31:06

+1

@cdhagmann: Теперь попробуйте это с помощью' list1 = []; песни2 = [] '. 26 фев. 142014-02-26 02:53:43

  0

@cdhagmann вы получаете [(1, 2, 3, 4), (1, 2, 3, 4)] из своих команд. 03 июл. 142014-07-03 13:07:52

+1

Это не работает в Python3. См. Http://stackoverflow.com/questions/24590614/python3-unzipping-a-list-of-tuples 05 июл. 142014-07-05 21:35:16

  0

zip не сохраняет элементы в более длинных итерациях, поэтому требуется заполнение 21 сен. 142014-09-21 06:03:50

  0

'tuple (map (list, zip (* оригинал))) ', чтобы получить именно указанный результат. 30 янв. 152015-01-30 04:26:57

+4

@Tommy Это неверно. 'zip' работает точно так же в Python 3, за исключением того, что он возвращает итератор вместо списка. Чтобы получить тот же результат, что и выше, вам просто нужно обернуть zip-вызов в списке: 'list (zip (* [('a', 1), ('b', 2), ('c', 3), ('d', 4)])) 'будет выводить' [('a', 'b', 'c', 'd'), (1, 2, 3, 4)] ' 11 мар. 152015-03-11 14:11:12

+1

уведомление: вы могут справиться с проблемами памяти и производительности с очень длинными списками. 14 окт. 162016-10-14 12:44:09

  0

Это фантастический ответ. :) 16 май. 172017-05-16 19:00:59


22

Вы также могли бы сделать

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

It должен масштаб лучше. Особенно, если Python делает все возможное, чтобы не расширять список, если это необходимо.

(кстати, он делает 2-кортеж (пара) списков, а не список кортежей, как zip делает.)

Если генераторы вместо фактических списков в порядке, это будет сделать это:

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

Генераторы не пробиваются по списку, пока вы не спросите о каждом элементе, но, с другой стороны, они сохраняют ссылки на исходный список.

+6

«Особенно, если Python делает все возможное, чтобы не расширять список, если это не нужно». mmm ... нормально, списки размножаются сразу же - или я что-то не так понял? 15 авг. 112011-08-15 19:52:57

  0

@glglgl: Нет, вы, вероятно, правы. Я просто надеялся, что какая-то будущая версия может начать поступать правильно. (Невозможно изменить, семантика побочных эффектов, которая нуждается в изменениях, вероятно, уже разочарована.) 15 окт. 122012-10-15 12:54:03

+8

Что вы надеетесь получить, является выражением генератора, которое уже существует. 15 окт. 122012-10-15 13:12:19

  0

Нет, я надеюсь получить многолетний любимый «достаточно умный компилятор» (или интерпретатор в этом случае). Я не думаю, что есть что-то разумное, что было бы сломано, анализируя bejeebus из кода и делая что-то совершенно другое. (например, создание ленивой коллекции) Python никогда не обещал эту функцию, и, скорее всего, ее никогда не будет, но я вижу эту мечту в дизайне. 18 окт. 122012-10-18 07:11:03

  0

+1 для весов лучше. 19 июл. 132013-07-19 10:59:09

+11

Это не «масштабируется лучше», чем версия 'zip (* x)'. 'zip (* x)' требует только одного прохода через цикл и не использует элементы стека. 17 ноя. 132013-11-17 16:38:20

+1

Является ли оно «масштабируется лучше» или не зависит от жизненного цикла исходных данных по сравнению с транспонированными данными. Этот ответ лучше, чем использование 'zip', если прецедентом является то, что транспонированные данные используются и отбрасываются немедленно, в то время как исходные списки остаются в памяти намного дольше. 15 ноя. 152015-11-15 06:55:50


19

Если у вас есть списки не одинаковой длины, вы можете не использовать zip в соответствии с ответом Патрика. Это работает:

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

Но с различными списками длины, застежка-молния обрежет каждый элемент на длину кратчайшего списка:

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

Вы можете использовать карту без функции, чтобы заполнить пустые результаты с None:

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

zip() немного быстрее, хотя.

+3

интересно, можете ли вы объяснить, как работает «карта»? 26 сен. 132013-09-26 15:53:09

+4

Вы также можете использовать 'izip_longest' 26 сен. 132013-09-26 16:52:55

+2

Известный как' zip_longest' для пользователей python3. 08 мар. 162016-03-08 09:02:13

  0

@GrijeshChauhan Я знаю, что это действительно старый, но это странная встроенная функция: https://docs.python.org/2/library/functions.html#map «Если функция None, предполагается функция идентификации; если существует несколько аргументов, map() возвращает список, состоящий из кортежей, содержащих соответствующие элементы из всех итераций (вид операции транспонирования). Итерируемые аргументы могут быть последовательностью или любым итерируемым объектом, результатом всегда является список ». 14 июл. 172017-07-14 19:26:37

  0

@ cactus1 Спасибо, полезно, что я изучал Python 15 июл. 172017-07-15 04:42:25


11

Мне нравится использовать zip(*iterable) (который является частью кода, который вы ищете) в моих программах, так:

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

Я нахожу unzip более читаемым.


2

Это только один способ сделать это, но это помогло мне, так что я пишу это здесь:

Имея такую ​​структуру данных:

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

Итоговое в:

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

более pythonic способ распаковать его и вернуться к оригиналу, это, на мой взгляд,

x,y=zip(*XY) 

Но это возвращает кортеж, так что если вам нужен массив, который вы можете использовать:

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

дает кортеж списков, как в этом вопросе.

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

Распаковывает два списка.