[英]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)]
您可以指定迭代次數。
一種方法可能是將itertools
與more_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.