Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Numpy find indices of groups with same value

I have a numpy array of zeros and ones:

y=[1,1,1,0,0,0,0,0,1,1,0,0,0,0,0,0,1,1,1,1]

I want to calculate the indices of groups of ones (or zeros). So for the above example the result for groups of ones should be something similar to:

result=[(0,2), (8,9), (16,19)]

(How) Can I do that with numpy? I found nothing like a group-by function.

I experimented around with np.ediff1d, but couldn't figure out a good solution. Not that the array may or may not begin/end with a group of ones:

import numpy as np

y = [1,1,1,0,0,0,0,0,1,1,0,0,0,0,0,0,1,1,1,1]
mask = np.ediff1d(y)
starts = np.where(mask > 0)
ends = np.where(mask < 0)

I also found a partial solution here: Find index where elements change value numpy

But that one only gives me the indices where the values change.

like image 417
tiefenauer Avatar asked May 03 '18 09:05

tiefenauer


1 Answers

We can do something like this that works for any generic array -

def islandinfo(y, trigger_val, stopind_inclusive=True):
    # Setup "sentients" on either sides to make sure we have setup
    # "ramps" to catch the start and stop for the edge islands
    # (left-most and right-most islands) respectively
    y_ext = np.r_[False,y==trigger_val, False]

    # Get indices of shifts, which represent the start and stop indices
    idx = np.flatnonzero(y_ext[:-1] != y_ext[1:])

    # Lengths of islands if needed
    lens = idx[1::2] - idx[:-1:2]

    # Using a stepsize of 2 would get us start and stop indices for each island
    return list(zip(idx[:-1:2], idx[1::2]-int(stopind_inclusive))), lens

Sample run -

In [320]: y
Out[320]: array([1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1])

In [321]: islandinfo(y, trigger_val=1)[0]
Out[321]: [(0, 2), (8, 9), (16, 19)]

In [322]: islandinfo(y, trigger_val=0)[0]
Out[322]: [(3, 7), (10, 15)]

Alternatively, we can use diff to get the sliced comparisons and then simply reshape with 2 columns to replace the step-sized slicing to give ourselves a one-liner -

In [300]: np.flatnonzero(np.diff(np.r_[0,y,0])!=0).reshape(-1,2) - [0,1]
Out[300]: 
array([[ 0,  2],
       [ 8,  9],
       [16, 19]])
like image 183
Divakar Avatar answered Oct 29 '22 15:10

Divakar