index data array python numpy

python - data - Encontrar el índice de una matriz numpy en una lista



index of element list python (4)

¿Que tal este?

arr = np.array([[1,2,3]]) foo = np.array([1, ''hello'', arr], dtype=np.object) # if foo array is of heterogeneous elements (str, int, array) [idx for idx, el in enumerate(foo) if type(el) == type(arr)] # if foo array has only numpy arrays in it [idx for idx, el in enumerate(foo) if np.array_equal(el, arr)]

Salida:

[2]

Nota: Esto también funcionará incluso si foo es una lista. Simplemente lo puse como una matriz numpy aquí.

import numpy as np foo = [1, "hello", np.array([[1,2,3]]) ]

Yo esperaría

foo.index( np.array([[1,2,3]]) )

regresar

2

pero en cambio yo obtengo

ValueError: el valor de verdad de una matriz con más de un elemento es ambiguo. Use a.any () o a.all ()

algo mejor que mi solución actual? Parece ineficiente.

def find_index_of_array(list, array): for i in range(len(list)): if np.all(list[i]==array): return i find_index_of_array(foo, np.array([[1,2,3]]) ) # 2


El motivo del error aquí es, obviamente, porque numpy''s ndarray anula == para devolver una matriz en lugar de una booleana.

AFAIK, no hay una solución simple aquí. Lo siguiente funcionará mientras el
np.all(val == array) funciona.

next((i for i, val in enumerate(lst) if np.all(val == array)), -1)

Si ese bit funciona o no depende críticamente de lo que son los otros elementos en el conjunto y si se pueden comparar con matrices numpy.


El problema aquí (probablemente ya lo sepas pero solo para repetirlo) es que list.index funciona de la siguiente manera:

for idx, item in enumerate(your_list): if item == wanted_item: return idx

La línea if item == wanted_item es el problema, porque convierte implícitamente item == wanted_item en boolean. Pero numpy.ndarray (excepto si es un escalar) plantea este ValueError entonces:

ValueError: el valor de verdad de una matriz con más de un elemento es ambiguo. Use a.any () o a.all ()

Solución 1: clase de adaptador (envoltura delgada)

Generalmente utilizo un envoltorio delgado (adaptador) alrededor de numpy.ndarray cada vez que necesito usar funciones de python como list.index :

class ArrayWrapper(object): __slots__ = ["_array"] # minimizes the memory footprint of the class. def __init__(self, array): self._array = array def __eq__(self, other_array): # array_equal also makes sure the shape is identical! # If you don''t mind broadcasting you can also use # np.all(self._array == other_array) return np.array_equal(self._array, other_array) def __array__(self): # This makes sure that `np.asarray` works and quite fast. return self._array def __repr__(self): return repr(self._array)

Estas envolturas delgadas son más costosas que manualmente utilizando algún ciclo de enumerate o comprensión, pero no es necesario volver a implementar las funciones de Python. Suponiendo que la lista solo contenga matrices numpy (de lo contrario, debe hacer algunas comprobaciones if ... else ... ):

list_of_wrapped_arrays = [ArrayWrapper(arr) for arr in list_of_arrays]

Después de este paso, puede usar todas sus funciones de python en esta lista:

>>> list_of_arrays = [np.ones((3, 3)), np.ones((3)), np.ones((3, 3)) * 2, np.ones((3))] >>> list_of_wrapped_arrays.index(np.ones((3,3))) 0 >>> list_of_wrapped_arrays.index(np.ones((3))) 1

Estas envolturas ya no son numpy-arrays pero tienes envoltorios delgados así que la lista extra es bastante pequeña. De modo que, dependiendo de sus necesidades, puede mantener la lista envuelta y la lista original y elegir las operaciones, por ejemplo, también puede list.count las matrices idénticas ahora:

>>> list_of_wrapped_arrays.count(np.ones((3))) 2

o list.remove :

>>> list_of_wrapped_arrays.remove(np.ones((3))) >>> list_of_wrapped_arrays [array([[ 1., 1., 1.], [ 1., 1., 1.], [ 1., 1., 1.]]), array([[ 2., 2., 2.], [ 2., 2., 2.], [ 2., 2., 2.]]), array([ 1., 1., 1.])]

Solución 2: subclase y ndarray.view

Este enfoque usa subclases explícitas de numpy.array . Tiene la ventaja de que obtienes toda la funcionalidad incorporada de la matriz y solo modificas la operación solicitada (que sería __eq__ ):

class ArrayWrapper(np.ndarray): def __eq__(self, other_array): return np.array_equal(self, other_array) >>> your_list = [np.ones(3), np.ones(3)*2, np.ones(3)*3, np.ones(3)*4] >>> view_list = [arr.view(ArrayWrapper) for arr in your_list] >>> view_list.index(np.array([2,2,2])) 1

De nuevo obtienes la mayoría de los métodos de lista de esta manera: list.remove , list.count además de list.index .

Sin embargo, este enfoque puede producir un comportamiento sutil si alguna operación usa implícitamente __eq__ . Siempre se puede volver a interpretar como una matriz numpy simple utilizando np.asarray o .view(np.ndarray) :

>>> view_list[1] ArrayWrapper([ 2., 2., 2.]) >>> view_list[1].view(np.ndarray) array([ 2., 2., 2.]) >>> np.asarray(view_list[1]) array([ 2., 2., 2.])

Alternativa: __bool__ (o __nonzero__ para python 2)

En lugar de solucionar el problema en el método __eq__ , también puede anular __bool__ o __nonzero__ :

class ArrayWrapper(np.ndarray): # This could also be done in the adapter solution. def __bool__(self): return bool(np.all(self)) __nonzero__ = __bool__

De nuevo, esto hace que la list.index funcione como se list.index :

>>> your_list = [np.ones(3), np.ones(3)*2, np.ones(3)*3, np.ones(3)*4] >>> view_list = [arr.view(ArrayWrapper) for arr in your_list] >>> view_list.index(np.array([2,2,2])) 1

¡Pero esto definitivamente modificará más comportamiento! Por ejemplo:

>>> if ArrayWrapper([1,2,3]): ... print(''that was previously impossible!'') that was previously impossible!


Para el rendimiento, es posible que desee procesar solo las matrices NumPy en la lista de entrada. Entonces, podríamos verificar el tipo antes de entrar en el ciclo e indexar en los elementos que son matrices.

Por lo tanto, una implementación sería -

def find_index_of_array_v2(list1, array1): idx = np.nonzero([type(i).__module__ == np.__name__ for i in list1])[0] for i in idx: if np.all(list1[i]==array1): return i