Итерация по спискам

Я хочу, чтобы алгоритм перебирал фрагменты списка. Размер срезов устанавливается вне функции и может отличаться.

На мой взгляд, это что-то вроде:

for list_of_x_items in fatherList: foo(list_of_x_items) 

Есть ли способ правильно определить list_of_x_items или какой-либо другой способ сделать это с помощью python 2.5?


edit1: Уточнение. Звук, используемый как «разделение» и «скользящее окно», применим к моей задаче, но я не эксперт. Поэтому я объясню проблему немного глубже и добавлю к вопросу:

FatherList – многоуровневый numpy.array, который я получаю из файла. Функция должна найти средние числа серий (пользователь предоставляет длину серии). Для усреднения я использую функцию mean() . Теперь для расширения вопроса:

edit2: Как изменить функцию, которую вы предоставили для хранения дополнительных элементов, и использовать их, когда следующий параметр fatherList подается в функцию?

например, если список имеет длину 10, а размер куска – 3, то 10-й член списка сохраняется и добавляется к началу следующего списка.


Связанный:

  • Каков самый «питонический» способ перебора списка в кусках?

8 Solutions collect form web for “Итерация по спискам”

Ответ на последнюю часть вопроса:

Обновление вопроса: как изменить функцию, которую вы предоставили для хранения дополнительных элементов, и использовать их, когда следующий параметр 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)] 

Если количество элементов не делится на размер среза, и вы хотите заполнить список 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 -> сгенерирует ссылку N для итератора the_list.

zip(*(iter(the_list),) * N) -> будет 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 над squance lst , т. е. создает перекрывающиеся срезы: [1,2,3], [2,3,4], [3,4,5], … Не уверен если это намерение OP, все же.

Я не уверен, но, похоже, вы хотите сделать так называемую скользящую среднюю. numpy предоставляет возможности для этого (функция convolve).

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

Ваш вопрос мог бы использовать более подробно, но как насчет:

 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 ) в духе ответа Нади, касающегося не-chunk-делимых размеров без заполнения:

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

Python - лучший язык программирования в мире.