35

我想要一个算法来迭代列表切片。切片大小在函数之外设置并且可以不同。

在我看来,它是这样的:

for list_of_x_items in fatherList:
    foo(list_of_x_items)

有没有办法使用python 2.5正确定义list_of_x_items或其他方式来做到这一点?


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

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

编辑2:如何修改您提供的功能以存储额外的项目并在下一个fatherList被提供给该函数时使用它们?

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


有关的:

4

10 回答 10

70

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

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

例如

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

如果项目的数量不能被切片大小整除,并且你想用 None 填充列表,你可以这样做:

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

一个迭代器对象可以像这样被消费一次:

>>> 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 前面的 * 用于解压缩参数。您可以在此处找到更多详细信息。所以

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()在原始迭代器上重复调用

我希望能解释它。我建议您使用更易于理解的解决方案。我只是想提及这个技巧,因为我喜欢它。

于 2009-08-26T15:35:05.303 回答
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)
于 2009-08-26T15:28:54.347 回答
11

使用生成器:

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)

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

于 2009-08-26T15:13:22.787 回答
8

你的意思是这样的:

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)
于 2009-08-26T15:12:18.437 回答
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
于 2009-08-26T19:02:07.803 回答
5

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

>>> x = numpy.array(范围(20))
>>> x
    数组([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
       17、18、19])    
>>> n = 2 # 移动平均窗口
>>> numpy.convolve(numpy.ones(n)/n, x)[n-1:-n+1]
数组([ 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

于 2009-08-27T08:11:39.913 回答
3

扩展@Ants Aasma 的答案:在 Python 3.7 中,StopIteration异常的处理发生了变化(根据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
于 2018-03-21T16:09:32.117 回答
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)
于 2009-08-26T15:13:03.333 回答
1

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.

于 2014-05-21T18:07:21.280 回答
0

将列表或迭代器切成给定大小的块的函数。如果最后一个块更小,也会正确处理这种情况:

def slice_iterator(data, slice_len):
    it = iter(data)
    while True:
        items = []
        for index in range(slice_len):
            try:
                item = next(it)
            except StopIteration:
                if items == []:
                    return # we are done
                else:
                    break # exits the "for" loop
            items.append(item)
        yield items

使用示例:

for slice in slice_iterator([1,2,3,4,5,6,7,8,9,10],3):
    print(slice)

结果:

[1, 2, 3]
[4, 5, 6]
[7, 8, 9]
[10]
于 2021-10-30T01:27:20.497 回答