Ответ 1
Мы будем использовать np.searchsorted
и логику для поиска краев кластера.
Во-первых, давайте более подробно рассмотрим, что делает np.searchsorted
:
Найти индексы в отсортированном массиве a так, чтобы, если соответствующие элементы из v были вставлены перед индексами, порядок a был бы сохранен.
Я сделаю np.searchsorted
с a
с помощью a - delta_left
. Давайте посмотрим, что для delta_left = 1
# a =
# [ 0 2 3 4 5 10 11 11 14 19 20 20]
#
# a - delta_left
# [-1 1 2 3 4 9 10 10 13 18 19 19]
-
-1
будет вставлен в позицию0
для поддержания порядка -
1
будет вставлен в позицию1
для поддержания порядка -
2
также будет вставлен в позицию1
, указывая, что2
может находиться в том же кластере, что и1
-
3
будет вставлен в позицию2
, указав, что3
может находиться в том же кластере, что и2
- и так далее и т.д.
Мы заметили, что только когда элемент с меньшей дельта будет вставлен в текущую позицию, мы рассмотрим запуск нового кластера.
Мы делаем это снова для правой стороны с разницей. Разница заключается в том, что по умолчанию, если группа элементов одинакова, np.searchsorted
предполагает вставить в начало значений. Чтобы определить концы кластеров, мне захочется вставить после идентичных элементов. Поэтому я буду использовать paramater side='right'
Если 'left, указатель первого подходящего местоположения найден. Если "правильно", верните последний такой индекс. Если нет подходящего индекса, верните либо 0, либо N (где N - длина a).
Теперь логика. Кластер может начинаться только в том случае, если предыдущий кластер закончился, за исключением первого кластера. Затем мы рассмотрим сдвинутую версию результатов нашего второго np.searchsorted
Теперь определим нашу функцию
def delta_cluster(a, dleft, dright):
# use to track whether searchsorted results are at correct positions
rng = np.arange(len(a))
edge_left = a.searchsorted(a - dleft)
starts = edge_left == rng
# we append 0 to shift
edge_right = np.append(0, a.searchsorted(a + dright, side='right')[:-1])
ends = edge_right == rng
return (starts & ends).cumsum()
демонстрация
с левыми, правыми дельтами, равными 1 и 1
print(delta_cluster(a, 1, 1))
[1 2 2 2 2 3 3 3 4 5 5 5]
с левыми, правыми дельтами, равными 2 и 1
print(delta_cluster(a, 2, 1))
[1 1 1 1 1 2 2 2 3 4 4 4]
Дополнительный кредит
Что делать, если a
не сортируется?
Я буду использовать информацию, полученную из этого сообщения
def delta_cluster(a, dleft, dright):
s = a.argsort()
size = s.size
if size > 1000:
y = np.empty(s.size, dtype=np.int64)
y[s] = np.arange(s.size)
else:
y = s.argsort()
a = a[s]
rng = np.arange(len(a))
edge_left = a.searchsorted(a - dleft)
starts = edge_left == rng
edge_right = np.append(0, a.searchsorted(a + dright, side='right')[:-1])
ends = edge_right == rng
return (starts & ends).cumsum()[y]
демонстрация
b = np.random.permutation(a)
print(b)
[14 10 3 11 20 0 19 20 4 11 5 2]
print(delta_cluster(a, 2, 1))
[1 1 1 1 1 2 2 2 3 4 4 4]
print(delta_cluster(b, 2, 1))
[3 2 1 2 4 1 4 4 1 2 1 1]
print(delta_cluster(b, 2, 1)[b.argsort()])
[1 1 1 1 1 2 2 2 3 4 4 4]