Итерация по срезам списка
Я хочу, чтобы алгоритм перебирал срезы списка. Размер срезов устанавливается вне функции и может отличаться.
В моем понимании это что-то вроде:
for list_of_x_items in fatherList:
foo(list_of_x_items)
есть ли способ правильно определить list_of_x_items
или какой-то другой способ сделать это с помощью Python 2.5?
edit1: разъяснения термины "разбиение" и "раздвижное окно" применимы к моей задаче, но я не эксперт. Поэтому я объясню проблему немного глубже и добавить к вопросу:
В fatherList является многоуровневым и NumPy.массив я получаю из файла. Функция должна найти средние ряды (пользователь предоставляет длину ряда) для усреднения я использую
9 ответов
ответ на последнюю часть вопроса:
обновление вопроса: Как изменить функция, которую вы предоставили для хранения дополнительные детали и используют их когда следующий 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
если вы хотите разделить список на ломтики, вы можете использовать этот трюк:
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)]
это грязный трюк
хорошо, я объясню, как это работает. Будет сложно объяснить, но я постараюсь.
сначала немного предыстории:
In 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
- > создаст ссылку N на итератор the_list.
zip(*(iter(the_list),) * N)
- > будет кормить этот список итераторов в zip. Которые, в свою очередь, сгруппируют их в N кортежей. Но так как все N элементов фактически являются ссылками на один и тот же итератор iter(the_list)
результатом будут повторные вызовы next()
на оригинальный итератор
надеюсь, это все объясняет. Я советую вам пойти с более простым для понимания решением. Я только хотел упомянуть об этом трюке, потому что он мне нравится.
Если вы хотите иметь возможность использовать любую итерацию, вы можете использовать следующие функции:
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)
вы имеете в виду что-то вроде:
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)
использовать генератор:
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
над squence lst
, т. е. он производит перекрывающиеся ломтики: [1,2,3], [2,3,4], [3,4,5], ... Не уверен, что это намерение OP.
Я не уверен, но кажется, вы хотите сделать то, что называется скользящей средней. 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
ваш вопрос может использовать более подробную информацию, но как насчет:
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)
для почти одного лайнера (после itertools
import) в духе ответа Нади, касающегося не-куска делимых размеров без заполнения:
>>> 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
.
расширение ответа @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