Summation of only consecutive values in a python array

I am new on python (and even programing!), so I will try to be as clear as I can to explain my question. For you guys it could be easy, but I have not found a satisfactory result on this yet.

Here is the problem:

I have an array with both negative and positive values, say:

x = numpy.array([1, 4, 2, 3, -1, -6, -6, 5, 6, 7, 3, 1, -5, 4, 9, -5, -2, -1, -4])

I would like to sum ONLY the negative values that are continuous, i.e. only sum(-1, -6, -6), sum(-5, -2, -1, -4) and so on. I have tried using numpy.where, as well as numpy.split based on the condition.

For example:

 for i in range(len(x)):
     if x[i] < 0.:
         y[i] = sum(x[i])

However, as you can expect, I just got the summation of all negative values in the array instead. In this case sum(-1, -6, -6, -5, -5, -2, -1, -4)
Could guys share with me an aesthetic and efficient way to solve this problem? I will appreciate any response on this.

Thank you very much

Best answer

Here’s a vectorized NumPythonic solution –

# Mask of negative numbers
mask = x<0

# Differentiation between Consecutive mask elements. We would look for 
# 1s and -1s to detect rising and falling edges in the mask corresponding 
# to the islands of negative numbers.
diffs = np.diff(mask.astype(int))

# Mask with 1s at start of negative islands
start_mask = np.append(True,diffs==1) 

# Mask of negative numbers with islands of one isolated negative numbers removed
mask1 = mask & ~(start_mask & np.append(diffs==-1,True))

# ID array for IDing islands of negative numbers
id = (start_mask & mask1).cumsum()

# Finally use bincount to sum elements within their own IDs
out = np.bincount(id[mask1]-1,x[mask1])

You can also use np.convolve to get mask1, like so –

mask1 = np.convolve(mask.astype(int),np.ones(3),'same')>1

You can also get the count of negative numbers in each “island” with a little tweak to existing code –

counts = np.bincount(id[mask1]-1)

Sample run –

In [395]: x
Out[395]: 
array([ 1,  4,  2,  3, -1, -6, -6,  5,  6,  7,  3,  1, -5,  4,  9, -5, -2,
       -1, -4])

In [396]: out
Out[396]: array([-13., -12.])

In [397]: counts
Out[397]: array([3, 4])