Функция транспонирования / распаковки (обратная сторона zip)?
138529 просмотра
13 ответа
У меня есть список кортежей из 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])
Есть ли встроенная функция, которая делает это?
Автор: Cristian Источник Размещён: 29.07.2019 08:53Ответы (13)
710 плюса
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
напрямую (после преобразования в кортеж), поэтому не нужно беспокоиться о том, что количество аргументов становится слишком большим.
26 плюса
Вы могли бы также сделать
result = ([ a for a,b in original ], [ b for a,b in original ])
Это должно масштабироваться лучше. Особенно, если Python преуспевает в том, чтобы не расширять список пониманий без необходимости.
(Между прочим, он создает 2-кортеж (пару) списков, а не список кортежей, как это zip
делает.)
Если генераторы вместо реальных списков в порядке, это будет сделано так:
result = (( a for a,b in original ), ( b for a,b in original ))
Генераторы не просматривают список, пока вы не запросите каждый элемент, но, с другой стороны, они сохраняют ссылки на исходный список.
Автор: Anders Eurenius Размещён: 24.08.2008 05:0720 плюса
Если у вас есть списки, которые не имеют одинаковую длину, вы можете не использовать 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), ('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 () немного быстрее.
Автор: Chris Размещён: 02.01.2011 12:1415 плюса
Мне нравится использовать zip(*iterable)
(это фрагмент кода, который вы ищете) в моих программах, так:
def unzip(iterable):
return zip(*iterable)
Я нахожу unzip
более читабельным.
12 плюса
>>> 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)]
Распаковывает два списка.
Автор: Noyer282 Размещён: 05.03.2016 11:084 плюса
Это всего лишь другой способ сделать это, но он мне очень помог, поэтому я пишу это здесь:
Имея эту структуру данных:
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')]
На мой взгляд, более питонный способ распаковать его и вернуться к оригиналу:
x,y=zip(*XY)
Но это возвращает кортеж, поэтому, если вам нужен список, вы можете использовать:
x,y=(list(x),list(y))
Автор: G M
Размещён: 26.01.2016 10:45
3 плюса
Наивный подход
def transpose_finite_iterable(iterable):
return zip(*iterable) # `itertools.izip` for Python 2 users
отлично работает для конечных итерируемых (например, последовательностей типа list
/ tuple
/ str
) (потенциально бесконечных) итерируемых, которые можно проиллюстрировать как
| |a_00| |a_10| ... |a_n0| |
| |a_01| |a_11| ... |a_n1| |
| |... | |... | ... |... | |
| |a_0i| |a_1i| ... |a_ni| |
| |... | |... | ... |... | |
где
n in ℕ
,a_ij
соответствует -омуj
элементуi
-й итерируемой,
и после подачи заявления transpose_finite_iterable
мы получаем
| |a_00| |a_01| ... |a_0i| ... |
| |a_10| |a_11| ... |a_1i| ... |
| |... | |... | ... |... | ... |
| |a_n0| |a_n1| ... |a_ni| ... |
Python пример такого случая , когда a_ij == j
,n == 2
>>> from itertools import count
>>> iterable = [count(), count()]
>>> result = transpose_finite_iterable(iterable)
>>> next(result)
(0, 0)
>>> next(result)
(1, 1)
Но мы не можем использовать transpose_finite_iterable
снова, чтобы вернуться к структуре оригинала, iterable
потому что result
это бесконечная итерация конечных итераций ( tuple
в нашем случае s):
>>> transpose_finite_iterable(result)
... hangs ...
Traceback (most recent call last):
File "...", line 1, in ...
File "...", line 2, in transpose_finite_iterable
MemoryError
Итак, как мы можем справиться с этим делом?
... и вот идет deque
После того, как мы посмотрим на документы по itertools.tee
функциям , есть рецепт Python, который с некоторой модификацией может помочь в нашем случае
def transpose_finite_iterables(iterable):
iterator = iter(iterable)
try:
first_elements = next(iterator)
except StopIteration:
return ()
queues = [deque([element])
for element in first_elements]
def coordinate(queue):
while True:
if not queue:
try:
elements = next(iterator)
except StopIteration:
return
for sub_queue, element in zip(queues, elements):
sub_queue.append(element)
yield queue.popleft()
return tuple(map(coordinate, queues))
давай проверим
>>> from itertools import count
>>> iterable = [count(), count()]
>>> result = transpose_finite_iterables(transpose_finite_iterable(iterable))
>>> result
(<generator object transpose_finite_iterables.<locals>.coordinate at ...>, <generator object transpose_finite_iterables.<locals>.coordinate at ...>)
>>> next(result[0])
0
>>> next(result[0])
1
Синтез
Теперь мы можем определить общую функцию для работы с итерациями итерируемых, одни из которых конечны, а другие потенциально бесконечны, используя functools.singledispatch
декоратор, такой как
from collections import (abc,
deque)
from functools import singledispatch
@singledispatch
def transpose(object_):
"""
Transposes given object.
"""
raise TypeError('Unsupported object type: {type}.'
.format(type=type))
@transpose.register(abc.Iterable)
def transpose_finite_iterables(object_):
"""
Transposes given iterable of finite iterables.
"""
iterator = iter(object_)
try:
first_elements = next(iterator)
except StopIteration:
return ()
queues = [deque([element])
for element in first_elements]
def coordinate(queue):
while True:
if not queue:
try:
elements = next(iterator)
except StopIteration:
return
for sub_queue, element in zip(queues, elements):
sub_queue.append(element)
yield queue.popleft()
return tuple(map(coordinate, queues))
def transpose_finite_iterable(object_):
"""
Transposes given finite iterable of iterables.
"""
yield from zip(*object_)
try:
transpose.register(abc.Collection, transpose_finite_iterable)
except AttributeError:
# Python3.5-
transpose.register(abc.Mapping, transpose_finite_iterable)
transpose.register(abc.Sequence, transpose_finite_iterable)
transpose.register(abc.Set, transpose_finite_iterable)
который можно рассматривать как свой собственный обратный (математики называют этот вид функций «инволюциями» ) в классе бинарных операторов над конечными непустыми итерациями.
В качестве бонуса singledispatch
мы можем обрабатывать numpy
массивы, такие как
import numpy as np
...
transpose.register(np.ndarray, np.transpose)
а затем использовать его как
>>> array = np.arange(4).reshape((2,2))
>>> array
array([[0, 1],
[2, 3]])
>>> transpose(array)
array([[0, 2],
[1, 3]])
Заметка
Поскольку transpose
возвращает итераторы , и если кто - то хочет иметь tuple
в list
с , как в OP - это может быть дополнительно со map
встроенной функцией , как
>>> original = [('a', 1), ('b', 2), ('c', 3), ('d', 4)]
>>> tuple(map(list, transpose(original)))
(['a', 'b', 'c', 'd'], [1, 2, 3, 4])
Реклама
Я добавил обобщенное решение для lz
пакета из 0.5.0
версии, которая может быть использована как
>>> from lz.transposition import transpose
>>> list(map(tuple, transpose(zip(range(10), range(10, 20)))))
[(0, 1, 2, 3, 4, 5, 6, 7, 8, 9), (10, 11, 12, 13, 14, 15, 16, 17, 18, 19)]
PS
Не существует решения (по крайней мере, очевидного) для обработки потенциально бесконечной итерируемой потенциально потенциально бесконечной итерации, но этот случай менее распространен.
Автор: Azat Ibrakov Размещён: 21.12.2018 12:461 плюс
Поскольку он возвращает кортежи (и может использовать тонны памяти), zip(*zipped)
уловка кажется мне более умной, чем полезной.
Вот функция, которая на самом деле даст вам обратную сторону zip.
def unzip(zipped):
"""Inverse of built-in zip function.
Args:
zipped: a list of tuples
Returns:
a tuple of lists
Example:
a = [1, 2, 3]
b = [4, 5, 6]
zipped = list(zip(a, b))
assert zipped == [(1, 4), (2, 5), (3, 6)]
unzipped = unzip(zipped)
assert unzipped == ([1, 2, 3], [4, 5, 6])
"""
unzipped = ()
if len(zipped) == 0:
return unzipped
dim = len(zipped[0])
for i in range(dim):
unzipped = unzipped + ([tup[i] for tup in zipped], )
return unzipped
Автор: Waylon Flinn
Размещён: 11.06.2018 01:35
1 плюс
original = [('a', 1), ('b', 2), ('c', 3), ('d', 4)]
#unzip
a1 , a2 = zip(*original)
#make tuple with two list
result=(list(a1),list(a2))
result
Автор: Aditya kumar Размещён: 12.04.2019 03:18результат = (['a', 'b', 'c', 'd'], [1, 2, 3, 4])
1 плюс
Попробуйте использовать more_itertools.unzip :
>>> from more_itertools import unzip
>>> original = [('a', 1), ('b', 2), ('c', 3), ('d', 4)]
>>> [list(x) for x in unzip(original)]
[['a', 'b', 'c', 'd'], [1, 2, 3, 4]]
Автор: Neil G
Размещён: 02.01.2019 09:30
0 плюса
Ни один из предыдущих ответов эффективно не обеспечивает требуемый вывод, который является кортежем списков , а не списком кортежей . Для первого вы можете использовать tuple
с map
. Вот разница:
res1 = list(zip(*original)) # [('a', 'b', 'c', 'd'), (1, 2, 3, 4)]
res2 = tuple(map(list, zip(*original))) # (['a', 'b', 'c', 'd'], [1, 2, 3, 4])
Кроме того, большинство предыдущих решений предполагает Python 2.7, где zip
возвращает список, а не итератор.
Для Python 3.x вам нужно будет передать результат в функцию, например, list
или tuple
исчерпать итератор. Для итераторов с эффективным использованием памяти вы можете опустить внешние list
и tuple
вызовы соответствующих решений.
0 плюса
Хотя zip(*seq)
это очень полезно, оно может быть непригодным для очень длинных последовательностей, поскольку оно создаст кортеж значений для передачи. Например, я работал с системой координат с более чем миллионом записей и нашел, что ее создание значительно быстрее последовательности напрямую.
Общий подход будет выглядеть примерно так:
from collections import deque
seq = ((a1, b1, …), (a2, b2, …), …)
width = len(seq[0])
output = [deque(len(seq))] * width # preallocate memory
for element in seq:
for s, item in zip(output, element):
s.append(item)
Но, в зависимости от того, что вы хотите сделать с результатом, выбор коллекции может иметь большое значение. В моем реальном случае использования наборы без внутреннего цикла заметно быстрее всех других подходов.
И, как уже отмечали другие, если вы делаете это с наборами данных, может иметь смысл вместо этого использовать коллекции Numpy или Pandas.
Автор: Charlie Clark Размещён: 26.09.2018 02:08-1 плюса
Вот как вы можете переместить кортеж 2x4 в кортеж 4x2.
>>> tuple(zip(*[('a', 1), ('b', 2), ('c', 3), ('d', 4)]))
результат
[('a', 'b', 'c', 'd'), (1, 2, 3, 4)]
Автор: helcode
Размещён: 30.06.2018 03:23
Вопросы из категории :
- python Обработка XML в Python
- python Как я могу использовать Python itertools.groupby ()?
- python Python: На какой ОС я работаю?
- python Как я могу создать непосредственно исполняемое кроссплатформенное приложение с графическим интерфейсом на Python?
- python Вызов функции модуля с использованием его имени (строки)
- list Функция транспонирования / распаковки (обратная сторона zip)?
- list How would you make a comma-separated string from a list of strings?
- list Удалить дубликаты из списка <T> в C #
- list Console.WriteLine и общий список
- list Как проверить, если список пуст?
- matrix Как вы вращаете двумерный массив?
- matrix Как мне перебрать каждый элемент в n-мерной матрице в MATLAB?
- matrix Компактная матричная индексация MATLAB
- matrix How do I resize a matrix in MATLAB?
- transpose Эффективный способ транспонировать файл в Bash
- transpose Транспонирование и перенос строки в java
- transpose Как транспонировать матрицу в прологе
- transpose Javascript эквивалент функции zip в Python