Индексирование
См. также
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 объекты также могут быть вставлены между ними.
Устарело начиная с версии 1.15.0: Для обеспечения обратной совместимости со стандартным применением в Numeric, базовая нарезка также инициируется, если объект выбора — любая последовательность, не являющаяся ndarray и не являющаяся кортежем (такая как list), содержащая slice объекты, объект Ellipsis или объект newaxis, но не для целочисленных массивов или других вложенных последовательностей.
Простейший случай индексирования с N целыми числами возвращает скаляр массива, представляющий соответствующий элемент. Как и в Python, все индексы нулевые: для i-го индекса , допустимый диапазон —
, где
— i-й элемент формы массива. Отрицательные индексы интерпретируются как отсчет от конца массива (т.е., если
, это означает
).
Все массивы, сгенерированные с помощью базовой нарезки, всегда являются видами исходного массива.
Примечание
Нарезка NumPy создаёт вид, а не копию, как и в случае встроенных Python-последовательностей, таких как строка, кортеж и список. Необходимо быть внимательным при извлечении небольшой части из большого массива, которая становится бесполезной после извлечения, поскольку извлеченная небольшая часть содержит ссылку на большой исходный массив, память которого не будет освобождена, пока все массивы, полученные из него, не будут удалены сборщиком мусора. В таких случаях рекомендуется явное copy().
Стандартные правила нарезки последовательностей применяются к базовой нарезке в каждом измерении (включая использование индекса шага). Некоторые полезные понятия, которые следует запомнить:
-
Базовый синтаксис нарезки —
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]] вызовет расширенное индексирование, в то время как из-за устаревшей совместимости с Numeric, 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_, будут выбраны только диагональные элементы, как и в предыдущем примере. Эта разница является наиболее важным моментом, который следует запомнить об индексации с помощью нескольких расширенных индексов.
Объединение расширенной и базовой индексации
Когда в индексе присутствует хотя бы один срез (:), эллипсис (...) или newaxis (или массив имеет больше измерений, чем расширенных индексов), поведение может быть более сложным. Это похоже на конкатенацию результата индексации для каждого элемента расширенного индекса
В простейшем случае имеется только один расширенный индекс. Один расширенный индекс может, например, заменить срез, и результирующий массив будет таким же, однако он является копией и может иметь другое расположение в памяти. Срез предпочтительнее, когда это возможно.
Пример
>>> x[1:2, 1:3] array([[4, 5]]) >>> x[1:2, [1, 2]] array([[4, 5]])
Самый простой способ понять ситуацию — это подумать в терминах формы результата. Операция индексации имеет две части: подпространство, определённое базовой индексацией (исключая целые числа), и подпространство от части расширенной индексации. Необходимо различать два случая комбинирования индексов:
- Расширенные индексы разделены срезом,
Ellipsisилиnewaxis. Например,x[arr1, :, arr2]. - Расширенные индексы расположены друг за другом. Например,
x[..., arr1, arr2, :]но неx[arr1, :, 1]поскольку1в этом случае является расширенным индексом.
В первом случае измерения, полученные в результате расширенной индексации, стоят первыми в результирующем массиве, а измерения подпространства — после этого. Во втором случае измерения от операций расширенной индексации вставляются в результирующий массив в том же месте, что и в исходном массиве (последняя логика делает простую расширенную индексацию аналогичной срезу).
Пример
Предположим, что x.shape имеет форму (10,20,30) и ind — это индексный массив формы (2,3,4), тогда 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, то будет поднято исключение индекса. Если 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.16 это возвращает вид, содержащий только эти поля. В более ранних версиях 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. Поэтому форма любого возвращаемого массива соответствует форме объекта целочисленного индексирования.
© 2005–2020 NumPy Developers
Licensed under the 3-clause BSD License.
https://numpy.org/doc/1.18/reference/arrays.indexing.html