在Python中创建另一个列表的相邻元素的列表

时间:2017-08-17 19:45:45

标签: python list iteration

我希望将列表作为输入,然后创建另一个列表,其中包含原始列表中相邻元素的元组(或子列表),包含起始元素和结束元素。输入/输出如下所示:

l_in  = [0, 1, 2, 3]
l_out = [(3, 0, 1), (0, 1, 2), (1, 2, 3), (2, 3, 0)]

我的问题与另一个名为getting successive adjacent elements of a list的问题密切相关,但另一个问题没有考虑到结束元素的包装,只处理元素对而不是三元组。

我有一个更长的方法来做这个涉及旋转deques和拉链他们:

from collections import deque
l_in = [0, 1, 2, 3]
deq = deque(l_in)
deq.rotate(1)
deq_prev = deque(deq)
deq.rotate(-2)
deq_next = deque(deq)
deq.rotate(1)
l_out = list(zip(deq_prev, deq, deq_next))
# l_out is [(3, 0, 1), (0, 1, 2), (1, 2, 3), (2, 3, 0)]

但是,我觉得使用其他内置Python功能可能有更优雅(和/或更有效)的方法。例如,如果rotate()的{​​{1}}函数返回旋转列表而不是将其修改到位,则可能是一行或两行(尽管这种方法可能会将旋转列表压缩在一起)不是最有效的)。如何更优雅和/或更有效地完成这项工作?

4 个答案:

答案 0 :(得分:1)

这可以通过切片完成:

l_in  = [0, 1, 2, 3]

l_in = [l_in[-1]] + l_in + [l_in[0]]
l_out = [l_in[i:i+3] for i in range(len(l_in)-2)]

好吧,还是这样的变态:

div = len(l_in)
n = 3
l_out = [l_in[i % div: i % div + 3]
         if len(l_in[i % div: i % div + 3]) == 3
         else l_in[i % div: i % div + 3] + l_in[:3 - len(l_in[i % div: i % div + 3])]
         for i in range(3, len(l_in) + 3 * n + 2)]

您可以指定迭代次数。

答案 1 :(得分:1)

一种方法可能是将itertoolsmore_itertools.windowed结合使用:

import itertools as it

import more_itertools as mit


l_in  = [0, 1, 2, 3]
n = len(l_in)
list(it.islice(mit.windowed(it.cycle(l_in), 3), n-1, 2*n-1))
# [(3, 0, 1), (0, 1, 2), (1, 2, 3), (2, 3, 0)]

这里我们生成了sliding windows的无限循环并切割了所需的子集。

FWIW,这里是后一代码的抽象,给出了任何可迭代输入的一般灵活解决方案,例如: range(5)"abcde"iter([0, 1, 2, 3])等等:

def get_windows(iterable, size=3, offset=-1):
    """Return an iterable of windows including an optional offset."""
    it1, it2 = it.tee(iterable)
    n = mit.ilen(it1)
    return it.islice(mit.windowed(it.cycle(it2), size), n+offset, 2*n+offset)


list(get_windows(l_in))
# [(3, 0, 1), (0, 1, 2), (1, 2, 3), (2, 3, 0)]

list(get_windows("abc", size=2))
# [('c', 'a'), ('a', 'b'), ('b', 'c')]

list(get_windows(range(5), size=2, offset=-2))
# [(3, 4), (4, 0), (0, 1), (1, 2), (2, 3)]

注意:more-itertools是一个单独的库,可以通过以下方式轻松安装:

> pip install more_itertools

答案 2 :(得分:0)

当我写这个问题时,我想出了一个更好的解决方案,但我已经完成了编写它的工作,所以这里有。这个解决方案至少要简洁得多:

deque

有关如何在Python中旋转列表的不同答案,请参阅this post

上面的单行解决方案应该至少与问题中的解决方案一样有效(基于我的理解),因为切片不应该比旋转复制deques更昂贵(见https://wiki.python.org/moin/TimeComplexity)。

尽管如此,仍然欢迎提供更高效(或优雅)解决方案的其他答案。

答案 3 :(得分:0)

你发现有一个基于习语lst[i:] + lst[:i]

的列表轮换切片

在理解中使用它为变量n获取所需的相邻元素的数量更为通用[lst[i:] + lst[:i] for i in range(n)]

所以一切都可以参数化,循环旋转中的相邻元素n的数量和'阶段'p,如果不是'自然'0基本索引的起点,尽管默认p=-1设置为-1以适合所需的输出

tst = list(range(4))

def rot(lst, n, p=-1):
    return list(zip(*([lst[i+p:] + lst[:i+p] for i in range(n)])))

rot(tst, 3)
Out[2]: [(3, 0, 1), (0, 1, 2), (1, 2, 3), (2, 3, 0)]    

根据评论

显示短代码