Extract individual nonzero blocks from an array

having such an array, for example:

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

What is the fastest way in Python to get nonzero elements ordered in a list where each element contains block indices of continuous nonzero values?

Here the result will be a list containing many arrays:

 ([0, 1, 2, 3], [9, 10, 11], [14, 15], [20, 21]) 
+3
python numpy extract
source share
4 answers
 >>> 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]] 
+6
source share

Take a look at 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 contains exactly what you requested. Please note that depending on your final goal, labelled may also provide you with useful (additional) information.

+1
source share

Trivially changing my answer to Finding consecutive zeros in a numpy array gives the find_runs function:

 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 

For example,

 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]] 

If the value 1 in your example was not representative, and you really need runs of any non-zero values ​​(as suggested in the text of the question), you can change np.equal(a, value) to (a != 0) and change the arguments and comments accordingly way. For example.

 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 

For example,

 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]]) 
+1
source share

You can use np.split as soon as you find out the non-zero length interval and the corresponding indices in A Assuming A as an input array, the implementation will look something like this:

 # 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] 

Example input, output -

 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])] 
0
source share

All Articles