Индексирование
См. также
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 — это массив расширенной индексации типа intp формы (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, то будет поднято исключение 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]])
Объединение нескольких булевых массивов индексации или булевого массива с массивом целочисленной индексации лучше всего понимается с помощью аналогии с 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–2021 NumPy Developers
Licensed under the 3-clause BSD License.
https://numpy.org/doc/1.20/reference/arrays.indexing.html