Transpose/Unzip Function (inverse of zip)?
我有一个2项元组的列表,我想将它们转换为2个列表,其中第一个列表包含每个元组中的第一个项,第二个列表包含第二个项。
例如:
1 2 3 | original = [('a', 1), ('b', 2), ('c', 3), ('d', 4)] # and I want to become... result = (['a', 'b', 'c', 'd'], [1, 2, 3, 4]) |
是否有一个内置函数可以做到这一点?
1 2 | >>> zip(*[('a', 1), ('b', 2), ('c', 3), ('d', 4)]) [('a', 'b', 'c', 'd'), (1, 2, 3, 4)] |
其工作方式是通过以下参数调用
1 | zip(('a', 1), ('b', 2), ('c', 3), ('d', 4)) |
号
…除了参数直接传递给
你也可以
1 | result = ([ a for a,b in original ], [ b for a,b in original ]) |
它应该有更好的伸缩性。尤其是如果python在不扩展列表理解方面做得很好,除非需要。
(顺便说一下,它是一对列表,而不是像
如果生成器(而不是实际列表)正常,则可以这样做:
1 | result = (( a for a,b in original ), ( b for a,b in original )) |
。
在您请求每个元素之前,生成器不会仔细浏览列表,但在另一方面,它们保留对原始列表的引用。
如果您的列表长度不相同,您可能不想按照帕特里克的回答使用zip。这项工作:
1 2 | >>> zip(*[('a', 1), ('b', 2), ('c', 3), ('d', 4)]) [('a', 'b', 'c', 'd'), (1, 2, 3, 4)] |
但是,对于不同的长度列表,zip会将每个项目截断为最短列表的长度:
1 2 | >>> zip(*[('a', 1), ('b', 2), ('c', 3), ('d', 4), ('e', )]) [('a', 'b', 'c', 'd', 'e')] |
。
可以使用不带函数的map将空结果填充为无:
1 2 | >>> map(None, *[('a', 1), ('b', 2), ('c', 3), ('d', 4), ('e', )]) [('a', 'b', 'c', 'd', 'e'), (1, 2, 3, 4, None)] |
。
不过,zip()速度稍快。
我喜欢在我的程序中使用
1 2 | def unzip(iterable): return zip(*iterable) |
我发现
1 2 3 | >>> 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]) |
号
给出问题中的一组列表。
1 | list1, list2 = [list(tup) for tup in zip(*original)] |
号
打开两个列表。
这只是另一种方法,但它对我有很大帮助,所以我在这里写了:
具有这种数据结构:
1 2 3 | X=[1,2,3,4] Y=['a','b','c','d'] XY=zip(X,Y) |
。
导致:
1 2 | In: XY Out: [(1, 'a'), (2, 'b'), (3, 'c'), (4, 'd')] |
在我看来,解压并回到原版的更为Python式的方法是:
1 | x,y=zip(*XY) |
。
但这会返回一个元组,因此如果需要列表,可以使用:
1 | x,y=(list(x),list(y)) |
号
天真的方法
1 2 | def transpose_finite_iterable(iterable): return zip(*iterable) # `itertools.izip` for Python 2 users |
对于有限不可迭代(如
1 2 3 4 5 | | |a_00| |a_10| ... |a_n0| | | |a_01| |a_11| ... |a_n1| | | |... | |... | ... |... | | | |a_0i| |a_1i| ... |a_ni| | | |... | |... | ... |... | | |
号
哪里
n in ? ,a_ij 对应于i th iterable的j th元素,
应用
1 2 3 4 | | |a_00| |a_01| ... |a_0i| ... | | |a_10| |a_11| ... |a_1i| ... | | |... | |... | ... |... | ... | | |a_n0| |a_n1| ... |a_ni| ... | |
python示例,其中
1 2 3 4 5 6 7 | >>> from itertools import count >>> iterable = [count(), count()] >>> result = transpose_finite_iterable(iterable) >>> next(result) (0, 0) >>> next(result) (1, 1) |
。
但是我们不能再用
1 2 3 4 5 6 | >>> transpose_finite_iterable(result) ... hangs ... Traceback (most recent call last): File"...", line 1, in ... File"...", line 2, in transpose_finite_iterable MemoryError |
那么我们如何处理这个案子呢?
…江户一号〔14〕来了。在我们看了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 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)) |
。
让我们检查一下
1 2 3 4 5 6 7 8 9 | >>> 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 |
。合成
现在我们可以定义一般的函数来处理iterables的iterables,其中一个是有限的,另一个可能是无限的,使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | 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) |
在有限非空iterables上的二元算子类中,它可以被看作是它自己的逆(数学家称这种函数为"对合")。
作为
1 2 3 | import numpy as np ... transpose.register(np.ndarray, np.transpose) |
。
然后像这样使用
1 2 3 4 5 6 7 | >>> array = np.arange(4).reshape((2,2)) >>> array array([[0, 1], [2, 3]]) >>> transpose(array) array([[0, 2], [1, 3]]) |
注释
由于
1 2 3 | >>> original = [('a', 1), ('b', 2), ('c', 3), ('d', 4)] >>> tuple(map(list, transpose(original))) (['a', 'b', 'c', 'd'], [1, 2, 3, 4]) |
。广告
我在
1 2 3 | >>> 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)] |
号附笔。
没有解决方案(至少是显而易见的)来处理潜在的无限不可迭代的无限不可迭代的无限不可迭代,但这种情况并不常见。
1 2 3 4 5 6 7 | 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 |
号
result=(['a', 'b', 'c', 'd'], [1, 2, 3, 4])
号
考虑使用更多的itertools.unzip:
1 2 3 4 | >>> 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]] |
号
因为它返回元组(并且可以使用大量的内存),所以在我看来,
这是一个函数,它实际上会给你zip的倒数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | 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 |
号
虽然
一般方法如下:
1 2 3 4 5 6 7 | 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集合可能是有意义的。
前面的答案都不能有效地提供所需的输出,即列表的元组,而不是元组的列表。对于前者,您可以使用
1 2 | 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,其中
对于python 3.x,您需要将结果传递给诸如
这就是如何将2x4元组转换为4x2元组的方法。
1 | >>> tuple(zip(*[('a', 1), ('b', 2), ('c', 3), ('d', 4)])) |
号
结果
1 | [('a', 'b', 'c', 'd'), (1, 2, 3, 4)] |
号