rle implementation credit to Thomas Browne from his SOF post Sept 2015

        Parameters
        ----------
        a : array, shape[n,]
            input vector

        Returns
        -------
        z : array, shape[nt,]
            run lengths
        p : array, shape[nt,]
            start positions of each run
        ar : array, shape[nt,]
            values for each run

def _rle(self, a):
        '''
        rle implementation credit to Thomas Browne from his SOF post Sept 2015

        Parameters
        ----------
        a : array, shape[n,]
            input vector

        Returns
        -------
        z : array, shape[nt,]
            run lengths
        p : array, shape[nt,]
            start positions of each run
        ar : array, shape[nt,]
            values for each run
        '''
        ia = np.asarray(a)
        n = len(ia)
        y = np.array(ia[1:] != ia[:-1])  # pairwise unequal (string safe)
        i = np.append(np.where(y), n - 1)  # must include last element posi
        z = np.diff(np.append(-1, i))  # run lengths
        p = np.cumsum(np.append(0, z))[:-1]  # positions
        return (z, p, ia[i])