Проблема здесь (вы, наверное, уже знаете, а просто повторить его), что list.index
работы по линиям:
for idx, item in enumerate(your_list):
if item == wanted_item:
return idx
Линейка if item == wanted_item
является проблемой, потому что она неявно преобразует item == wanted_item
в булево. Но numpy.ndarray
(за исключением, если это скаляр) поднимает этот ValueError
то:
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
Решение 1: адаптер (тонкая оболочка) Класс
Я обычно используют тонкую оболочку (адаптер) вокруг numpy.ndarray
всякий раз, когда мне нужно использовать функции питона как 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)
Эти тонкие оболочки являются более дорогими, чем вручную с помощью некоторых enumerate
цикла или понимания, но вы не должны г e-реализовать функции python.Если предположить, что список содержит только Numpy-массивы (в противном случае вы должны сделать некоторые if ... else ...
проверки):
list_of_wrapped_arrays = [ArrayWrapper(arr) for arr in list_of_arrays]
После этого шага вы можете использовать все функции питона в этом списке:
>>> 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
Эти упаковщики а не numpy-массивы, но у вас тонкие обертки, поэтому дополнительный список довольно мал. Таким образом, в зависимости от ваших потребностей вы можете сохранить обернутый список и исходный список и выбрать, на которых делать операции, например, вы можете также list.count
одинаковые массивы сейчас:
>>> list_of_wrapped_arrays.count(np.ones((3)))
2
или 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.])]
Этот подход использует явные подклассы numpy.array
. Это имеет то преимущество, что вы получите все-массив встроенной функциональности и только изменять запрошенную операцию (которая будет __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
Опять вы получите самые современные методы перечисляю этот путь: list.remove
, list.count
кроме list.index
.
Однако этот подход может привести к тонкому поведению, если какая-либо операция неявно использует __eq__
. Вы всегда можете повторно интерпретировать это как обычный Numpy массив с помощью np.asarray
или .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.])
Альтернатива: Перекрытие __bool__
(или __nonzero__
для питона 2)
Вместо фиксации проблемы в __eq__
методе вы могли бы переопределить __bool__
или __nonzero__
:
class ArrayWrapper(np.ndarray):
# This could also be done in the adapter solution.
def __bool__(self):
return bool(np.all(self))
__nonzero__ = __bool__
Опять же это делает 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
Но это определенно изменит больше поведения! Например:
>>> if ArrayWrapper([1,2,3]):
... print('that was previously impossible!')
that was previously impossible!
Очень очень интересный. –
Является ли это неоднородным списком в качестве примера, или у вас действительно есть список со многими различными типами в нем? – mgilson
@mgilson только мой надуманный пример. Я работаю со списком массивов numpy равного размера – Lee88