Индексация по ndarrays#
Смотрите также
ndarrays может быть проиндексирован с использованием стандартного Python
x[obj] синтаксис, где x является массивом и obj выбор. Доступны различные виды индексирования в зависимости от obj: базовое индексирование, расширенное индексирование и доступ к полям.
Большинство следующих примеров показывают использование индексации при обращении к данным в массиве. Примеры работают так же хорошо при присваивании массиву. См. Присвоение значений индексированным массивам для конкретных примеров и объяснений того, как работают присваивания.
Обратите внимание, что в Python, x[(exp1, exp2, ..., expN)] эквивалентно
x[exp1, exp2, ..., expN]; последнее – это просто синтаксический сахар для первого.
Базовое индексирование#
Индексирование одного элемента#
Индексирование одного элемента работает точно так же, как и для других стандартных последовательностей Python. Оно начинается с 0 и принимает отрицательные индексы для индексирования с конца массива.
>>> x = np.arange(10)
>>> x[2]
2
>>> x[-2]
8
Не обязательно разделять индекс каждого измерения в свой собственный набор квадратных скобок.
>>> x.shape = (2, 5) # now x is 2-dimensional
>>> x[1, 3]
8
>>> x[1, -1]
9
Обратите внимание, что если индексировать многомерный массив с меньшим количеством индексов, чем измерений, получается подмерный массив. Например:
>>> x[0]
array([0, 1, 2, 3, 4])
То есть каждый указанный индекс выбирает массив, соответствующий остальным выбранным измерениям. В приведённом выше примере выбор 0 означает, что оставшееся измерение длиной 5 остаётся неуказанным, и возвращается массив такой размерности и размера. Следует отметить, что возвращаемый массив является представление, т.е. это не копия оригинала, а указывает на те же значения в памяти, что и исходный массив. В этом случае возвращается одномерный массив на первой позиции (0). Таким образом, использование одного индекса для возвращаемого массива приводит к возврату одного элемента. То есть:
>>> x[0][2]
2
Так что обратите внимание, что x[0, 2] == x[0][2] хотя второй случай менее эффективен, так как создается новый временный массив после первого индекса, который затем индексируется 2.
Примечание
NumPy использует индексацию в стиле C. Это означает, что последний индекс обычно представляет наиболее быстро изменяющееся местоположение в памяти, в отличие от Fortran или IDL, где первый индекс представляет наиболее быстро изменяющееся местоположение в памяти. Это различие представляет большой потенциал для путаницы.
Срезы и шаги#
Базовое срезы расширяет базовую концепцию срезов Python до N
измерений. Базовое срезы происходит, когда obj является slice объект
(созданный start:stop:step обозначение внутри скобок),
целое число или кортеж объектов среза и целых чисел. Ellipsis
и newaxis объекты могут быть перемежаемы с ними.
Простейший случай индексации с N целые числа возвращают массив скаляр представляющий соответствующий элемент. Как в Python, все индексы начинаются с нуля: для i-й индекс \(n_i\), допустимый диапазон составляет \(0 \le n_i < d_i\) где \(d_i\) является i-й элемент формы массива. Отрицательные индексы интерпретируются как отсчёт с конца массива (т.е., если \(n_i < 0\), это означает \(n_i + d_i\)).
Все массивы, созданные базовой индексацией, всегда представления исходного массива.
Примечание
Срез в NumPy создает представление вместо копирования, как в случае встроенных последовательностей Python, таких как строка, кортеж и список.
Необходимо соблюдать осторожность при извлечении небольшой части из большого массива, который становится бесполезным после извлечения, потому что извлеченная небольшая часть содержит ссылку на большой исходный массив, чья память не будет освобождена, пока все массивы, производные от него, не будут собраны сборщиком мусора. В таких случаях требуется явное copy() рекомендуется.
Стандартные правила среза последовательностей применяются к базовому срезу на основе каждого измерения (включая использование индекса шага). Некоторые полезные концепции, которые стоит помнить, включают:
Базовый синтаксис срезов
i:j:kгде i является начальным индексом, j является конечным индексом, и k это шаг (\(k\neq0\)). Это выбирает m элементы (в соответствующем измерении) со значениями индекса i, i + k, …, i + (m - 1) k где \(m = q + (r\neq0)\) и q и r являются частным и остатком, полученными при делении j - i by 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]]])
Целое число, i, возвращает те же значения, что и
i:i+1кроме размерность возвращаемого объекта уменьшается на 1. В частности, кортеж выбора с p-й элемент является целым числом (и все остальные записи:) возвращает соответствующий подмассив с размерностью N - 1. Если N = 1 тогда возвращаемый объект является скаляром массива. Эти объекты объясняются в Скаляры.Если кортеж выбора содержит все записи
:за исключением p-й элемент, который является объектом срезаi:j:k, тогда возвращаемый массив имеет размерность N сформированный путем стекирования, вдоль p-я ось, подмассивы, возвращаемые целочисленной индексацией элементов 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]. Это может быть полезно для создания универсального кода, работающего с массивами произвольной размерности. См. Работа с переменным количеством индексов в программах для получения дополнительной информации.
Инструменты многомерной индексации#
Существуют некоторые инструменты для облегчения простого сопоставления форм массивов с выражениями и в присваиваниях.
Ellipsis расширяется до количества : объектов, необходимых для
кортежа выбора для индексации всех измерений. В большинстве случаев это означает, что
длина расширенного кортежа выбора равна x.ndim. Может присутствовать только одно многоточие.
Из приведенного выше примера:
>>> x[..., 0]
array([[1, 2, 3],
[4, 5, 6]])
Это эквивалентно:
>>> x[:, :, 0]
array([[1, 2, 3],
[4, 5, 6]])
Каждый newaxis объект в кортеже выбора служит для расширения
размерностей результирующего выбора на одну единичную
размерность. Добавленная размерность — это позиция newaxis
объект в кортеже выбора. newaxis является псевдонимом для
None, и None может быть использован вместо этого с тем же результатом.
Из приведённого выше примера:
>>> x[:, np.newaxis, :, :].shape
(2, 1, 3, 1)
>>> x[:, None, :, :].shape
(2, 1, 3, 1)
Это может быть удобно для объединения двух массивов способом, который в противном случае потребовал бы явных операций изменения формы. Например:
>>> x = np.arange(5)
>>> x[:, np.newaxis] + x[np.newaxis, :]
array([[0, 1, 2, 3, 4],
[1, 2, 3, 4, 5],
[2, 3, 4, 5, 6],
[3, 4, 5, 6, 7],
[4, 5, 6, 7, 8]])
Расширенная индексация#
Расширенная индексация активируется, когда объект выбора, obj, является
некортежным последовательным объектом, ndarray (типа данных integer или bool), или кортеж, содержащий хотя бы один объект последовательности или ndarray (типа данных integer или bool). Существует два типа расширенной индексации: целочисленная и булева.
Расширенная индексация всегда возвращает copy данных (в отличие от базовой индексации, которая возвращает представление).
Предупреждение
Определение расширенного индексирования означает, что x[(1, 2, 3),] принципиально отличается от x[(1, 2, 3)]. Последнее эквивалентно x[1, 2, 3] что вызовет базовый выбор, в то время как первое вызовет расширенную индексацию. Убедитесь, что понимаете, почему это происходит.
Целочисленная индексация массивов#
Целочисленная индексация массива позволяет выбирать произвольные элементы в массиве на основе их N-мерный индекс. Каждый целочисленный массив представляет несколько индексов в этом измерении.
Отрицательные значения разрешены в индексных массивах и работают так же, как с одиночными индексами или срезами:
>>> x = np.arange(10, 1, -1)
>>> x
array([10, 9, 8, 7, 6, 5, 4, 3, 2])
>>> x[np.array([3, 3, 1, 8])]
array([7, 7, 9, 2])
>>> x[np.array([3, 3, -3, 8])]
array([7, 7, 4, 2])
Если значения индекса выходят за границы, то IndexError выбрасывается:
>>> x = np.array([[1, 2], [3, 4], [5, 6]])
>>> x[np.array([1, -1])]
array([[3, 4],
[5, 6]])
>>> x[np.array([3, 4])]
Traceback (most recent call last):
...
IndexError: index 3 is out of bounds for axis 0 with size 3
Когда индекс состоит из такого же количества целочисленных массивов, как и размерностей индексируемого массива, индексирование выполняется напрямую, но отличается от среза.
Расширенные индексы всегда broadcast и итерируется как один:
result[i_1, ..., i_M] == x[ind_1[i_1, ..., i_M], ind_2[i_1, ..., i_M],
..., ind_N[i_1, ..., i_M]]
Обратите внимание, что результирующая форма идентична формам массива индексации
(после broadcast) ind_1, ..., ind_N. Если индексы не могут быть приведены к
одинаковой форме, исключение IndexError: shape mismatch: indexing arrays could
not be broadcast together with shapes... вызывается исключение.
Индексирование с многомерными индексными массивами, как правило, является более необычным использованием, но оно разрешено и полезно для некоторых задач. Мы начнем с простейшего многомерного случая:
>>> y = np.arange(35).reshape(5, 7)
>>> y
array([[ 0, 1, 2, 3, 4, 5, 6],
[ 7, 8, 9, 10, 11, 12, 13],
[14, 15, 16, 17, 18, 19, 20],
[21, 22, 23, 24, 25, 26, 27],
[28, 29, 30, 31, 32, 33, 34]])
>>> y[np.array([0, 2, 4]), np.array([0, 1, 2])]
array([ 0, 15, 30])
В этом случае, если массивы индексов имеют совпадающую форму и существует
массив индексов для каждого измерения индексируемого массива, результирующий
массив имеет ту же форму, что и массивы индексов, а значения соответствуют
набору индексов для каждой позиции в массивах индексов. В этом примере
первое значение индекса равно 0 для обоих массивов индексов, и поэтому первое значение
результирующего массива равно y[0, 0]. Следующее значение — y[2, 1], и
последний является y[4, 2].
Если индексные массивы не имеют одинаковой формы, предпринимается попытка транслировать их к одной форме. Если их нельзя транслировать к одной форме, возникает исключение:
>>> y[np.array([0, 2, 4]), np.array([0, 1])]
Traceback (most recent call last):
...
IndexError: shape mismatch: indexing arrays could not be broadcast
together with shapes (3,) (2,)
Механизм broadcasting позволяет комбинировать массивы индексов со скалярами для других индексов. Эффект заключается в том, что скалярное значение используется для всех соответствующих значений массивов индексов:
>>> y[np.array([0, 2, 4]), 1]
array([ 1, 15, 29])
Переходя на следующий уровень сложности, можно частично индексировать массив с помощью массивов индексов. Требуется немного подумать, чтобы понять, что происходит в таких случаях. Например, если мы используем только один массив индексов с y:
>>> y[np.array([0, 2, 4])]
array([[ 0, 1, 2, 3, 4, 5, 6],
[14, 15, 16, 17, 18, 19, 20],
[28, 29, 30, 31, 32, 33, 34]])
Это приводит к построению нового массива, где каждое значение индексного массива выбирает одну строку из индексируемого массива, и результирующий массив имеет соответствующую форму (количество элементов индекса, размер строки).
В общем случае форма результирующего массива будет конкатенацией формы массива индексов (или формы, к которой были приведены все массивы индексов) с формой любых неиспользуемых измерений (тех, которые не индексируются) в индексируемом массиве.
Пример
Из каждой строки должен быть выбран конкретный элемент. Индекс строки просто
[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_ вызов, только диагональные элементы будут выбраны:
>>> x[rows, columns]
array([ 0, 11])
Эта разница — самое важное, что нужно помнить при индексировании с несколькими продвинутыми индексами.
Пример
Реальный пример, где расширенная индексация может быть полезна — таблица поиска цветов, где мы хотим сопоставить значения изображения с RGB-тройками для отображения. Таблица поиска может иметь форму (nlookup, 3). Индексация такого массива изображением с формой (ny, nx) и типом данных np.uint8 (или любым целочисленным типом, пока значения находятся в пределах таблицы поиска) приведёт к массиву формы (ny, nx, 3), где тройка значений RGB связана с каждым пикселем.
Булево индексирование массива#
Такое расширенное индексирование происходит, когда obj является объектом массива логического
типа, который может быть возвращён операторами сравнения. Один
логический индексный массив практически идентичен x[obj.nonzero()] где,
как описано выше, obj.nonzero() возвращает кортеж (длиной obj.ndim) целочисленных индексных массивов, показывающих True элементы obj. Однако, он
быстрее, когда obj.shape == x.shape.
Если obj.ndim == x.ndim, x[obj]
возвращает одномерный массив, заполненный элементами x
соответствующий True значения obj. Порядок поиска
будет row-majorC-стиль. Ошибка индекса будет вызвана, если форма obj не соответствует соответствующим измерениям x,
независимо от того, являются ли эти значения True или
False.
Распространенный случай использования — фильтрация по желаемым значениям элементов. Например, может потребоваться выбрать все записи из массива, которые не numpy.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[b, ...], что означает, что x индексируется b,
за которым следует столько : сколько требуется для заполнения ранга x. Таким образом,
форма результата — это одно измерение, содержащее количество истинных элементов
логического массива, за которым следуют оставшиеся измерения индексируемого
массива:
>>> x = np.arange(35).reshape(5, 7)
>>> b = x > 20
>>> b[:, 5]
array([False, False, False, True, True])
>>> x[b[:, 5]]
array([[21, 22, 23, 24, 25, 26, 27],
[28, 29, 30, 31, 32, 33, 34]])
Здесь 4-я и 5-я строки выбираются из индексированного массива и объединяются в 2-D массив.
Пример
Из массива выберите все строки, сумма которых меньше или равна двум:
>>> 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]])
Пример
Использование двумерного булева массива формы (2, 3) с четырьмя элементами True для выбора строк из трёхмерного массива формы (2, 3, 5) даёт двумерный результат формы (4, 5):
>>> x = np.arange(30).reshape(2, 3, 5)
>>> x
array([[[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14]],
[[15, 16, 17, 18, 19],
[20, 21, 22, 23, 24],
[25, 26, 27, 28, 29]]])
>>> b = np.array([[True, True, False], [False, True, True]])
>>> x[b]
array([[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[20, 21, 22, 23, 24],
[25, 26, 27, 28, 29]])
Комбинирование расширенной и базовой индексации#
Когда есть хотя бы один срез (:), многоточие (...) или newaxis
в индексе (или массив имеет больше измерений, чем есть продвинутых индексов),
тогда поведение может быть более сложным. Это похоже на конкатенацию
результатов индексирования для каждого элемента продвинутого индекса.
В простейшем случае есть только одиночный продвинутый индекс в сочетании со срезом. Например:
>>> y = np.arange(35).reshape(5,7)
>>> y[np.array([0, 2, 4]), 1:3]
array([[ 1, 2],
[15, 16],
[29, 30]])
По сути, операции срезов и индексации массива независимы. Операция среза извлекает столбцы с индексами 1 и 2 (т.е. второй и третий столбцы), за которой следует операция индексации массива, которая извлекает строки с индексами 0, 2 и 4 (т.е. первую, третью и пятую строки). Это эквивалентно:
>>> y[:, 1:3][np.array([0, 2, 4]), :]
array([[ 1, 2],
[15, 16],
[29, 30]])
Один расширенный индекс может, например, заменить срез, и результирующий массив будет таким же. Однако это копия и может иметь другую структуру памяти. Срез предпочтительнее, когда это возможно. Например:
>>> x = np.array([[ 0, 1, 2],
... [ 3, 4, 5],
... [ 6, 7, 8],
... [ 9, 10, 11]])
>>> x[1:2, 1:3]
array([[4, 5]])
>>> x[1:2, [1, 2]]
array([[4, 5]])
Самый простой способ понять комбинацию несколько Продвинутые индексы можно рассматривать с точки зрения результирующей формы. Операция индексирования состоит из двух частей: подпространства, определяемого базовым индексированием (исключая целые числа), и подпространства из части продвинутого индексирования. Необходимо различать два случая комбинации индексов:
Умножить полином на x.
Ellipsisилиnewaxis. Напримерx[arr1, :, arr2].Расширенные индексы находятся рядом друг с другом. Например
x[..., arr1, arr2, :]но неx[arr1, :, 1]с1является расширенным индексом в этом отношении.
В первом случае размерности, полученные в результате операции расширенной индексации, идут первыми в результирующем массиве, а подпространственные размерности после них. Во втором случае размерности из операций расширенной индексации вставляются в результирующий массив на том же месте, где они были в исходном массиве (последняя логика заставляет простое расширенное индексирование вести себя так же, как срез).
Пример
Предположим x.shape равно (10, 20, 30) и ind является индексацией формы (2, 5, 2) intp массив, тогда result = x[..., ind, :] имеет форму (10, 2, 5, 2, 30), потому что подпространство формы (20,) было заменено на подпространство индексации с вещанием формы (2, 5, 2). Если мы позволим i, j, k цикл по подпространству формы (2, 5, 2), затем
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.
Пример
Срезы можно комбинировать с транслируемыми булевыми индексами:
>>> x = np.arange(35).reshape(5, 7)
>>> b = x > 20
>>> b
array([[False, False, False, False, False, False, False],
[False, False, False, False, False, False, False],
[False, False, False, False, False, False, False],
[ True, True, True, True, True, True, True],
[ True, True, True, True, True, True, True]])
>>> x[b[:, 5], 1:3]
array([[22, 23],
[29, 30]])
Доступ к полю#
Смотрите также
Если 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-непрерывности с последним индексом, изменяющимся быстрее всего). Этот объект-итератор также может быть проиндексирован с использованием базового среза или расширенной индексации, пока объект выбора не является кортежем. Это должно быть ясно из того факта, что x.flat является одномерным представлением. Его можно использовать для целочисленной
индексации с одномерными индексами в стиле C. Форма любого
возвращаемого массива, следовательно, является формой объекта целочисленной индексации.
Присвоение значений индексированным массивам#
Как упоминалось, можно выбрать подмножество массива для присваивания, используя один индекс, срезы, массивы индексов и масок. Значение, присваиваемое индексированному массиву, должно быть согласовано по форме (той же формы или транслируемо к форме, которую производит индекс). Например, разрешено присваивать константу срезу:
>>> x = np.arange(10)
>>> x[2:7] = 1
или массив правильного размера:
>>> x[2:7] = np.arange(5)
Обратите внимание, что присваивания могут приводить к изменениям, если присваиваются высшие типы к низшим (например, float к int) или даже к исключениям (присваивание complex к float или int):
>>> x[1] = 1.2
>>> x[1]
1
>>> x[1] = 1.2j
Traceback (most recent call last):
...
TypeError: can't convert complex to int
В отличие от некоторых ссылок (таких как индексы массива и маски) присваивания всегда выполняются к исходным данным в массиве (действительно, ничего другого не имело бы смысла!). Однако обратите внимание, что некоторые действия могут работать не так, как можно было бы наивно ожидать. Этот конкретный пример часто удивляет людей:
>>> x = np.arange(0, 50, 10)
>>> x
array([ 0, 10, 20, 30, 40])
>>> x[np.array([1, 1, 3, 1])] += 1
>>> x
array([ 0, 11, 20, 31, 40])
Где люди ожидают, что 1-я позиция будет увеличена на 3. На самом деле, она будет увеличена только на 1. Причина в том, что новый массив извлекается из оригинала (как временный), содержащий значения в 1, 1, 3, 1, затем значение 1 добавляется к временному массиву, а затем временный массив присваивается обратно исходному массиву. Таким образом, значение массива в x[1] + 1 присваивается x[1] три раза, а не увеличиваться 3 раза.
Работа с переменным количеством индексов в программах#
Синтаксис индексирования очень мощный, но ограничен при работе с переменным количеством индексов. Например, если вы хотите написать функцию, которая может обрабатывать аргументы с различным количеством измерений без необходимости писать специальный код для каждого возможного количества измерений, как это можно сделать? Если передать в индекс кортеж, кортеж будет интерпретирован как список индексов. Например:
>>> z = np.arange(81).reshape(3, 3, 3, 3)
>>> indices = (1, 1, 1, 1)
>>> z[indices]
40
Таким образом, можно использовать код для построения кортежей любого количества индексов и затем использовать их в индексе.
Срезы могут быть указаны в программах с помощью функции slice() в Python. Например:
>>> indices = (1, 1, 1, slice(0, 2)) # same as [1, 1, 1, 0:2]
>>> z[indices]
array([39, 40])
Аналогично, многоточие может быть указано в коде с использованием объекта Ellipsis:
>>> indices = (1, Ellipsis, 1) # same as [1, ..., 1]
>>> z[indices]
array([[28, 31, 34],
[37, 40, 43],
[46, 49, 52]])
По этой причине возможно использовать вывод из
np.nonzero() функцию напрямую в качестве индекса, поскольку
она всегда возвращает кортеж индексных массивов.
Из-за особой обработки кортежей они не преобразуются автоматически в массив, как это происходит со списками. Например:
>>> z[[1, 1, 1, 1]] # produces a large array
array([[[[27, 28, 29],
[30, 31, 32], ...
>>> z[(1, 1, 1, 1)] # returns a single value
40
Подробные примечания#
Это некоторые подробные примечания, которые не важны для повседневного индексирования (в произвольном порядке):
Стандартный тип индексации в NumPy –
intpи может отличаться от типа массива целых чисел по умолчанию.intpявляется наименьшим типом данных, достаточным для безопасного индексирования любого массива; для расширенного индексирования он может быть быстрее других типов.Для сложных присваиваний в общем случае нет гарантии порядка итерации. Это означает, что если элемент устанавливается более одного раза, невозможно предсказать конечный результат.
Пустой (кортежный) индекс является полным скалярным индексом в нульмерный массив.
x[()]возвращает скаляр ifxявляется нульмерным и представлением в противном случае. С другой стороны,x[...]всегда возвращает представление.Если в индексе присутствует нульмерный массив и это полный целочисленный индекс, результат будет скаляр а не нуль-мерный массив. (Расширенная индексация не срабатывает.)
Когда многоточие (
...) присутствует, но не имеет размера (т.е. заменяет ноль:) результат все равно всегда будет массивом. Представление, если нет расширенного индекса, иначе копия.The
nonzeroэквивалентность для булевых массивов не выполняется для нульмерных булевых массивов.Когда результат операции расширенного индексирования не имеет элементов, но отдельный индекс выходит за границы, будет ли
IndexErrorвызывается неопределено (например,x[[], [123]]с123выходящие за границы).Когда приведение типов Если ошибка возникает во время присваивания (например, при обновлении числового массива с использованием последовательности строк), массив, которому присваивается значение, может оказаться в непредсказуемом частично обновленном состоянии. Однако, если возникает любая другая ошибка (например, выход за границы индекса), массив останется неизменным.
Расположение памяти результата расширенной индексации оптимизировано для каждой операции индексации, и нельзя предполагать определенный порядок памяти.
При использовании подкласса (особенно того, который манипулирует своей формой), значение по умолчанию
ndarray.__setitem__поведение вызовет__getitem__для базовый индексирования, но не для расширенный индексирование. Для такого подкласса может быть предпочтительнее вызватьndarray.__setitem__с базовый класс ndarray представление данных. Это должен должно быть сделано, если подклассы__getitem__не возвращает представления.