如何以最Pythonic和高效的方式在列表中迭代滑动对?以下是一个相关的示例:
>>> l
['a', 'b', 'c', 'd', 'e', 'f', 'g']
>>> for x, y in itertools.izip(l, l[1::2]): print x, y
...
a b
b d
c f
这是成对迭代,但如何实现滑动窗口的迭代呢?也就是迭代这些成对元素:
a b
b c
c d
d e
etc.
这是一种遍历所有配对的方法,每次移动一个元素而不是两个元素。谢谢。
如何以最Pythonic和高效的方式在列表中迭代滑动对?以下是一个相关的示例:
>>> l
['a', 'b', 'c', 'd', 'e', 'f', 'g']
>>> for x, y in itertools.izip(l, l[1::2]): print x, y
...
a b
b d
c f
这是成对迭代,但如何实现滑动窗口的迭代呢?也就是迭代这些成对元素:
a b
b c
c d
d e
etc.
这是一种遍历所有配对的方法,每次移动一个元素而不是两个元素。谢谢。
你甚至可以更简单。只需将列表和列表偏移量压缩在一起。
In [4]: zip(l, l[1:])
Out[4]: [('a', 'b'), ('b', 'c'), ('c', 'd'), ('d', 'e'), ('e', 'f'), ('f', 'g')]
for x, y in itertools.izip(l, l[1:]): print x, y
izip
,但有zip_longest
。为了避免最后一对出现空位,我使用了for x, y in itertools.zip_longest(l[:-1], l[1:]): print x, y
。 - jtpereydazip
,它和itertools.izip
的作用相同。如果您不想在最后一对中留下空位,那么可以使用@chmullig的答案。 - Elias Zamaria我之前写了一个小生成器,可以用于类似的情况:
def pairs(items):
items_iter = iter(items)
prev = next(items_iter)
for item in items_iter:
yield prev, item
prev = item
PairsIter
)呢? - Silas Rayi
。相反,我可能会称其为 items_iter
或类似的名称 -- 虽然这会多占用 18 字节的磁盘空间,但我认为这对于清晰度来说是非常值得的 :) - mgilsondef sliding(seq, n):
return izip(*starmap(islice, izip(tee(seq, n), count(0), repeat(None))))
以下显示了由列表中两个后续条目的添加所定义的时间顺序,按从快到慢排序。
Gil
In [69]: timeit.repeat("for x,y in itertools.izip(l, l[1::1]): x + y", setup=setup, number=1000)
Out[69]: [1.029047966003418, 0.996290922164917, 0.998831033706665]
杰夫·瑞迪
In [70]: timeit.repeat("for x,y in sliding(l,2): x+y", setup=setup, number=1000)
Out[70]: [1.2408790588378906, 1.2099130153656006, 1.207326889038086]
Alestanis
In [66]: timeit.repeat("for i in range(0, len(l)-1): l[i] + l[i+1]", setup=setup, number=1000)
Out[66]: [1.3387370109558105, 1.3243639469146729, 1.3245630264282227]
chmullig
In [68]: timeit.repeat("for x,y in zip(l, l[1:]): x+y", setup=setup, number=1000)
Out[68]: [1.4756009578704834, 1.4369518756866455, 1.5067830085754395]
内森·维拉埃斯库萨
In [63]: timeit.repeat("for x,y in pairs(l): x+y", setup=setup, number=1000)
Out[63]: [2.254757881164551, 2.3750967979431152, 2.302199125289917]
sr2222
注意重复次数减少了...
In [60]: timeit.repeat("for x,y in SubsequenceIter(l,2): x+y", setup=setup, number=100)
Out[60]: [1.599524974822998, 1.5634570121765137, 1.608154058456421]
setup="""
from itertools import izip, starmap, islice, tee, count, repeat
l = range(10000)
def sliding(seq, n):
return izip(*starmap(islice, izip(tee(seq, n), count(0), repeat(None))))
class SubsequenceIter(object):
def __init__(self, iterable, subsequence_length):
self.iterator = iter(iterable)
self.subsequence_length = subsequence_length
self.subsequence = [0]
def __iter__(self):
return self
def next(self):
self.subsequence.pop(0)
while len(self.subsequence) < self.subsequence_length:
self.subsequence.append(self.iterator.next())
return self.subsequence
def pairs(items):
items_iter = iter(items)
prev = items_iter.next()
for item in items_iter:
yield (prev, item)
prev = item
"""
虽然不是最高效的,但相当灵活:
class SubsequenceIter(object):
def __init__(self, iterable, subsequence_length):
self.iterator = iter(iterable)
self.subsequence_length = subsequence_length
self.subsequence = [0]
def __iter__(self):
return self
def next(self):
self.subsequence.pop(0)
while len(self.subsequence) < self.subsequence_length:
self.subsequence.append(self.iterator.next())
return self.subsequence
使用方法:
for x, y in SubsequenceIter(l, 2):
print x, y
不需要导入任何模块,只要提供一个对象列表或字符串,任何具有 var[indexing]
格式的都可以使用这段代码。已在 Python 3.6
上测试通过。
# This will create windows with all but 1 overlap
def ngrams_list(a_list, window_size=5, skip_step=1):
return list(zip(*[a_list[i:] for i in range(0, window_size, skip_step)]))
单独的for循环会创建这个列表,其中a_list
是字母表(显示window = 5
,OP想要window = 2
):
['ABCDEFGHIJKLMNOPQRSTUVWXYZ',
'BCDEFGHIJKLMNOPQRSTUVWXYZ',
'CDEFGHIJKLMNOPQRSTUVWXYZ',
'DEFGHIJKLMNOPQRSTUVWXYZ',
'EFGHIJKLMNOPQRSTUVWXYZ']
zip(*循环结果)
将收集所有完整的垂直列作为结果。如果你想要少于所有但一个重叠:
# You can sample that output to get less overlap:
def sliding_windows_with_overlap(a_list, window_size=5, overlap=2):
zip_output_as_list = ngrams_list(a_list, window_size)])
return zip_output_as_list[::overlap+1]
使用 overlap=2
,它跳过以 B
和 C
开头的列,并选择 D
[('A', 'B', 'C', 'D', 'E'),
('D', 'E', 'F', 'G', 'H'),
('G', 'H', 'I', 'J', 'K'),
('J', 'K', 'L', 'M', 'N'),
('M', 'N', 'O', 'P', 'Q'),
('P', 'Q', 'R', 'S', 'T'),
('S', 'T', 'U', 'V', 'W'),
('V', 'W', 'X', 'Y', 'Z')]
编辑:看起来这与 @chmullig 提供的类似,有可选项
None
在位置 0。 - Nathan Villaescusa