Как сгруппировать блоки идентичных логических значений в Python - PullRequest
0 голосов
/ 12 ноября 2018

Предположим, у меня есть следующий список:

a = [True, True, True, False, False, False, False, True, True]

Как их лучше всего сгруппировать, возвращая только 0, 3, 7 или группировку типа

[True, True, True]
[False, False, False, False]
[True, True]

Предыстория: я пытаюсь найти плато в моих массивах numpy, и хотя установка производной на ноль - хорошее начало, мне все еще нужно отсортировать массив на куски. Я думаю, что это в основном сводится к проблеме выше. Я посмотрел numpy и itertools (пытаясь найти решение по этому вопросу Numpy grouping с использованием производительности itertools.groupby ), но мне это не удалось. Я думаю, можно использовать комбинацию itertools.takewhile и FilterFalse (см. Документ здесь ), но я там не в своей глубине. Или, может быть, я просто думаю, что сложно.

Ответы [ 3 ]

0 голосов
/ 12 ноября 2018

Мы могли бы получить индексы с помощью сравнения нарезанных массивов, и они должны быть хорошими с производительностью для списков / массивов большого размера -

a_ext = np.r_[~a[0],a]
out = np.flatnonzero(a_ext[:-1]!=a_ext[1:])

В качестве однострочника мы можем использовать np.diff + np.flatnonzero -

np.flatnonzero(np.diff(np.r_[~a[0],a]))
# compact alternative : np.where(np.diff(np.r_[~a[0],a]))[0]
0 голосов
/ 13 ноября 2018

Вы можете сделать это полностью с помощью itertools.groupby:

Учитывая

import itertools as it


a = [True, True, True, False, False, False, False, True, True]

Код

[list(g)[0][0] for _, g in it.groupby(enumerate(a), key=lambda x: x[-1])]
# [0, 3, 7]

Подробности

Это вывод groupby из вашей итерации:

[(k, list(g)) for k, g in it.groupby(a)]
# [(True, [True, True, True]),
#  (False, [False, False, False, False]),
#  (True, [True, True])]

Мы можем перечислить каждый элемент на группу (g)как кортежи и группировка по последнему индексу в каждом кортеже:

[list(g) for k, g in it.groupby(enumerate(a), key=lambda x: x[-1])]
# [[(0, True), (1, True), (2, True)],
#  [(3, False), (4, False), (5, False), (6, False)],
#  [(7, True), (8, True)]]

Теперь мы хотим, чтобы первый элемент ([0]) и первая позиция ([0]) получили индекс каждой группы.

@ Предложение Chris_Rands о [next(g)[0] ...] еще чище.

См. Также этот пост о том, как использовать groupby.

0 голосов
/ 12 ноября 2018

Простейший способ, возможно, будет таким:

a = [True, True, True, False, False, False, False, True, True]

res = [0] + [i+1 for i, (x, y) in enumerate(zip(a, a[1:])) if x!=y]
print(res)  # -> [0, 3, 7]

Что касается решения groupby, вы можете сделать:

from itertools import groupby

groups = [list(g) for _, g in groupby(a)]
print(groups)  # -> [[True, True, True], [False, False, False, False], [True, True]]
...