Индексирование
ndarrays может быть индексировано с использованием стандартного синтаксиса Python x[obj] , где x — массив, а obj — выбор. Доступны три вида индексирования: доступ к полю, базовый срез, расширенное индексирование. Какой вид произойдёт, зависит от obj.
Примечание
В Python, x[(exp1, exp2, ..., expN)] эквивалентно x[exp1, exp2, ..., expN]; последнее — это просто синтаксический сахар для первого.
Базовый срез и индексирование
Базовый срез расширяет базовое понятие среза Python до N измерений. Базовый срез происходит, когда obj является объектом slice (созданным с помощью start:stop:step обозначения в скобках), целым числом или кортежем из объектов среза и целых чисел. Ellipsis и newaxis объекты также могут быть вставлены. Для сохранения обратной совместимости с общим использованием в Numeric, базовый срез также инициируется, если объект выбора — любая последовательность, не являющаяся ndarray (например, list) содержащая slice объекты, объект Ellipsis или объект newaxis, но не для целочисленных массивов или других вложенных последовательностей.
В самом простом случае индексирования с N целыми числами возвращается скаляр массива, представляющий соответствующий элемент. Как и в Python, все индексы нулевые: для i-го индекса
, допустимый диапазон равен
, где
— это i-й элемент формы массива. Отрицательные индексы интерпретируются как отсчёт от конца массива (т.е., если
, это означает
).
Все массивы, сгенерированные базовым срезом, всегда являются views исходного массива.
Стандартные правила среза последовательностей применяются к базовому срезу на основе каждого измерения (включая использование индекса шага). Несколько полезных понятий для запоминания включают:
-
Синтаксис базового среза —
i:j:k, где i — начальный индекс, j — индекс окончания, а k — шаг (
). Это выбирает m элементов (в соответствующем измерении) с индексными значениями i, i + k, ..., i + (m - 1) k, где
и q и r — частное и остаток, полученные при делении j - i на k: j - i = q k + r, так что i + (m - 1) k < j.Пример
>>> x = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) >>> x[1:7:2] array([1, 3, 5])
-
Отрицательные i и j интерпретируются как n + i и n + j, где n — количество элементов в соответствующем измерении. Отрицательный k меняет направление шага на меньшие индексы.
Пример
>>> x[-2:10] array([8, 9]) >>> x[-3:3:-1] array([7, 6, 5, 4])
-
Предположим, что n — количество элементов в измерении, которое разрезается. Если i не задано, по умолчанию оно равно 0 для k > 0 и n - 1 для k < 0. Если j не задано, оно по умолчанию равно n для k > 0 и -1 для k < 0. Если k не задано, оно по умолчанию равно 1. Обратите внимание, что
::эквивалентно:и означает выбор всех индексов вдоль этой оси.Пример
>>> x[5:] array([5, 6, 7, 8, 9])
-
Если количество объектов в кортеже выбора меньше N, то
:предполагается для всех последующих измерений.Пример
>>> x = np.array([[[1],[2],[3]], [[4],[5],[6]]]) >>> x.shape (2, 3, 1) >>> x[1:2] array([[[4], [5], [6]]]) -
Ellipsisрасширяются до количества:объектов, необходимых для создания кортежа выбора такой же длины, как и уx.ndim. Может быть только один эллипсис.Пример
>>> x[...,0] array([[1, 2, 3], [4, 5, 6]]) -
Каждый объект
newaxisв кортеже выбора расширяет размерность полученного среза на одномерное измерение единичной длины. Добавляемое измерение — позиция объектаnewaxisв кортеже выбора.Пример
>>> x[:,np.newaxis,:,:].shape (2, 1, 3, 1)
- Целое число i возвращает те же значения, что и
i:i+1за исключением того, что размерность возвращаемого объекта уменьшается на 1. В частности, кортеж выбора с p-м элементом — целым числом (а все другие записи:) возвращает соответствующий подмассив с размерностью N - 1. Если N = 1, то возвращаемый объект — скаляр массива. Эти объекты описаны в Скаляры. - Если кортеж выбора имеет все записи
:за исключением p-й записи, которая является объектом срезаi:j:k, то возвращаемый массив имеет размерность N, образованную конкатенацией подмассивов, возвращаемых целочисленным индексированием элементов i, i+k, ..., i + (m - 1) k < j, -
Базовый срез с более чем одной не-
:записью в кортеже среза работает как повторяемое применение среза с одной не-:записью, где не-:записи последовательно выбираются (при этом все другие не-:записи заменяются на:). Таким образом,x[ind1,...,ind2,:]работает какx[ind1][...,ind2,:]при базовом срезе.Предупреждение
Вышесказанное не верно для расширенного индексирования.
- Вы можете использовать срез для установки значений в массив, но (в отличие от списков) вы никогда не сможете увеличить массив. Размер значения, которое нужно установить в
x[obj] = value, должен быть (совместимым с операцией расширения) такой же формы, как и уx[obj].
Примечание
Помните, что кортеж среза всегда может быть построен как obj и использован в x[obj] обозначении. Объекты среза могут быть использованы в конструкции вместо [start:stop:step] обозначения. Например, x[1:10:5,::-1] также можно реализовать как obj = (slice(1,10,5), slice(None,None,-1)); x[obj] . Это может быть полезно для создания универсального кода, работающего с массивами произвольной размерности.
-
numpy.newaxis -
Объект
newaxisможет использоваться во всех операциях среза для создания оси длиной один. :const:newaxis— псевдоним для ‘None’, и ‘None’ можно использовать вместо него с тем же результатом.
Расширенное индексирование
Расширенное индексирование вызывается, когда объект выбора, obj, — это последовательность объектов, не являющаяся кортежем, ndarray (типа данных целое число или булево), или кортеж, содержащий как минимум один объект последовательности или ndarray (типа данных целое число или булево). Существуют два типа расширенного индексирования: целочисленное и булево.
Расширенное индексирование всегда возвращает копию данных (в отличие от базового среза, который возвращает view).
Предупреждение
Определение расширенного индексирования означает, что x[(1,2,3),] фундаментально отличается от x[(1,2,3)]. Последнее эквивалентно x[1,2,3] , что вызовет базовый выбор, в то время как первое вызовет расширенное индексирование. Убедитесь, что вы понимаете, почему это происходит.
Также учтите, что x[[1,2,3]] вызовет расширенное индексирование, а x[[1,2,slice(None)]] — базовый срез.
Целочисленное индексирование
Целочисленное индексирование позволяет выбирать произвольные элементы в массиве на основе их N-мерного индекса. Каждый целочисленный массив представляет ряд индексов в этом измерении.
Чисто целочисленное индексирование
Когда индекс состоит из такого же количества целочисленных массивов, как измерений индексируемого массива, индексирование прямолинейно, но отличается от среза.
Расширенные индексы всегда подвергаются расширению и обрабатываются как один:
result[i_1, ..., i_M] == x[ind_1[i_1, ..., i_M], ind_2[i_1, ..., i_M],
..., ind_N[i_1, ..., i_M]]
Обратите внимание, что форма результата совпадает с формами массивов индексирования (с расширением) ind_1, ..., ind_N.
Пример
Из каждой строки должен быть выбран определённый элемент. Индекс строки — просто [0, 1, 2], а индекс столбца определяет элемент для выбора в соответствующей строке, здесь [0, 1, 0]. Используя оба вместе, задачу можно решить с помощью расширенного индексирования:
>>> x = np.array([[1, 2], [3, 4], [5, 6]]) >>> x[[0, 1, 2], [0, 1, 0]] array([1, 4, 5])
Для достижения поведения, аналогичного базовому срезу выше, можно использовать расширение. Функция ix_ может помочь с этим расширением. Лучше всего это понять на примере.
Пример
Из массива 4x3 должны быть выбраны угловые элементы с помощью расширенного индексирования. Таким образом, необходимо выбрать все элементы, для которых столбец является одним из [0, 2] и строка является одним из [0, 3]. Чтобы использовать расширенное индексирование, нужно выбрать все элементы явным образом. Используя описанный ранее метод, можно написать:
>>> x = array([[ 0, 1, 2],
... [ 3, 4, 5],
... [ 6, 7, 8],
... [ 9, 10, 11]])
>>> rows = np.array([[0, 0],
... [3, 3]], dtype=np.intp)
>>> columns = np.array([[0, 2],
... [0, 2]], dtype=np.intp)
>>> x[rows, columns]
array([[ 0, 2],
[ 9, 11]])
Однако, поскольку массивы индексирования выше просто повторяются, расширение может быть использовано (сравните операции, такие как rows[:, np.newaxis] + columns) для упрощения этого:
>>> rows = np.array([0, 3], dtype=np.intp)
>>> columns = np.array([0, 2], dtype=np.intp)
>>> rows[:, np.newaxis]
array([[0],
[3]])
>>> x[rows[:, np.newaxis], columns]
array([[ 0, 2],
[ 9, 11]])
Это расширение также можно получить с помощью функции ix_:
>>> x[np.ix_(rows, columns)]
array([[ 0, 2],
[ 9, 11]])
Обратите внимание, что без вызова np.ix_, будут выбраны только диагональные элементы, как и в предыдущем примере. Это различие — самое важное, что нужно запомнить об индексировании с несколькими расширенными индексами.
Объединение расширенного и базового индексирования
Когда в индексе есть хотя бы один срез (:), эллипсис (...) или np.newaxis (или массив имеет больше измерений, чем расширенных индексов), поведение может быть более сложным. Оно похоже на конкатенацию результата индексирования для каждого элемента расширенного индекса
В простейшем случае существует только один расширенный индекс. Один расширенный индекс может, например, заменить срез, и результирующий массив будет таким же, однако это копия, и она может иметь другую структуру памяти. Срез предпочтительнее, когда это возможно.
Пример
>>> x[1:2, 1:3] array([[4, 5]]) >>> x[1:2, [1, 2]] array([[4, 5]])
Самый простой способ понять ситуацию — это подумать в терминах формы результата. Операция индексирования состоит из двух частей: подпространства, определяемого базовым индексированием (исключая целые числа), и подпространства из части расширенного индексирования. Необходимо различать два случая комбинирования индексов:
- Расширенные индексы разделены срезом, эллипсисом или newaxis. Например,
x[arr1, :, arr2]. - Расширенные индексы находятся рядом друг с другом. Например,
x[..., arr1, arr2, :]но неx[arr1, :, 1]так как1в этом отношении является расширенным индексом.
В первом случае измерения, полученные в результате операции расширенного индексирования, стоят первыми в результирующем массиве, а измерения подпространства — после. Во втором случае измерения, полученные от операций расширенного индексирования, вставляются в результирующий массив в той же позиции, что и в исходном массиве (последняя логика делает простое расширенное индексирование похожим на срезы).
Пример
Предположим, что x.shape равен (10,20,30) и ind — это массив индексов формы (2,3,4) intp, тогда result = x[...,ind,:] имеет форму (10,2,3,4,30), потому что подпространство формы (20,) было заменено подпространством индексов формы (2,3,4) с использованием трансляции. Если мы запустим циклы i, j, k по подпространству формы (2,3,4), то result[...,i,j,k,:] = x[...,ind[i,j,k],:]. Этот пример дает тот же результат, что и x.take(ind, axis=-2).
Пример
Пусть x.shape равен (10,20,30,40,50) и предположим, что ind_1 и ind_2 могут быть транслированы в форму (2,3,4). Тогда x[:,ind_1,ind_2] имеет форму (10,2,3,4,40,50), потому что подпространство формы (20,30) из X было заменено подпространством (2,3,4) из индексов. Однако x[:,ind_1,:,ind_2] имеет форму (2,3,4,10,30,50), потому что нет однозначного места для вставки подпространства индексов, поэтому оно прикрепляется к началу. Всегда можно использовать .transpose(), чтобы переместить подпространство в любое нужное место. Обратите внимание, что этот пример нельзя воспроизвести с помощью take.
Индексирование булевыми массивами
Это расширенное индексирование происходит, когда obj — это массив объекта булевого типа, например, тот, что может быть возвращен операторами сравнения. Один булевский массив индексов практически идентичен x[obj.nonzero()] , где, как описано выше, obj.nonzero() возвращает кортеж (длины obj.ndim) целочисленных массивов индексов, показывающих True элементы obj. Однако это быстрее, когда obj.shape == x.shape.
Если obj.ndim == x.ndim, x[obj] возвращает одномерный массив, заполненный элементами x, соответствующими True значениям obj. Порядок поиска будет row-major, C-стиль. Если obj имеет True значения в позициях, которые находятся за пределами границ x, то будет возбуждено исключение индекса. Если obj меньше x, это идентично заполнению его False.
Пример
Часто используется для фильтрации значений элементов. Например, можно выбрать все элементы из массива, которые не равны NaN:
>>> x = np.array([[1., 2.], [np.nan, 3.], [np.nan, np.nan]]) >>> x[~np.isnan(x)] array([ 1., 2., 3.])
Или добавить константу ко всем отрицательным элементам:
>>> x = np.array([1., -1., -2., 3]) >>> x[x < 0] += 20 >>> x array([ 1., 19., 18., 3.])
В общем случае, если индекс включает булевский массив, результат будет идентичен вставке obj.nonzero() в ту же позицию и применению механизма индексирования целочисленного массива, описанного выше. x[ind_1, boolean_array, ind_2] эквивалентно x[(ind_1,) + boolean_array.nonzero() + (ind_2,)].
Если присутствует только один булевский массив и нет целочисленного массива индексов, это просто. Нужно только убедиться, что булевский индекс имеет точно столько измерений, сколько нужно.
Пример
Из массива выберите все строки, сумма элементов которых меньше или равна двум:
>>> x = np.array([[0, 1], [1, 1], [2, 2]])
>>> rowsum = x.sum(-1)
>>> x[rowsum <= 2, :]
array([[0, 1],
[1, 1]])
Но если rowsum тоже имеет два измерения:
>>> rowsum = x.sum(-1, keepdims=True) >>> rowsum.shape (3, 1) >>> x[rowsum <= 2, :] # fails IndexError: too many indices >>> x[rowsum <= 2] array([0, 1])
Последний пример дает только первые элементы из-за дополнительного измерения. Сравните rowsum.nonzero() для понимания этого примера.
Объединение нескольких булевских массивов индексирования или булевского с целочисленным массивом индексирования лучше всего понять с помощью аналогии obj.nonzero(). Функция ix_ также поддерживает булевские массивы и будет работать без неожиданностей.
Пример
Используйте булевское индексирование для выбора всех строк, сумма элементов которых — четное число. В то же время, столбцы 0 и 2 должны быть выбраны с помощью расширенного целочисленного индекса. С помощью функции ix_ это можно сделать следующим образом:
>>> x = array([[ 0, 1, 2],
... [ 3, 4, 5],
... [ 6, 7, 8],
... [ 9, 10, 11]])
>>> rows = (x.sum(-1) % 2) == 0
>>> rows
array([False, True, False, True], dtype=bool)
>>> columns = [0, 2]
>>> x[np.ix_(rows, columns)]
array([[ 3, 5],
[ 9, 11]])
Без вызова np.ix_ были бы выбраны только диагональные элементы.
Или без np.ix_ (сравните примеры целочисленных массивов):
>>> rows = rows.nonzero()[0]
>>> x[rows[:, np.newaxis], columns]
array([[ 3, 5],
[ 9, 11]])
Подробные замечания
Это некоторые подробные замечания, которые не важны для повседневного индексирования (в произвольном порядке):
- Родной тип индексирования NumPy —
intpи может отличаться от стандартного типа целочисленного массива.intp— это наименьший тип данных, достаточный для безопасного индексирования любого массива; для расширенного индексирования он может быть быстрее, чем другие типы. - Для расширенных присваиваний нет гарантии порядка итераций. Это означает, что если элемент устанавливается более одного раза, невозможно предсказать конечный результат.
- Пустой (кортеж) индекс — это полный скалярный индекс в нульмерном массиве.
x[()]возвращает скаляр, еслиxнульмерный, и представление в противном случае. С другой стороны,x[...]всегда возвращает представление. - Если нульмерный массив присутствует в индексе и это целочисленный индекс, результат будет скаляром, а не нульмерным массивом. (Расширенное индексирование не срабатывает.)
- Когда эллипсис (
...) присутствует, но не имеет размера (т.е. заменяет ноль:), результат все равно всегда будет массивом. Представление, если нет расширенного индекса, иначе копия. - эквивалентность
nonzeroдля булевских массивов не сохраняется для нульмерных булевских массивов. - Когда результат операции расширенного индексирования не содержит элементов, но отдельный индекс выходит за пределы, возбуждается ли
IndexError— не определено (например,x[[], [123]]с123выходит за пределы). - Когда во время присваивания возникает ошибка преобразования (например, обновление числового массива с помощью последовательности строк), массив, которому присваивается значение, может оказаться в неопределённом частично обновлённом состоянии. Однако, если возникает любая другая ошибка (например, индекс выходит за пределы), массив останется неизменным.
- Структура памяти результата расширенного индексирования оптимизирована для каждой операции индексирования, и нельзя предполагать какой-либо конкретный порядок памяти.
- При использовании подкласса (особенно того, который изменяет свою форму), стандартное поведение
ndarray.__setitem__вызовет__getitem__для базового индексирования, но не для расширенного индексирования. Для такого подкласса предпочтительнее вызватьndarray.__setitem__с представлением ndarray базового класса на данных. Это должно быть сделано, если подклассы__getitem__не возвращают представления.
Доступ к полям массива
См. также
Если объект ndarray является структурированным массивом, то fields массива может быть получен путем индексирования массива строками, подобно словарям.
Индексирование x['field-name'] возвращает новое view к массиву, форма которого такая же, как у x (за исключением подмассивов) но типа x.dtype['field-name'] и содержит только часть данных в указанном поле. Также скаляры массива записей могут быть «индексированы» таким образом.
Индексирование в структурированный массив также можно выполнить с помощью списка имен полей, например x[['field-name1','field-name2']]. В настоящее время это возвращает новый массив, содержащий копию значений в указанных полях. С NumPy 1.7 возврат копии устарел в пользу возврата представления. Копия по-прежнему будет возвращаться, но будет выведено FutureWarning при записи в копию. Если вы зависите от текущего поведения, то мы рекомендуем копировать возвращаемый массив явно, т.е. используйте x[[‘field-name1’,’field-name2’]].copy(). Это будет работать и с прошлыми, и с будущими версиями NumPy.
Если доступное поле является подмассивом, измерения подмассива добавляются к форме результата.
Пример
>>> x = np.zeros((2,2), dtype=[('a', np.int32), ('b', np.float64, (3,3))])
>>> x['a'].shape
(2, 2)
>>> x['a'].dtype
dtype('int32')
>>> x['b'].shape
(2, 2, 3, 3)
>>> x['b'].dtype
dtype('float64')
Индексирование плоского итератора
x.flat возвращает итератор, который будет перебирать весь массив (в стиле C-contiguous, при этом последний индекс изменяется быстрее всего). Объект этого итератора также можно индексировать с помощью базового среза или продвинутой индексации, если только объект выбора не является кортежем. Это должно быть ясно из того факта, что x.flat является одномерным представлением. Его можно использовать для целочисленной индексации с одномерными индексами в стиле C-flat. Форма любого возвращаемого массива, следовательно, соответствует форме объекта целочисленной индексации.
© 2008–2016 NumPy Developers
Licensed under the NumPy License.
https://docs.scipy.org/doc/numpy-1.10.1/reference/arrays.indexing.html