简体   繁体   English

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

[英]create list of adjacent elements of another list in Python

I am looking to take as input a list and then create another list which contains tuples (or sub-lists) of adjacent elements from the original list, wrapping around for the beginning and ending elements. 我正在寻找一个列表作为输入,然后创建另一个列表,其中包含原始列表中相邻元素的元组(或子列表),环绕着开始和结束元素。 The input/output would look like this: 输入/输出如下所示:

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

My question is closely related to another titled getting successive adjacent elements of a list , but this other question does not take into account wrapping around for the end elements and only handles pairs of elements rather than triplets. 我的问题与另一个标题获取列表的相邻相邻元素紧密相关,但是此其他问题未考虑到结束元素的环绕,只处理了成对的元素而不是三元组。

I have a somewhat longer approach to do this involving rotating deques and zipping them together: 我有一个更长的方法来做到这一点,包括旋转双端队列并将它们压缩在一起:

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)]

However, I feel like there is probably a more elegant (and/or efficient) way to do this using other built-in Python functionality. 但是,我觉得使用其他内置的Python功能可能有更优雅(和/或更有效)的方法来执行此操作。 If, for instance, the rotate() function of deque returned the rotated list instead of modifying it in place, this could be a one- or two-liner (though this approach of zipping together rotated lists is perhaps not the most efficient). 例如,如果deque rotate()函数返回了旋转后的列表而不是在原地对其进行修改,则它可能是一排或两排的(尽管这种将旋转后的列表压缩在一起的方法可能不是最有效的)。 How can I accomplish this more elegantly and/or efficiently? 如何才能更优雅地和/或更有效地完成此任务?

This can be done with slices: 这可以通过切片来完成:

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)]

Well, or such a perversion: 好吧,或者这样的变态:

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)]

You can specify the number of iterations. 您可以指定迭代次数。

One approach may be to use itertools combined with more_itertools.windowed : 一种方法可能是将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)]

Here we generated an infinite cycle of sliding windows and sliced the desired subset. 在这里,我们生成了滑动窗口的无限循环,并对所需的子集进行了切片。


FWIW, here is an abstraction of the latter code for a general, flexible solution given any iterable input eg range(5) , "abcde" , iter([0, 1, 2, 3]) , etc.: 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)]

Note: more-itertools is a separate library, easily installed via: 注意: more-itertools是一个单独的库,可以通过以下方式轻松安装:

> pip install more_itertools

Well I figured out a better solution as I was writing the question, but I already went through the work of writing it, so here goes. 好吧,在编写问题时,我找到了一个更好的解决方案,但是我已经完成了编写该问题的工作,因此可以继续。 This solution is at least much more concise: 该解决方案至少更为简洁:

l_out = list(zip(l_in[-1:] + l_in[:-1], l_in, l_in[1:] + l_in[:1]))

See this post for different answers on how to rotate lists in Python. 有关如何在Python中旋转列表的不同答案,请参见本文

The one-line solution above should be at least as efficient as the solution in the question (based on my understanding) since the slicing should not be more expensive than the rotating and copying of the deques (see https://wiki.python.org/moin/TimeComplexity ). 上面的单行解决方案至少应与问题的解决方案一样有效(基于我的理解),因为切片的成本不应该比双端队列的旋转复制更昂贵(请参阅https://wiki.python。 org / moin / TimeComplexity )。

Other answers with more efficient (or elegant) solutions are still welcome though. 但是,仍然欢迎使用更有效(或更优雅)的解决方案的其他答案。

as you found there is a list rotation slicing based idiom lst[i:] + lst[:i] 如您所见,有一个基于列表旋转切片的习惯用法lst[i:] + lst[:i]

using it inside a comprehension taking a variable n for the number of adjacent elements wanted is more general [lst[i:] + lst[:i] for i in range(n)] 在一个理解中使用它,对于需要的相邻元素的数量取一个变量n更为通用[lst[i:] + lst[:i] for i in range(n)]

so everything can be parameterized, the number of adjacent elements n in the cyclic rotation and the 'phase' p , the starting point if not the 'natural' 0 base index, although the default p=-1 is set to -1 to fit the apparant desired output 因此可以对所有参数进行参数化,包括循环旋转中相邻元素的数量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)]    

showing the shortend code as per the comment 根据评论显示缩短的代码

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM