转置/解压缩功能(zip的反转)?

Cri*_*ian 480 python transpose list matrix

我有一个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])
Run Code Online (Sandbox Code Playgroud)

是否有内置函数可以做到这一点?

Pat*_*ick 739

zip是它自己的逆!如果您使用特殊*运算符.

>>> zip(*[('a', 1), ('b', 2), ('c', 3), ('d', 4)])
[('a', 'b', 'c', 'd'), (1, 2, 3, 4)]
Run Code Online (Sandbox Code Playgroud)

这种方式的工作方式是zip使用参数调用:

zip(('a', 1), ('b', 2), ('c', 3), ('d', 4))
Run Code Online (Sandbox Code Playgroud)

...除了参数zip直接传递给(在转换为元组之后),所以不必担心参数的数量太大.

  • @Tommy这是不正确的.`zip`在Python 3中的工作方式完全相同,只是它返回迭代器而不是列表.为了得到与上面相同的输出,你只需要将zip调用包装在一个列表中:`list(zip(*[('a',1),('b',2),('c',3 ),('d',4)]))`将输出`[('a','b','c','d'),(1,2,3,4)]` (25认同)
  • 哦,如果只是这么简单.解压缩`zip([],[])`这种方式不会让你`[],[]`.它会让你`[]`.要是... (18认同)
  • 这在Python3中不起作用.请参阅:http://stackoverflow.com/questions/24590614/python3-unzipping-a-list-of-tuples (4认同)
  • 注意:您可以通过很长的列表来解决内存和性能问题. (4认同)
  • @cdhagmann:现在尝试使用`list1 = []; list2 = []`。 (2认同)

And*_*ius 26

你也可以这样做

result = ([ a for a,b in original ], [ b for a,b in original ])
Run Code Online (Sandbox Code Playgroud)

应该更好地扩展.特别是如果Python不擅长扩展列表推导,除非需要.

(顺便说一句,它产生了一个2元组(对)列表,而不是像元组列表那样zip.)

如果生成器而不是实际列表都可以,那么这样做:

result = (( a for a,b in original ), ( b for a,b in original ))
Run Code Online (Sandbox Code Playgroud)

在您询问每个元素之前,生成器不会遍历列表,但另一方面,它们会保留对原始列表的引用.

  • 这不比'zip(*x)`版本"扩展得更好".`zip(*x)`只需要一次遍历循环,并且不会使用堆栈元素. (12认同)
  • 你希望得到的是一个生成器表达 - 它已经存在. (8认同)
  • "特别是如果Python不擅长扩展列表推导,除非需要." 嗯...通常,列表理解会立即扩展 - 或者我是否出错了? (7认同)

小智 21

如果您的列表长度不同,则可能不希望按照Patricks的说法使用zip.这有效:

>>> zip(*[('a', 1), ('b', 2), ('c', 3), ('d', 4)])
[('a', 'b', 'c', 'd'), (1, 2, 3, 4)]
Run Code Online (Sandbox Code Playgroud)

但是使用不同的长度列表,zip会将每个项目截断为最短列表的长度:

>>> zip(*[('a', 1), ('b', 2), ('c', 3), ('d', 4), ('e', )])
[('a', 'b', 'c', 'd', 'e')]
Run Code Online (Sandbox Code Playgroud)

您可以使用没有函数的map来填充空结果,而不是:

>>> map(None, *[('a', 1), ('b', 2), ('c', 3), ('d', 4), ('e', )])
[('a', 'b', 'c', 'd', 'e'), (1, 2, 3, 4, None)]
Run Code Online (Sandbox Code Playgroud)

zip()虽然略快.

  • 你也可以使用`izip_longest` (4认同)
  • 有趣的是,你能解释一下`map`是如何工作的吗? (3认同)
  • 对于python3用户称为"zip_longest". (3认同)

was*_*ans 15

我喜欢zip(*iterable)在我的程序中使用(这是你正在寻找的代码段),如下所示:

def unzip(iterable):
    return zip(*iterable)
Run Code Online (Sandbox Code Playgroud)

我发现unzip更具可读性.


Noy*_*282 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])
Run Code Online (Sandbox Code Playgroud)

在问题中给出一个列表元组.

list1, list2 = [list(tup) for tup in zip(*original)]
Run Code Online (Sandbox Code Playgroud)

解压缩两个列表.


Aza*_*kov 8

天真的方法

def transpose_finite_iterable(iterable):
    return zip(*iterable)  # `itertools.izip` for Python 2 users
Run Code Online (Sandbox Code Playgroud)

工作正常的有限可迭代(例如序列像list/ tuple/ str)的(可能是无限的)iterables可以像图示

| |a_00| |a_10| ... |a_n0| |
| |a_01| |a_11| ... |a_n1| |
| |... | |... | ... |... | |
| |a_0i| |a_1i| ... |a_ni| |
| |... | |... | ... |... | |
Run Code Online (Sandbox Code Playgroud)

在哪里

  • n in ?,
  • a_ij对应于-th iterable 的j-th 元素i

申请后transpose_finite_iterable我们得到

| |a_00| |a_01| ... |a_0i| ... |
| |a_10| |a_11| ... |a_1i| ... |
| |... | |... | ... |... | ... |
| |a_n0| |a_n1| ... |a_ni| ... |
Run Code Online (Sandbox Code Playgroud)

这种情况的 Python 示例,其中a_ij == jn == 2

>>> from itertools import count
>>> iterable = [count(), count()]
>>> result = transpose_finite_iterable(iterable)
>>> next(result)
(0, 0)
>>> next(result)
(1, 1)
Run Code Online (Sandbox Code Playgroud)

但是我们不能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
Run Code Online (Sandbox Code Playgroud)

那么我们该如何处理这个案子呢?

......这里来了 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))
Run Code Online (Sandbox Code Playgroud)

让我们检查

>>> 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
Run Code Online (Sandbox Code Playgroud)

合成

现在我们可以定义通用函数来处理迭代的迭代,其中一些是有限的,另一个可能是无限的,使用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)
Run Code Online (Sandbox Code Playgroud)

它可以被认为是它自己的逆(数学家称这种函数为“对合”),属于有限非空迭代的二元运算符类。


作为singledispatching 的一个好处,我们可以处理numpy像这样的数组

import numpy as np
...
transpose.register(np.ndarray, np.transpose)
Run Code Online (Sandbox Code Playgroud)

然后像这样使用它

>>> array = np.arange(4).reshape((2,2))
>>> array
array([[0, 1],
       [2, 3]])
>>> transpose(array)
array([[0, 2],
       [1, 3]])
Run Code Online (Sandbox Code Playgroud)

笔记

由于transpose返回迭代器,并且如果有人想在 OP 中使用tupleof lists - 这可以使用map内置函数进行额外制作例如

>>> original = [('a', 1), ('b', 2), ('c', 3), ('d', 4)]
>>> tuple(map(list, transpose(original)))
(['a', 'b', 'c', 'd'], [1, 2, 3, 4])
Run Code Online (Sandbox Code Playgroud)

广告

我已经添加推广解决方案lz0.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)]
Run Code Online (Sandbox Code Playgroud)

聚苯乙烯

没有解决方案(至少是显而易见的)来处理潜在无限迭代的潜在无限迭代,但这种情况不太常见。