Recuento de islas de números negativos y positivos en una matriz NumPy

Tengo una matriz que contiene trozos de elementos negativos y trozos de elementos positivos. Un ejemplo muy simplificado de esto sería una matriz que se parece a: array([-3, -2, -1, 1, 2, 3, 4, 5, 6, -5, -4])

(a<0).sum() y (a>0).sum() me dan el número total de elementos negativos y positivos, pero ¿cómo los cuento en orden? Con esto quiero decir que quiero saber que mi matriz contiene los primeros 3 elementos negativos, 6 positivos y 2 negativos.

Esto suena como un tema que se ha tratado en alguna parte, y puede haber un duplicado, pero no puedo encontrar uno.

Un método es usar numpy.roll(a,1) en un bucle en toda la matriz y contar el número de elementos de un signo dado que aparecen, por ejemplo, el primer elemento de la matriz a medida que se desplaza, pero no parece mucho. Numpyic (o pythonic) ni muy eficiente para mí.

Aquí hay un enfoque vectorizado –

 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 

Ejecuciones de muestra

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

Prueba de tiempo de ejecución

Otro (s) enfoque (s) -

 # @Franz's 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 

Tiempos en un conjunto de datos más grande -

 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 

Solo usa

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

y obtendrás la indización de los valores negativos. Después de eso puedes dividir esta matriz:

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

y además calcular el tamaño de las matrices internas:

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

Y obtendrás lo que buscas:

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

Solo tienes que mencionar cuál es tu primer elemento. Por definición en mi código, uno negativo.

Así que solo ejecuta:

 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) 

Mi solución (bastante simple y probablemente ineficiente) sería:

 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