Элегантный способ удалить непрерывные повторяющиеся элементы в списке? - PullRequest
4 голосов
/ 04 октября 2011

Я ищу чистый, Pythonic, способ исключить из следующего списка:

li = [0, 1, 2, 3, 3, 4, 3, 2, 2, 2, 1, 0, 0]

все смежные повторяющиеся элементы (работает более одного числа), чтобы получить:

re = [0, 1, 2, 4, 3, 1]

но, хотя у меня есть рабочий код, он кажется не-Pythonic, и я совершенно уверен, что должен быть выход (может быть, некоторые менее известные itertools функции?) Для достижения того, что я хочу, в гораздо более сжатой и элегантной способ.

Ответы [ 4 ]

8 голосов
/ 04 октября 2011

Вот версия, основанная на Карле, для которой не требуются копии списка (tmp, фрагменты и сжатый список). izip значительно быстрее, чем (Python 2) zip для больших списков. chain немного медленнее, чем нарезка, но не требует tmp объекта или копий списка. islice плюс создание tmp немного быстрее, но требует больше памяти и менее элегантно.

from itertools import izip, chain
[y for x, y, z in izip(chain((None, None), li), 
                       chain((None,), li), 
                       li) if x != y != z]

Тест timeit показывает, что он примерно в два раза быстрее, чем у Карла или моей самой быстрой версии groupby для коротких групп.

Обязательно используйте значение, отличное от None (например, object()), если ваш список может содержать None s.

Используйте эту версию, если она нужна для работы с итератором / итерируемым, который не является последовательностью, или если ваши группы длинные:

[key for key, group in groupby(li) 
        if (next(group) or True) and next(group, None) is None]

timeit показывает, что примерно в десять раз быстрее, чем в другой версии для 1000 групп элементов.

Более ранние, медленные версии:

[key for key, group in groupby(li) if sum(1 for i in group) == 1]
[key for key, group in groupby(li) if len(tuple(group)) == 1]
4 голосов
/ 04 октября 2011

Ответ agf хорош, если размер групп невелик, но если в строке достаточно дубликатов, будет эффективнее не «суммировать 1» по этим группам

[key for key, group in groupby(li) if all(i==0 for i,j in enumerate(group)) ]
1 голос
/ 04 октября 2011

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

_undef = object()

def itersingles(source):
    cur = _undef
    dup = True
    for elem in source:
        if dup:
            if elem != cur:
                cur = elem
                dup = False
        else:
            if elem == cur:
                dup = True
            else:
                yield cur
                cur = elem
    if not dup:
        yield cur

source = [0, 1, 2, 3, 3, 4, 3, 2, 2, 2, 1, 0, 0]
result = list(itersingles(source))
1 голос
/ 04 октября 2011
tmp = [object()] + li + [object()]
re = [y for x, y, z in zip(tmp[2:], tmp[1:-1], tmp[:-2]) if y != x and y != z]
...