【问题标题】:Pythonic iteration over sliding window pairs in list?Pythonic迭代列表中的滑动窗口对?
【发布时间】:2012-10-12 10:47:22
【问题描述】:

在滑动对中迭代列表的最 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.

这是对对的迭代,除了每次将对滑动 1 个元素而不是 2 个元素。谢谢。

【问题讨论】:

  • 是的,唯一的区别是在另一个问题中,他们希望第一对在位置 0 处具有 None
  • @NathanVillaescusa -- 是的,这就是为什么我没有将其标记为骗子的原因。但我认为一般的想法仍然适用于那里的各种答案。

标签: python list iteration itertools list-manipulation


【解决方案1】:

不需要导入,只要提供对象列表或字符串即可; 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(*result_of_for_loop) 将收集所有完整的垂直列作为结果。如果您想要少于一个重叠:

# 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 提供的类似,带有选项

【讨论】:

    【解决方案2】:

    这是我不久前为类似场景编写的一个小生成器:

    def pairs(items):
        items_iter = iter(items)
        prev = next(items_iter)
    
        for item in items_iter:
            yield prev, item
            prev = item
    

    【讨论】:

    • 这基本上是迭代器协议,除了写成生成器。为什么不把它做成一个自定义的迭代器类(比如PairsIter)?
    • @sr2222 -- 你可以这样做,但为什么呢?虽然我没有做过任何测试,但我有点怀疑这个类会快得多,而且生成器也很简单。
    • 这就是我要做的 (+1) -- 虽然,我可能会避免使用非描述性变量 i。相反,我可能会称它为items_iter 或类似的东西——虽然这确实需要额外的 18 字节磁盘空间,但我认为为了清晰起见,这是值得的 :)
    【解决方案3】:

    通过在列表中添加两个后续条目定义的时间显示在下方,并按从最快到最慢的顺序排列。

    吉尔

    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]
    

    阿莱斯塔尼斯

    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
    """
    

    【讨论】:

      【解决方案4】:

      怎么样:

      for x, y in itertools.izip(l, l[1:]): print x, y
      

      【讨论】:

      • Python 3 没有izip,但有zip_longest。为了避免在最后一对中出现空位,我使用了for x, y in itertools.zip_longest(l[:-1], l[1:]): print x, y
      • @jtpereyda,Python 3 有顶级函数zip,它和itertools.izip 做同样的事情。如果您不想在最后一对中有空位,可以使用@chmullig 的答案。
      【解决方案5】:

      不完全是最有效的,但相当灵活:

      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
      

      【讨论】:

      • 这要求 iterable 是可切片的——它不适用于一般的迭代器。
      【解决方案6】:

      这是一个适用于迭代器/生成器以及列表的任意大小滑动窗口的函数

      def sliding(seq, n):
        return izip(*starmap(islice, izip(tee(seq, n), count(0), repeat(None))))
      

      Nathan 的解决方案可能更有效。

      【讨论】:

        【解决方案7】:

        你可以更简单。只需压缩列表并将列表偏移 1。

        In [4]: zip(l, l[1:])
        Out[4]: [('a', 'b'), ('b', 'c'), ('c', 'd'), ('d', 'e'), ('e', 'f'), ('f', 'g')]
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-10-12
          • 2010-10-09
          • 2021-05-19
          • 2016-11-25
          • 2021-06-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多