Индексирование
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 — это массив типа Boolean, например, полученный из операторов сравнения. Один булевый массив индексов практически идентичен 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, то будет поднято исключение IndexError. Если 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 является структурированным массивом, к полям массива можно получить доступ, индексируя массив строками, как в словаре.
Индексирование 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. Таким образом, форма любого возвращаемого массива является формой объекта индексирования целыми числами.
© 2008–2017 NumPy Developers
Licensed under the NumPy License.
https://docs.scipy.org/doc/numpy-1.13.0/reference/arrays.indexing.html