我有一个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])
有没有内置函数可以做到这一点?
我有一个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])
有没有内置函数可以做到这一点?
当前回答
虽然numpy数组和pandas可能更可取,但此函数在作为unzip(args)调用时模仿zip(*args)的行为。
允许生成器(如Python 3中zip的结果)在遍历值时作为参数传递。
def unzip(items, cls=list, ocls=tuple):
"""Zip function in reverse.
:param items: Zipped-like iterable.
:type items: iterable
:param cls: Container factory. Callable that returns iterable containers,
with a callable append attribute, to store the unzipped items. Defaults
to ``list``.
:type cls: callable, optional
:param ocls: Outer container factory. Callable that returns iterable
containers. with a callable append attribute, to store the inner
containers (see ``cls``). Defaults to ``tuple``.
:type ocls: callable, optional
:returns: Unzipped items in instances returned from ``cls``, in an instance
returned from ``ocls``.
"""
# iter() will return the same iterator passed to it whenever possible.
items = iter(items)
try:
i = next(items)
except StopIteration:
return ocls()
unzipped = ocls(cls([v]) for v in i)
for i in items:
for c, v in zip(unzipped, i):
c.append(v)
return unzipped
要使用列表容器,只需运行unzip(zip),如
unzip(zip(["a","b","c"],[1,2,3])) == (["a","b","c"],[1,2,3])
要使用deques或其他带有附加的容器,需要传递一个工厂函数。
from collections import deque
unzip([("a",1),("b",2)], deque, list) == [deque(["a","b"]),deque([1,2])]
(装饰cls和/或main_cls以微管理容器初始化,如上面最后的assert语句所简单显示的那样。)
其他回答
虽然numpy数组和pandas可能更可取,但此函数在作为unzip(args)调用时模仿zip(*args)的行为。
允许生成器(如Python 3中zip的结果)在遍历值时作为参数传递。
def unzip(items, cls=list, ocls=tuple):
"""Zip function in reverse.
:param items: Zipped-like iterable.
:type items: iterable
:param cls: Container factory. Callable that returns iterable containers,
with a callable append attribute, to store the unzipped items. Defaults
to ``list``.
:type cls: callable, optional
:param ocls: Outer container factory. Callable that returns iterable
containers. with a callable append attribute, to store the inner
containers (see ``cls``). Defaults to ``tuple``.
:type ocls: callable, optional
:returns: Unzipped items in instances returned from ``cls``, in an instance
returned from ``ocls``.
"""
# iter() will return the same iterator passed to it whenever possible.
items = iter(items)
try:
i = next(items)
except StopIteration:
return ocls()
unzipped = ocls(cls([v]) for v in i)
for i in items:
for c, v in zip(unzipped, i):
c.append(v)
return unzipped
要使用列表容器,只需运行unzip(zip),如
unzip(zip(["a","b","c"],[1,2,3])) == (["a","b","c"],[1,2,3])
要使用deques或其他带有附加的容器,需要传递一个工厂函数。
from collections import deque
unzip([("a",1),("b",2)], deque, list) == [deque(["a","b"]),deque([1,2])]
(装饰cls和/或main_cls以微管理容器初始化,如上面最后的assert语句所简单显示的那样。)
如果您的列表长度不相同,您可能不希望按照patrick的回答使用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')]
你可以使用map没有函数来填充空结果为None:
>>> map(None, *[('a', 1), ('b', 2), ('c', 3), ('d', 4), ('e', )])
[('a', 'b', 'c', 'd', 'e'), (1, 2, 3, 4, None)]
不过Zip()稍微快一点。
要获得一个列表的元组,如问题中所示:
>>> 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)]
前面的答案都没有有效地提供所需的输出,即一个由列表组成的元组,而不是由元组组成的列表。对于前者,您可以使用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等函数以耗尽迭代器。对于内存效率高的迭代器,可以忽略各自解的外部列表和元组调用。
虽然zip(*seq)非常有用,但它可能不适合非常长的序列,因为它将创建一个值的元组来传递。例如,我一直在使用一个拥有超过100万个条目的坐标系,并发现直接创建序列要快得多。
一般的方法是这样的:
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集合可能是有意义的。