python - transpuesta - Cómo contar el número de ceros a la izquierda de cada uno en una matriz Numpy




transpuesta de una matriz en python numpy (3)

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

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




binary