python - transpuesta - tamaño de un arreglo numpy
Cómo contar el número de ceros a la izquierda de cada uno en una matriz Numpy (4)
Tengo una matriz binaria numpy como esta:
Array A = [1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0]
Me gustaría contar cuántos ceros hay a la izquierda de cada uno y devolverlos en otra matriz que se vería así en este ejemplo:
nb_1s = [0, 0, 1, 2, 2, 5]
No hay 0 a la izquierda para los dos primeros 1s así que los primeros dos números de la matriz son 0, etc.
Sé que primero tengo que iniciar una matriz con número de 1s en mi matriz:
def give_zeros(binary_array):
binary_array = np.asarray(binary_array)
nb_zeros = np.zeros(binary_array.sum())
return nb_zeros
Pero no estoy seguro de cómo contar la cantidad de ceros. ¿Debería iterar en un ciclo for con ''nditer''? No parece eficiente ya que tendré que ejecutar esta función en matrices muy grandes.
¿Tienes alguna idea? Gracias.
Código
Podrías usar:
(A == 0).cumsum()[A > 0]
# array([0, 0, 1, 2, 2, 5])
o:
(~A).cumsum()[A]
# array([0, 0, 1, 2, 2, 5])
si A
es una matriz bool.
Explicación
A == 0
es una matriz booleana que es True
para cada 0
:
>>> import numpy as np
>>> A = np.array([1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0])
>>> A == 0
array([False, False, True, False, True, False, False, True, True,
True, False, True, True, True, True], dtype=bool)
Puede usar cumsum()
para contar el número de True
s:
>>> (A == 0).cumsum()
array([0, 0, 1, 1, 2, 2, 2, 3, 4, 5, 5, 6, 7, 8, 9])
Solo necesitas los valores donde A > 0
:
>>> (A == 0).cumsum()[A > 0]
array([0, 0, 1, 2, 2, 5])
¡Hecho!
De manera no vectorizada:
>>> x = [1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0]
>>> c, y = 0, []
>>> for i in x:
... if i == 1:
... y.append(c)
... else:
... c += 1
...
>>> y
[0, 0, 1, 2, 2, 5]
Para la solución vectorizada, vea la respuesta de @Divakar:
En numpy
, primero encuentre los índices distintos de cero, con np.nonzero()
:
>>> np.nonzero(x)[0]
array([ 0, 1, 3, 5, 6, 10])
Luego reste eso con la matriz de rango de longitud de índices:
>>> idx = np.nonzero(x)[0]
>>> np.arange(len(idx))
array([0, 1, 2, 3, 4, 5])
>>> np.nonzero(x)[0] - np.arange(len(idx))
array([0, 0, 1, 2, 2, 5])
>>> np.arange(x.count(1))
array([0, 1, 2, 3, 4, 5])
>>> np.nonzero(x)[0] - np.arange(x.count(1))
array([0, 0, 1, 2, 2, 5])
Si el recuento es acumulativo (según su ejemplo), puede hacerlo fácilmente en O (n). Simplemente tenga un contador que aumente en uno cada vez que encuentre un cero y luego agregue el valor de la variable de contador a otra matriz por cada uno que golpee en su conjunto inicial.
Aquí hay una forma vectorizada con la diferenciación de rango de rango de los índices de 1s
-
def leftzeros_count(a):
idx = np.flatnonzero(a!=0)
return idx - np.arange(len(idx))
Ejecuciones de muestra -
In [298]: a = np.array([1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0])
In [299]: leftzeros_count(a)
Out[299]: array([0, 0, 1, 2, 2, 5])
In [300]: a = np.array([0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0])
In [301]: leftzeros_count(a)
Out[301]: array([1, 1, 2, 3, 3, 6])
In [302]: a = np.array([0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1])
In [303]: leftzeros_count(a)
Out[303]: array([ 1, 1, 2, 3, 3, 6, 10])
Prueba de tiempo de ejecución
Para los tiempos, coloquemos la muestra dada un gran número de veces y el tiempo de los acercamientos vectorizados -
In [7]: a = np.array([1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0])
In [8]: a = np.tile(a,100000)
# @Eric Duminil''s soln
In [9]: %timeit (a == 0).cumsum()[a > 0]
100 loops, best of 3: 10.9 ms per loop
# Proposed in this post
In [10]: %timeit leftzeros_count(a)
100 loops, best of 3: 3.71 ms per loop