Предположим, у меня есть NumPy-массив целых чисел, как:
[34,2,3,22,22,22,22,22,22,18,90,5,-55,-19,22,6,6,6,6,6,6,6,6,23,53,1,5,-42,82]
Я хочу найти начальные и конечные индексы массива, где значение больше, чем x-раз (например, 5 раз) повторяется. Таким образом, в приведенном выше случае это значение 22 и 6. Начальный индекс повторного 22 равен 3, а конечный индекс - 8. То же самое для повторения 6.
Есть ли в Python специальный инструмент?
В противном случае я бы прошел через индекс массива индекса и сравнил фактическое значение с предыдущим.
С уважением.
Ответ 1
Используя np.diff
и метод здесь от @WarrenWeckesser для поиска прогонов нулей в массиве:
import numpy as np
def zero_runs(a): # from link
iszero = np.concatenate(([0], np.equal(a, 0).view(np.int8), [0]))
absdiff = np.abs(np.diff(iszero))
ranges = np.where(absdiff == 1)[0].reshape(-1, 2)
return ranges
a = [34,2,3,22,22,22,22,22,22,18,90,5,-55,-19,22,6,6,6,6,6,6,6,6,23,53,1,5,-42,82]
zero_runs(np.diff(a))
Out[87]:
array([[ 3, 8],
[15, 22]], dtype=int32)
Затем это можно отфильтровать по разности между началом и концом прогона:
runs = zero_runs(np.diff(a))
runs[runs[:, 1]-runs[:, 0]>5] # runs of 7 or more, to illustrate filter
Out[96]: array([[15, 22]], dtype=int32)
Ответ 2
На самом деле для этого не очень хорошо. Вы можете сделать что-то вроде:
mult = 5
for elem in val_list:
target = [elem] * mult
found_at = val_list.index(target)
Я оставляю вам не обнаруженные исключения и более длительное обнаружение последовательности.
Ответ 3
Вот решение, использующее Python native itertools
.
код
import itertools as it
def find_ranges(lst, n=2):
"""Return ranges for `n` or more repeated values."""
groups = ((k, tuple(g)) for k, g in it.groupby(enumerate(lst), lambda x: x[-1]))
repeated = (idx_g for k, idx_g in groups if len(idx_g) >=n)
return ((sub[0][0], sub[-1][0]) for sub in repeated)
lst = [34,2,3,22,22,22,22,22,22,18,90,5,-55,-19,22,6,6,6,6,6,6,6,6,23,53,1,5,-42,82]
list(find_ranges(lst, 5))
# [(3, 8), (15, 22)]
Испытания
import nose.tools as nt
def test_ranges(f):
"""Verify list results identifying ranges."""
nt.eq_(list(f([])), [])
nt.eq_(list(f([0, 1,1,1,1,1,1, 2], 5)), [(1, 6)])
nt.eq_(list(f([1,1,1,1,1,1, 2,2, 1, 3, 1,1,1,1,1,1], 5)), [(0, 5), (10, 15)])
nt.eq_(list(f([1,1, 2, 1,1,1,1, 2, 1,1,1], 3)), [(3, 6), (8, 10)])
nt.eq_(list(f([1,1,1,1, 2, 1,1,1, 2, 1,1,1,1], 3)), [(0, 3), (5, 7), (9, 12)])
test_ranges(find_ranges)
Этот пример захватывает пары (индекс, элемент) в lst
, а затем группирует их по элементу. Сохраняются только повторяющиеся пары. Наконец, первая и последняя пары нарезаны, что дает (начало, конец) индексы из каждой повторяющейся группы.
См. также этот пост для определения диапазонов индексов с помощью itertools.groupby
,
Ответ 4
Если вы ищете value
повторение n
раз в списке L
, вы можете сделать что-то вроде этого:
def find_repeat(value, n, L):
look_for = [value for _ in range(n)]
for i in range(len(L)):
if L[i] == value and L[i:i+n] == look_for:
return i, i+n
Ответ 5
Вот относительно быстрое, безошибочное решение, которое также говорит вам, сколько копий было в перспективе. Некоторые из этого кода были заимствованы из решения KAL.
# Return the start and (1-past-the-end) indices of the first instance of
# at least min_count copies of element value in container l
def find_repeat(value, min_count, l):
look_for = [value for _ in range(min_count)]
for i in range(len(l)):
count = 0
while l[i + count] == value:
count += 1
if count >= min_count:
return i, i + count