Индексирование
См. также
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 = np.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 - это массив объекта булевого типа, который может быть возвращен операторами сравнения. Один массив булевых индексов практически идентичен 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]])
Комбинирование нескольких булевых массивов индексирования или булевого с целочисленным массивом индексирования лучше всего понять с помощью аналогии obj.nonzero(). Функция ix_ также поддерживает булевы массивы и будет работать без неожиданностей.
Пример
Используйте булевое индексирование для выбора всех строк, сумма которых является четным числом. Одновременно столбцы 0 и 2 должны выбираться с помощью расширенного целочисленного индекса. С помощью функции ix_ это можно сделать с помощью:
>>> x = np.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-flat. Форма любого возвращаемого массива, следовательно, соответствует форме объекта индексирования целыми числами.
© 2005–2020 NumPy Developers
Licensed under the 3-clause BSD License.
https://numpy.org/doc/1.19/reference/arrays.indexing.html