簡體   English   中英

在Python中創建另一個列表的相鄰元素的列表

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

我正在尋找一個列表作為輸入,然后創建另一個列表,其中包含原始列表中相鄰元素的元組(或子列表),環繞着開始和結束元素。 輸入/輸出如下所示:

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

我的問題與另一個標題獲取列表的相鄰相鄰元素緊密相關,但是此其他問題未考慮到結束元素的環繞,只處理了成對的元素而不是三元組。

我有一個更長的方法來做到這一點,包括旋轉雙端隊列並將它們壓縮在一起:

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功能可能有更優雅(和/或更有效)的方法來執行此操作。 例如,如果deque rotate()函數返回了旋轉后的列表而不是在原地對其進行修改,則它可能是一排或兩排的(盡管這種將旋轉后的列表壓縮在一起的方法可能不是最有效的)。 如何才能更優雅地和/或更有效地完成此任務?

這可以通過切片來完成:

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

您可以指定迭代次數。

一種方法可能是將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)]

在這里,我們生成了滑動窗口的無限循環,並對所需的子集進行了切片。


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

好吧,在編寫問題時,我找到了一個更好的解決方案,但是我已經完成了編寫該問題的工作,因此可以繼續。 該解決方案至少更為簡潔:

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

有關如何在Python中旋轉列表的不同答案,請參見本文

上面的單行解決方案至少應與問題的解決方案一樣有效(基於我的理解),因為切片的成本不應該比雙端隊列的旋轉復制更昂貴(請參閱https://wiki.python。 org / moin / TimeComplexity )。

但是,仍然歡迎使用更有效(或更優雅)的解決方案的其他答案。

如您所見,有一個基於列表旋轉切片的習慣用法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)]    

根據評論顯示縮短的代碼

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM