Number of islands of negative and positive numbers in a NumPy array

I have an array containing chunks of negative and chunks of positive elements. A much simplified example of this would be an array a

like:array([-3, -2, -1, 1, 2, 3, 4, 5, 6, -5, -4])

(a<0).sum()

and (a>0).sum()

give me the total of negative and positive elements, but how can I count them in order? By this I mean I want to know that my array contains the first 3 negative elements, 6 positive and 2 negative ones.

It sounds like a topic that was addressed somewhere and there might be a duplicate, but I can't seem to find it.

The method is to numpy.roll(a,1)

loop through the entire array and count the number of elements of a given sign appearing in, for example, the first element of the array when it rolls, but it doesn't look much numpyic (or pythonic) and is not very efficient to me.

+3


source to share


3 answers


Here's one vector approach -

def pos_neg_counts(a):
    mask = a>0
    idx = np.flatnonzero(mask[1:] != mask[:-1])
    count = np.concatenate(( [idx[0]+1], idx[1:] - idx[:-1], [a.size-1-idx[-1]] ))
    if a[0]<0:
        return count[1::2], count[::2] # pos, neg counts
    else:
        return count[::2], count[1::2] # pos, neg counts

      

Run examples -

In [155]: a
Out[155]: array([-3, -2, -1,  1,  2,  3,  4,  5,  6, -5, -4])

In [156]: pos_neg_counts(a)
Out[156]: (array([6]), array([3, 2]))

In [157]: a[0] = 3

In [158]: a
Out[158]: array([ 3, -2, -1,  1,  2,  3,  4,  5,  6, -5, -4])

In [159]: pos_neg_counts(a)
Out[159]: (array([1, 6]), array([2, 2]))

In [160]: a[-1] = 7

In [161]: a
Out[161]: array([ 3, -2, -1,  1,  2,  3,  4,  5,  6, -5,  7])

In [162]: pos_neg_counts(a)
Out[162]: (array([1, 6, 1]), array([2, 1]))

      

Runtime test



Another approach is

# @Franz soln        
def split_app(my_array):
    negative_index = my_array<0
    splits = np.split(negative_index, np.where(np.diff(negative_index))[0]+1)
    len_list = [len(i) for i in splits]
    return len_list

      

The timeline for a larger dataset is

In [20]: # Setup input array
    ...: reps = np.random.randint(3,10,(100000))
    ...: signs = np.ones(len(reps),dtype=int)
    ...: signs[::2] = -1
    ...: a = np.repeat(signs, reps)*np.random.randint(1,9,reps.sum())
    ...: 

In [21]: %timeit split_app(a)
10 loops, best of 3: 90.4 ms per loop

In [22]: %timeit pos_neg_counts(a)
100 loops, best of 3: 2.21 ms per loop

      

+1


source


Just use

my_array = np.array([-3, -2, -1,  1,  2,  3,  4,  5,  6, -5, -4])
negative_index = my_array<0

      

and you will get the indices of negative values. After that, you can split this array:

splits = np.split(negative_index, np.where(np.diff(negative_index))[0]+1)

      

and, in addition, calculate the size of the internal arrays:

len_list = [len(i) for i in splits]
print(len_list)

      



And you get what you are looking for:

Out[1]: [3, 6, 2]

      

You just need to specify what your first element is. Negative by definition in my code.

So just do:

my_array = np.array([-3, -2, -1,  1,  2,  3,  4,  5,  6, -5, -4])
negative_index = my_array<0
splits = np.split(negative_index, np.where(np.diff(negative_index))[0]+1)
len_list = [len(i) for i in splits]
print(len_list)

      

+1


source


My (rather simple and probably ineffective) solution:

import numpy as np
arr = np.array([-3, -2, -1,  1,  2,  3,  4,  5,  6, -5, -4])
sgn = np.sign(arr[0])
res = []
cntr = 1 # counting the first one
for i in range(1, len(arr)):
 if np.sign(arr[i]) != sgn:
  res.append(cntr)
  cntr = 0
  sgn *= -1
 cntr += 1
res.append(cntr)
print res

      

0


source







All Articles