Извлечь отдельные ненулевые блоки из массива

имея такой массив, например:

[1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1] 

Какой самый быстрый способ в Python получить ненулевые элементы, упорядоченные в списке, где каждый элемент содержит индексы блоков непрерывных ненулевых значений?

Здесь результатом будет список, содержащий множество массивов:

 ([0, 1, 2, 3], [9, 10, 11], [14, 15], [20, 21]) 

4 Solutions collect form web for “Извлечь отдельные ненулевые блоки из массива”

 >>> L = [1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1] >>> import itertools >>> import operator >>> [[i for i,value in it] for key,it in itertools.groupby(enumerate(L), key=operator.itemgetter(1)) if key != 0] [[0, 1, 2, 3], [9, 10, 11], [14, 15], [20, 21]] 

Посмотрите на scipy.ndimage.measurements.label :

 import numpy as np from scipy.ndimage.measurements import label x = np.asarray([1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1]) labelled, numfeats = label(x) indices = [np.nonzero(labelled == k) for k in np.unique(labelled)[1:]] 

indices содержат именно то, что вы просили. Обратите внимание, что в зависимости от вашей конечной цели, labelled также могут предоставить вам полезную (дополнительную) информацию.

Тривиальное изменение моего ответа при поиске последовательных нулей в массиве numpy дает функцию find_runs :

 def find_runs(value, a): # Create an array that is 1 where a is `value`, and pad each end with an extra 0. isvalue = np.concatenate(([0], np.equal(a, value).view(np.int8), [0])) absdiff = np.abs(np.diff(isvalue)) # Runs start and end where absdiff is 1. ranges = np.where(absdiff == 1)[0].reshape(-1, 2) return ranges 

Например,

 In [43]: x Out[43]: array([1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1]) In [44]: find_runs(1, x) Out[44]: array([[ 0, 4], [ 9, 12], [14, 16], [20, 22]]) In [45]: [range(*run) for run in find_runs(1, x)] Out[45]: [[0, 1, 2, 3], [9, 10, 11], [14, 15], [20, 21]] 

Если значение 1 в вашем примере не было репрезентативным, и вам действительно нужны пробеги любых ненулевых значений (как предложено текстом вопроса), вы можете изменить np.equal(a, value) на (a != 0) и соответствующим образом измените аргументы и комментарии. Например

 def find_nonzero_runs(a): # Create an array that is 1 where a is nonzero, and pad each end with an extra 0. isnonzero = np.concatenate(([0], (np.asarray(a) != 0).view(np.int8), [0])) absdiff = np.abs(np.diff(isnonzero)) # Runs start and end where absdiff is 1. ranges = np.where(absdiff == 1)[0].reshape(-1, 2) return ranges 

Например,

 In [63]: y Out[63]: array([-1, 2, 99, 99, 0, 0, 0, 0, 0, 12, 13, 14, 0, 0, 1, 1, 0, 0, 0, 0, 42, 42]) In [64]: find_nonzero_runs(y) Out[64]: array([[ 0, 4], [ 9, 12], [14, 16], [20, 22]]) 

Вы можете использовать np.split , как только вы знаете интервал длины не нулей и соответствующие индексы в A Предполагая, что A в качестве входного массива реализация будет выглядеть примерно так:

 # Append A on either sides with zeros A_ext = np.diff(np.hstack(([0],A,[0]))) # Find interval of non-zeros lengths interval_lens = np.where(A_ext==-1)[0] - np.where(A_ext==1)[0] # Indices of non-zeros places in A idx = np.arange(A.size)[A!=0] # Finally split indices based on the interval lengths out = np.split(idx,interval_lens.cumsum())[:-1] 

Пример ввода, вывод –

 In [53]: A Out[53]: array([1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1]) In [54]: out Out[54]: [array([0, 1, 2, 3]), array([ 9, 10, 11]), array([14, 15]), array([20, 21])] 
Python - лучший язык программирования в мире.