迭代列表切片

时间:2009-08-26 15:04:05

标签: python loops iteration slice

我想要一个迭代列表切片的算法。切片大小设置在函数外部,可以有所不同。

在我看来,它就像是:

for list_of_x_items in fatherList:
    foo(list_of_x_items)

有没有办法使用python 2.5正确定义list_of_x_items或其他一些方法呢?


edit1:澄清“分区”和“滑动窗口”这两个术语听起来都适用于我的任务,但我不是专家。所以我会更深入地解释这个问题并添加问题:

fatherList是我从文件中获取的多级numpy.array。函数必须找到系列的平均值(用户提供系列的长度)为平均我使用mean()函数。现在进行问题扩展:

edit2:如何修改您提供的用于存储额外项目的函数,并在将下一个fatherList提供给函数时使用它们?

例如,如果列表长度为10且块的大小为3,则列表的第10个成员将被存储并附加到下一个列表的开头。


相关:

9 个答案:

答案 0 :(得分:61)

如果你想将一个列表分成片,你可以使用这个技巧:

list_of_slices = zip(*(iter(the_list),) * slice_size)

例如

>>> zip(*(iter(range(10)),) * 3)
[(0, 1, 2), (3, 4, 5), (6, 7, 8)]

如果切片尺寸无法分割项目数,并且您希望使用“无”填充列表,则可以执行以下操作:

>>> map(None, *(iter(range(10)),) * 3)
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, None, None)]

这是一个肮脏的小技巧


好的,我会解释它是如何工作的。解释起来会很棘手,但我会尽我所能。

首先是一点背景:

在Python中,您可以将列表乘以如下数字:

[1, 2, 3] * 3 -> [1, 2, 3, 1, 2, 3, 1, 2, 3]
([1, 2, 3],) * 3 -> ([1, 2, 3], [1, 2, 3], [1, 2, 3])

iterator对象可以像这样使用一次:

>>> l=iter([1, 2, 3])
>>> l.next()
1
>>> l.next()
2
>>> l.next()
3

zip函数返回元组列表,其中第i个元组包含来自每个参数序列或迭代的第i个元素。例如:

zip([1, 2, 3], [20, 30, 40]) -> [(1, 20), (2, 30), (3, 40)]
zip(*[(1, 20), (2, 30), (3, 40)]) -> [[1, 2, 3], [20, 30, 40]]

zip前面的*用于解压缩参数。您可以找到更多详细信息here。 所以

zip(*[(1, 20), (2, 30), (3, 40)])

实际上相当于

zip((1, 20), (2, 30), (3, 40))

但使用可变数量的参数

现在回到诀窍:

list_of_slices = zip(*(iter(the_list),) * slice_size)

iter(the_list) - >将列表转换为迭代器

(iter(the_list),) * N - >将生成对the_list迭代器的N引用。

zip(*(iter(the_list),) * N) - >将这些迭代器列表提供给zip。而这又将它们分组为N个大小的元组。但由于所有N个项实际上都是对同一个迭代器iter(the_list)的引用,结果将是对原始迭代器上的next()的重复调用

我希望能够解释它。我建议你使用一个更容易理解的解决方案。我很想提及这个技巧,因为我喜欢它。

答案 1 :(得分:21)

如果您希望能够使用任何迭代,您可以使用以下函数:

from itertools import chain, islice

def ichunked(seq, chunksize):
    """Yields items from an iterator in iterable chunks."""
    it = iter(seq)
    while True:
        yield chain([it.next()], islice(it, chunksize-1))

def chunked(seq, chunksize):
    """Yields items from an iterator in list chunks."""
    for chunk in ichunked(seq, chunksize):
        yield list(chunk)

答案 2 :(得分:8)

回答问题的最后部分:

  

问题更新:如何修改   你提供给商店的功能   额外的物品,并在使用时使用它们   下一个fatherList被送到了   功能

如果你需要存储状态,那么你可以使用一个对象。

class Chunker(object):
    """Split `iterable` on evenly sized chunks.

    Leftovers are remembered and yielded at the next call.
    """
    def __init__(self, chunksize):
        assert chunksize > 0
        self.chunksize = chunksize        
        self.chunk = []

    def __call__(self, iterable):
        """Yield items from `iterable` `self.chunksize` at the time."""
        assert len(self.chunk) < self.chunksize
        for item in iterable:
            self.chunk.append(item)
            if len(self.chunk) == self.chunksize:
                # yield collected full chunk
                yield self.chunk
                self.chunk = [] 

示例:

chunker = Chunker(3)
for s in "abcd", "efgh":
    for chunk in chunker(s):
        print ''.join(chunk)

if chunker.chunk: # is there anything left?
    print ''.join(chunker.chunk)

输出:

abc
def
gh

答案 3 :(得分:7)

你的意思是:

def callonslices(size, fatherList, foo):
  for i in xrange(0, len(fatherList), size):
    foo(fatherList[i:i+size])

如果这大致是您想要的功能,如果您愿意,可以在发电机中装扮一下:

def sliceup(size, fatherList):
  for i in xrange(0, len(fatherList), size):
    yield fatherList[i:i+size]

然后:

def callonslices(size, fatherList, foo):
  for sli in sliceup(size, fatherList):
    foo(sli)

答案 4 :(得分:7)

使用发电机:

big_list = [1,2,3,4,5,6,7,8,9]
slice_length = 3
def sliceIterator(lst, sliceLen):
    for i in range(len(lst) - sliceLen + 1):
        yield lst[i:i + sliceLen]

for slice in sliceIterator(big_list, slice_length):
    foo(slice)

sliceIterator在序列sliceLen上实现宽度为lst的“滑动窗口”,即它产生重叠切片:[1,2,3],[2,3,4] ],[3,4,5],......不确定这是否是OP的意图。

答案 5 :(得分:5)

我不确定,但似乎你想做所谓的移动平均线。 numpy为此提供了设施(卷积函数)。

>>> x = numpy.array(range(20))
>>> x
    array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16,
       17, 18, 19])    
>>> n = 2 # moving average window
>>> numpy.convolve(numpy.ones(n)/n, x)[n-1:-n+1]
array([  0.5,   1.5,   2.5,   3.5,   4.5,   5.5,   6.5,   7.5,   8.5,
         9.5,  10.5,  11.5,  12.5,  13.5,  14.5,  15.5,  16.5,  17.5,  18.5])

好处是它可以很好地适应不同的加权方案(只需将numpy.ones(n) / n更改为其他方式)。

您可以在此处找到完整的资料: http://www.scipy.org/Cookbook/SignalSmooth

答案 6 :(得分:2)

你的问题可以使用更多细节,但如何:

def iterate_over_slices(the_list, slice_size):
    for start in range(0, len(the_list)-slice_size):
        slice = the_list[start:start+slice_size]
        foo(slice)

答案 7 :(得分:2)

对于近乎一个班轮(itertools导入之后),Nadia的答案是处理非块可分割大小而没有填充:

>>> import itertools as itt
>>> chunksize = 5
>>> myseq = range(18)
>>> cnt = itt.count()
>>> print [ tuple(grp) for k,grp in itt.groupby(myseq, key=lambda x: cnt.next()//chunksize%2)]
[(0, 1, 2, 3, 4), (5, 6, 7, 8, 9), (10, 11, 12, 13, 14), (15, 16, 17)]

如果您愿意,可以使用itertools.count()摆脱enumerate()要求,而且更加丑陋:

[ [e[1] for e in grp] for k,grp in itt.groupby(enumerate(myseq), key=lambda x: x[0]//chunksize%2) ]

(在这个例子中,enumerate()将是多余的,但并非所有序列都是这样的整齐范围,显然)

远不及其他一些答案那么整洁,但在紧要关头有用,特别是如果已导入itertools

答案 8 :(得分:2)

扩展@Ants Aasma的答案:在Python 3.7中处理StopIteration例外changed(根据PEP-479)。兼容版本将是:

from itertools import chain, islice

def ichunked(seq, chunksize):
    it = iter(seq)
    while True:
        try:
            yield chain([next(it)], islice(it, chunksize - 1))
        except StopIteration:
            return