使用zip()旋转列表

时间:2012-05-21 15:30:02

标签: python list

我有一组以下列形式从数据库返回的记录:

data = [
    ["date", "value1a", "value2a", "value3a", ...],
    ["date", "value1b", "value2b", "value3b", ...]
]

我想将这组行转换为像

这样的列表
[
    [("date", "value1a"), ("date", "value1b"), ... ],
    [("date", "value2a"), ("date", "value2b"), ... ]
]

我知道zip()会做这类事情,但我不清楚如何将日期记录到每条记录中(并使它们成为元组)。从数据库返回的行的长度并不总是相同,但我知道每次调用的预期长度。

4 个答案:

答案 0 :(得分:4)

data = [["date_a", "1a", "2a", "3a"], 
        ["date_b", "1b", "2b", "3b"]]

print zip(*(zip(itertools.repeat(ls[0]), ls[1:]) for ls in data))

给出

[(('date_a', '1a'), ('date_b', '1b')),
 (('date_a', '2a'), ('date_b', '2b')),
 (('date_a', '3a'), ('date_b', '3b'))]

请参阅注释以了解一些有用的变体。

答案 1 :(得分:2)

对第一个元素使用itertools repeat:

zip(itertools.repeat(ls[0]), ls[1:])

答案 2 :(得分:2)

编辑:这是基于原始问题,假设数据将在单个列表中,而不是多个输入列表。对问题的编辑已经明确表明情况并非如此,因此我建议您关注Janne Karila's solution

假设您知道有多少不同的值,这是一个很好的解决方案,使用itertools'grouper() recipe

import itertools

def grouper(n, iterable, fillvalue=None):
     args = [iter(iterable)] * n
     return itertools.zip_longest(fillvalue=fillvalue, *args)

data = ["date", "1a", "2a", "3a", "1b", "2b", "3b", "1c", "2c", "3c"]
first = data.pop(0)
print([list(zip(itertools.repeat(first), items)) for items in zip(*grouper(3, data))])

给我们:

[
    [('date', '1a'), ('date', '1b'), ('date', '1c')], 
    [('date', '2a'), ('date', '2b'), ('date', '2c')], 
    [('date', '3a'), ('date', '3b'), ('date', '3c')]
]

请注意,如果没有足够的值,这将使用None s填充列表。

当然,如果只是想循环它,你可以使用a generator comprehension instead of a list comprehension,例如,不显示它。 E.g:

(zip(itertools.repeat(first), items) for items in zip(*grouper(3, data)))

请注意,我使用的是Python 3.x,因此在2.x下,无论我在哪里使用zip(),您可能需要itertools.izip(),而itertools.zip_longest()会变为itertools.izip_longest()。< / p>

请注意更好的方法 - 假设您知道定义应该在第一个列表中的值应该在第二个列表中的内容等等...

import itertools
import operator

data = ["date", "1a", "2a", "3a", "1b", "2b", "3b", "1c", "2c", "3c"]
first = data.pop(0)

print([list(zip(itertools.repeat(first), items)) for _, items in itertools.groupby(sorted(data), operator.itemgetter(0))])

产生:

[
    [('date', '1a'), ('date', '1b'), ('date', '1c')], 
    [('date', '2a'), ('date', '2b'), ('date', '2c')], 
    [('date', '3a'), ('date', '3b'), ('date', '3c')]
]

当然,这仅适用于给定的示例,假设您的实际数据不同,您需要将operator.itemgetter(0)更改为定义项目应归入哪个列表的函数。

请注意,我们可能值得封装我们的前缀工作:

def prefix(iterable, prefix):
    """Returns every element of an iterable prefixed with a given value."""
    #prefix("ABCDEFG", "x") --> ("x", "A"), ("x", "B"), ("x", "C"), ("x", "D"), ...
    return zip(itertools.repeat(prefix), iterable)

然后我们简单地说:

(prefix(items, first) for items in zip(*grouper(3, data)))

(prefix(items, first) for _, items in itertools.groupby(sorted(data), operator.itemgetter(0)))

哪些更具可读性。

答案 3 :(得分:0)

这也可以通过简单的列表理解技术来实现

data = [    ["date1", "value1a1", "value2a1", "value3a1"],
            ["date2", "value1b2", "value2b2", "value3b2"]
       ]
result = map(list, zip(*[[(x[0], x[i]) for x in data] for i in range(1,len(x))]))

[
  [('date1', 'value1a1'), ('date1', 'value2a1'), ('date1', 'value3a1')],
  [('date2', 'value1b2'), ('date2', 'value2b2'), ('date2', 'value3b2')]
]