Индексирование
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-й элемент формы массива. Отрицательные индексы интерпретируются как счёт от конца массива (т.е., если
, это означает
).
Все массивы, сгенерированные с помощью базовых срезов, всегда являются views исходного массива.
Стандартные правила срезов последовательностей применяются к базовым срезам на основе каждой размерности (включая использование индекса шага). Некоторые полезные понятия для запоминания:
-
Синтаксис базового среза —
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 и -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можно использовать во всех операциях срезов для создания оси длиной один. :const:newaxis— псевдоним для ‘None’, и ‘None’ можно использовать вместо него с тем же результатом.
Расширенное индексирование
Расширенное индексирование инициируется, когда объект выбора, obj, является объектом некортежной последовательности, ndarray (типа данных целое число или булево), или кортежем, содержащим по крайней мере один объект последовательности или ndarray (типа данных целое число или булево). Существуют два типа расширенного индексирования: целые числа и булевы.
Расширенное индексирование всегда возвращает копию данных (в отличие от базового среза, который возвращает view).
Предупреждение
Определение расширенного индексирования означает, что 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. Порядок поиска будет row-major, стиль 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], 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'] возвращает новое view к массиву, форма которого такая же, как у 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-contiguous, с последним индексом, изменяющимся быстрее всего). Объект итератора также можно индексировать с помощью базового среза или продвинутой индексации, при условии, что объект выбора не является кортежем. Это должно быть ясно из того факта, что x.flat представляет собой одномерный вид. Он может использоваться для целочисленной индексации с одномерными C-стилевыми плоскими индексами. Форма любого возвращаемого массива, следовательно, является формой объекта целочисленной индексации.
© 2008–2016 NumPy Developers
Licensed under the NumPy License.
https://docs.scipy.org/doc/numpy-1.11.0/reference/arrays.indexing.html