Подтвердить что ты не робот

Как определить последовательности значений в булевом массиве?

У меня длинный булев массив:

bool_array = [ True, True, True, True, True, False, False, False, False, False, True, True, True, False, False, True, True, True, True, False, False, False, False, False, False, False ]

Мне нужно выяснить, где значения переворачиваются, т.е. адреса, где начинаются последовательности True и False. В этом конкретном случае я хотел бы получить

index = [0, 5, 10, 13, 15, 19, 26]

Есть ли простой способ обойтись без ручного цикла, чтобы проверять каждый i-й элемент с помощью (i + 1) th?

4b9b3361

Ответ 1

В качестве более эффективного подхода для больших наборов данных в python 3.X вы можете использовать accumulate и groupby из модуля itertools.

>>> from itertools import accumulate, groupby
>>> [0] + list(accumulate(sum(1 for _ in g) for _,g in groupby(bool_array)))
[0, 5, 10, 13, 15, 19, 26]

Логика кода:

Этот код классифицирует последовательные повторяющиеся элементы с помощью функции groupby(), затем перебирает итератор, возвращенный groupby(), который содержит пары ключей (которые мы избежали, используя строку, вместо переменной throw), и эти классифицированные итераторы.

>>> [list(g) for _, g in groupby(bool_array)]
[[True, True, True, True, True], [False, False, False, False, False], [True, True, True], [False, False], [True, True, True, True], [False, False, False, False, False, False, False]]

Итак, все, что нам нужно, вычисляет длину этих итераторов и суммирует каждую длину с предыдущей длиной, чтобы получить индекс первого элемента, который именно там, где элемент изменен, именно это функция accumulate() для.

В Numpy вы можете использовать следующий подход:

In [19]: np.where(arr[1:] - arr[:-1])[0] + 1
Out[19]: array([ 5, 10, 13, 15, 19])
# With leading and trailing indices
In [22]: np.concatenate(([0], np.where(arr[1:] - arr[:-1])[0] + 1, [arr.size]))
Out[22]: array([ 0,  5, 10, 13, 15, 19, 26])

Ответ 2

Это скажет вам, где:

>>> import numpy as np
>>> np.argwhere(np.diff(bool_array)).squeeze()
array([ 4,  9, 12, 14, 18])

np.diff вычисляет разницу между каждым элементом и следующим. Для булевых значений он по существу интерпретирует значения как целые числа (0: False, non-zero: True), поэтому различия отображаются как значения +1 или -1, которые затем преобразуются обратно в booleans (True, когда есть изменение).

Затем функция np.argwhere сообщает вам, где значения True, которые теперь являются изменениями.

Ответ 3

Используя zip и enumerate, вы может сделать

>>> [i for i,(m,n) in enumerate(zip(bool_array[:-1],bool_array[1:])) if m!=n]
[4, 9, 12, 14, 18]

Теперь, когда у вас есть [4, 9, 12, 14, 18], вы можете

>>> [0]+[i+1 for i in [4, 9, 12, 14, 18]]+[len(bool_array)]
[0, 5, 10, 13, 15, 19, 26]

Чтобы достичь результата.


Логика кода:

  • zip принимает два итератора и возвращает последовательность из двух элементов. Мы передаем один и тот же список для обоих итераторов, начиная с первого элемента и начиная с второго. Следовательно, мы получаем список смежных чисел
  • enumerate дает вам последовательность индексов и значение итератора.
  • Теперь мы завершим его в понимании списка. Если значения zipped не совпадают, мы возвращаем индекс

Другая процедура с одним шагом -

>>> [i for i,(m,n) in enumerate(zip([2]+bool_array,bool_array+[2])) if m!=n]
[0, 5, 10, 13, 15, 19, 26]

Здесь мы намеренно вводим [2] в список, потому что первое и последнее значения всегда будут разными (поскольку [2] никогда не присутствует в списке). Следовательно, мы получим эти индексы напрямую.

Ответ 4

Начиная с Python 3.8 и введением выражений присваивания (PEP 572) (оператор :=), мы можем использовать и увеличивать переменную в пределах понимания списка. В сочетании с groupby:

from itertools import groupby

# bool_array = [True, True, True, True, True, False, False, False, False, False, True, True, True, False, False, True, True, True, True, False, False, False, False, False, False, False]
total = 0
[total := total + len(list(gp)) for _, gp in groupby(bool_array)]
# [5, 10, 13, 15, 19, 26]

Это:

  • Инициализирует переменную total до 0 которая символизирует совокупную сумму
  • groupby последовательные элементы с groupby (последовательный True будет сгруппирован вместе, то же самое относится к последовательному False)
  • Для каждой серии сгруппированных логических значений это оба:
    • увеличивает значение total на текущую длину серии логических значений (total := total + len(list(gp))) с помощью выражения присваивания
    • и в то же время сопоставляет последовательный ряд с новым значением total

Конечно, чтобы начать это с 0, вы всегда можете вставить [0] в начало списка.