2016-07-01 5 views
1

Модуль python itertools реализует некоторые базовые строительные блоки для итераторов. Как говорится, «они образуют итераторную алгебру». Я ожидал, но я не мог найти лаконичный способ сделать следующую итерацию с помощью модуля. Учитывая список упорядоченных действительных чисел, напримерИтерация над n последовательными элементами списка (с перекрытием)

a = [1.0,1.5,2.0,2.5,3.0] 

... возвращает новый список (или просто итерацию) группировка по некоторым n значение, скажем 2

b = [(1.0,1.5),(1.5,2.0),(2.0,2.5),(2.5,3.0)] 

так, как я нашел делать это было следующим. Во-первых разделить список на две части, с эвенов и фор индексов:

even, odds = a[::2], a[1::2] 

Затем построить новый список:

b = [(even, odd) for even, odd in zip(evens, odds)] 
b = sorted(b + [(odd, even) for even, odd in zip(evens[1:], odds)]) 

В сущности, он похож на движущейся средней.

Есть ли лаконичный способ сделать это (с или без itertools)?


PS .:

Применение

Представьте a список как набор меток времени некоторых событий, произошедших в ходе эксперимента:

timestamp  event 
47.8   1a 
60.5   1b 
67.4   2a 
74.5   2b 
78.5   1a 
82.2   1b 
89.5   2a 
95.3   2b 
101.7   1a 
110.2   1b 
121.9   2a 
127.1   2b 

... 

Этот код будучи используется для сегментации этих событий в соответствии с различными временными окнами. Сейчас меня интересуют данные между 2 последовательными событиями; 'n> 2' будет использоваться только в исследовательских целях.

ответ

3

Для 2, вы можете просто сделать

b = zip(a, a[1:]) # or list(zip(...)) on Python 3 if you really want a list 

При фиксированном п, техника аналогична:

# n = 4 
b = zip(a, a[1:], a[2:], a[3:]) 

Для переменной п, можно сжать переменное количество ломтиков, или (особенно если размер окна близок к размеру a), вы можете использовать нарезку для непосредственного отображения окон:

b = zip(*[a[i:] for i in xrange(n)]) 
# or 
b = [tuple(a[i:i+n]) for i in xrange(len(a)-n)] 

Если a не список, можно обобщить pairwise рецепт от itertools документы:

import copy 
import itertools 

def nwise(iterable, n): 
    # Make n tees at successive positions along the iterable. 
    tees = list(itertools.tee(iterable, 1)) 
    for _ in xrange(n-1): 
     tees.append(copy.copy(tees[-1])) 
     next(tees[-1]) 

    return zip(*tees) 
+0

@glibdud: Ах, вы правы. Единственный тройник все еще завернут в одноэлементный кортеж. – user2357112

4

Это именно то, что pairwise itertools recipe для, для n=2 то есть.

from itertools import tee 

def pairwise(iterable): 
    "s -> (s0,s1), (s1,s2), (s2, s3), ..." 
    a, b = tee(iterable) 
    next(b, None) 
    return zip(a, b) 

Demo:

>>> b = [1.0,1.5,2.0,2.5,3.0] 
>>> list(pairwise(b)) 
[(1.0, 1.5), (1.5, 2.0), (2.0, 2.5), (2.5, 3.0)] 

Если вы ищете переменные размеры группы, см user2357112's answer (мне нравится подход), или в более общем случае можно реализовать скользящее окно итератора и взять кусочки of which there are many approaches ,


Как и в стороне,? Возможно неэффективный, но забавное окно однострочного можно нарезать (контролировать перекрытие), что не на связанный вопросе будет это, используя новый yield from синтаксис для объединить генераторы.

from itertools import tee, islice 
def roll_window(it, sz): 
    yield from zip(*[islice(it, g, None) for g, it in enumerate(tee(it, sz))]) 

Demo:

>>> b = [1.0,1.5,2.0,2.5,3.0, 3.5, 4.0, 4.5] 
>>> list(islice(window(b, 3), None, None, 2)) 
[(1.0, 1.5, 2.0), (2.0, 2.5, 3.0), (3.0, 3.5, 4.0)] 
1

Использование генератора:

def groupListByN(lst, n): 
    for i in range(len(a)-n+1): 
    yield lst[i:i+n] 

a = [1.0,1.5,2.0,2.5,3.0] 
myDoubleList = [group for group in groupListByN(a, 2)] 
myTripleList = [group for group in groupListByN(a, 3)] 

print(myDoubleList) 
print(myTripleList) 

Результат:

[[1.0, 1.5], [1.5, 2.0], [2.0, 2.5], [2.5, 3.0]] 
[[1.0, 1.5, 2.0], [1.5, 2.0, 2.5], [2.0, 2.5, 3.0]] 

Я думаю, что это решение является довольно емким

Смежные вопросы