Индексирование
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-й элемент формы массива. Отрицательные индексы интерпретируются как отсчёт с конца массива (т.е., если
, это означает
).
Все массивы, созданные с помощью базового среза, всегда являются представлениями исходного массива.
Стандартные правила среза последовательностей применяются к базовому срезу для каждого измерения (включая использование индекса шага). Некоторые полезные понятия, которые следует запомнить:
-
Базовый синтаксис среза —
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 и -n-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может использоваться во всех операциях среза для создания оси длины один.newaxis— псевдоним для ‘None’, и ‘None’ можно использовать вместо него с тем же результатом.
Расширенное индексирование
Расширенное индексирование срабатывает, когда объект выбора obj — это последовательность, не являющаяся кортежем, ndarray (типа данных целое число или логическое), или кортеж с по крайней мере одним объектом последовательности или ndarray (типа данных целое число или логическое). Существуют два типа расширенного индексирования: целочисленное и булево.
Расширенное индексирование всегда возвращает копию данных (в отличие от базового среза, который возвращает представление).
Предупреждение
Определение расширенного индексирования означает, что 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. Порядок поиска будет строчно-последовательный, 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])
>>> 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 является структурированным массивом, то его поля можно получить, индексируя массив строками, подобно работе с словарями.
Индексирование x['field-name'] возвращает новое представление массива, форма которого такая же, как у 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 с наибольшей скоростью изменения последнего индекса). Объект этого итератора также можно индексировать с помощью базовых срезов или расширенной индексации, пока объект выбора не является кортежем. Это должно быть ясно из того, что x.flat представляет собой одномерный вид. Его можно использовать для целочисленной индексации с одномерными индексами в стиле C. Таким образом, форма любого возвращаемого массива соответствует форме объекта целочисленной индексации.
© 2005–2019 NumPy Developers
Licensed under the 3-clause BSD License.
https://docs.scipy.org/doc/numpy-1.14.5/reference/arrays.indexing.html