Saltar al contenido

Encuentre los índices de fila de varios valores en una matriz numpy

Solución:

Enfoque # 1

Un enfoque sería utilizar NumPy broadcasting, al igual que –

np.where((X==searched_values[:,None]).all(-1))[1]

Enfoque # 2

Un enfoque de memoria eficiente sería convertir cada fila como equivalentes de índice lineal y luego usar np.in1d, al igual que –

dims = X.max(0)+1
out = np.where(np.in1d(np.ravel_multi_index(X.T,dims),
                    np.ravel_multi_index(searched_values.T,dims)))[0]

Enfoque # 3

Otro enfoque eficiente en memoria usando np.searchsorted y con esa misma filosofía de conversión a equivalentes de índice lineal sería así:

dims = X.max(0)+1
X1D = np.ravel_multi_index(X.T,dims)
searched_valuesID = np.ravel_multi_index(searched_values.T,dims)
sidx = X1D.argsort()
out = sidx[np.searchsorted(X1D,searched_valuesID,sorter=sidx)]

Tenga en cuenta que esto np.searchsorted El método asume que hay una coincidencia para cada fila de searched_values en X.


Cómo np.ravel_multi_index ¿trabaja?

Esta función nos da los números equivalentes del índice lineal. Acepta un 2D gama de n-dimensional indices, establecidos como columnas y la forma de esa cuadrícula n-dimensional en sí misma en la que se mapearán esos índices y se calcularán índices lineales equivalentes.

Usemos las entradas que tenemos para el problema en cuestión. Tome el caso de la entrada X y observe la primera fila. Dado que, estamos tratando de convertir cada fila de X en su índice lineal equivalente y desde np.ravel_multi_index asume cada columna como una tupla de indexación, necesitamos transponer X antes de alimentar la función. Dado que, el número de elementos por fila en X en este caso es 2, la cuadrícula n-dimensional sobre la que se mapeará sería 2D. Con 3 elementos por fila en X, hubiera sido 3D cuadrícula para mapeo y así sucesivamente.

Para ver cómo esta función calcularía índices lineales, considere la primera fila de X

In [77]: X
Out[77]: 
array([[4, 2],
       [9, 3],
       [8, 5],
       [3, 3],
       [5, 6]])

Tenemos la forma de la cuadrícula n-dimensional como dims

In [78]: dims
Out[78]: array([10,  7])

Creemos la cuadrícula bidimensional para ver cómo funciona ese mapeo y cómo se calculan los índices lineales con np.ravel_multi_index

In [79]: out = np.zeros(dims,dtype=int)

In [80]: out
Out[80]: 
array([[0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0]])

Establezcamos la primera tupla de indexación de X, es decir, la primera fila de X en la cuadrícula –

In [81]: out[4,2] = 1

In [82]: out
Out[82]: 
array([[0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0],
       [0, 0, 1, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0]])

Ahora, para ver el índice lineal equivalente del elemento que acaba de configurar, aplanémoslo y usemos np.where para detectar eso 1.

In [83]: np.where(out.ravel())[0]
Out[83]: array([30])

Esto también podría calcularse si se tiene en cuenta el orden de las filas principales.

Usemos np.ravel_multi_index y verificar esos índices lineales –

In [84]: np.ravel_multi_index(X.T,dims)
Out[84]: array([30, 66, 61, 24, 41])

Por lo tanto, tendríamos índices lineales correspondientes a cada tupla de indexación de X, es decir, cada fila de X.

Elegir dimensiones para np.ravel_multi_index para formar índices lineales únicos

Ahora, la idea detrás de considerar cada fila de X como tupla de indexación de una cuadrícula de n dimensiones y convertir cada tupla en un escalar es tener escalares únicos correspondientes a tuplas únicas, es decir, filas únicas en X.

Echemos otro vistazo a X

In [77]: X
Out[77]: 
array([[4, 2],
       [9, 3],
       [8, 5],
       [3, 3],
       [5, 6]])

Ahora, como se discutió en la sección anterior, estamos considerando cada fila como una tupla de indexación. Dentro de cada una de estas tuplas de indexación, el primer elemento representaría el primer eje de la cuadrícula n-dim, el segundo elemento sería el segundo eje de la cuadrícula y así sucesivamente hasta el último elemento de cada fila en X. En esencia, cada columna representaría una dimensión o eje de la cuadrícula. Si vamos a mapear todos los elementos de X en la misma cuadrícula n-dim, debemos considerar el estiramiento máximo de cada eje de dicha cuadrícula n-dim propuesta. Suponiendo que estamos tratando con números positivos en X, tal tramo sería el máximo de cada columna en X + 1. Eso + 1 es porque Python sigue 0-based indexación. Así por ejemplo X[1,0] == 9 se asignaría a la décima fila de la cuadrícula propuesta. Similar, X[4,1] == 6 iría a la 7th columna de esa cuadrícula.

Entonces, para nuestro caso de muestra, tuvimos:

In [7]: dims = X.max(axis=0) + 1 # Or simply X.max(0) + 1

In [8]: dims
Out[8]: array([10,  7])

Por lo tanto, necesitaríamos una cuadrícula de al menos una forma de (10,7) para nuestro caso de muestra. Más longitudes a lo largo de las dimensiones no perjudicarán y también nos darían índices lineales únicos.

Observaciones finales: una cosa importante que debe tenerse en cuenta aquí es que si tenemos números negativos en X, necesitamos agregar compensaciones adecuadas a lo largo de cada columna en X para hacer esas tuplas de indexación como números positivos antes de usar np.ravel_multi_index.

Otra alternativa es utilizar asvoid (abajo) a view cada fila como un soltero
valor de void dtype. Esto reduce una matriz 2D a una matriz 1D, lo que le permite utilizar np.in1d como siempre:

import numpy as np

def asvoid(arr):
    """
    Based on http://stackoverflow.com/a/16973510/190597 (Jaime, 2013-06)
    View the array as dtype np.void (bytes). The items along the last axis are
    viewed as one value. This allows comparisons to be performed which treat
    entire rows as one value.
    """
    arr = np.ascontiguousarray(arr)
    if np.issubdtype(arr.dtype, np.floating):
        """ Care needs to be taken here since
        np.array([-0.]).view(np.void) != np.array([0.]).view(np.void)
        Adding 0. converts -0. to 0.
        """
        arr += 0.
    return arr.view(np.dtype((np.void, arr.dtype.itemsize * arr.shape[-1])))

X = np.array([[4,  2],
              [9,  3],
              [8,  5],
              [3,  3],
              [5,  6]])

searched_values = np.array([[4, 2],
                            [3, 3],
                            [5, 6]])

idx = np.flatnonzero(np.in1d(asvoid(X), asvoid(searched_values)))
print(idx)
# [0 3 4]

El paquete numpy_indexed (descargo de responsabilidad: soy su autor) contiene la funcionalidad para realizar tales operaciones de manera eficiente (también usa búsquedas ordenadas bajo el capó). En términos de funcionalidad, actúa como un equivalente vectorizado de list.index:

import numpy_indexed as npi
result = npi.indices(X, searched_values)

Tenga en cuenta que al usar el kwarg ‘missing’, tiene control total sobre el comportamiento de los elementos que faltan, y también funciona para nd-arrays (fi; pilas de imágenes).

Actualización: usando las mismas formas que @Rik X=[520000,28,28] y searched_values=[20000,28,28], corre en 0.8064 secs, usando missing = -1 para detectar y denotar entradas que no están presentes en X.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *