Индексирование ndarrays
См. также
ndarrays можно индексировать с использованием стандартного синтаксиса Python x[obj] , где x — массив, а obj — выборка. Доступны разные типы индексирования в зависимости от obj: базовая индексация, расширенная индексация и доступ к полям.
Большинство следующих примеров демонстрируют использование индексирования при обращении к данным в массиве. Примеры работают так же хорошо, когда присваиваются значения массиву. См. Присваивание значений индексированным массивам для конкретных примеров и объяснений работы присваиваний.
Обратите внимание, что в Python x[(exp1, exp2, ..., expN)] эквивалентно x[exp1, exp2, ..., expN]; последнее — просто синтаксический сахар для первого.
Базовая индексация
Индексирование одного элемента
Индексирование одного элемента работает точно так же, как и для других стандартных последовательностей Python. Оно нулевого базиса и допускает отрицательные индексы для индексирования с конца массива.
>>> 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 на 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 (целого или булевого типа данных), или кортежем, содержащим по меньшей мере один последовательный объект или массив ndarray (целого или булевого типа данных). Существуют два типа расширенной индексации: целочисленная и булева.
Расширенная индексация всегда возвращает копию данных (в отличие от базового среза, который возвращает вид).
Предупреждение
Определение расширенной индексации означает, что 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
Когда индекс состоит из такого же количества целочисленных массивов, что и измерений индексируемого массива, индексация является простой, но отличается от срезов.
Расширенные индексы всегда вещаются и обрабатываются как один:
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. Если индексы не могут быть распространены на одну и ту же форму, генерируется исключение 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,)
Механизм вещания позволяет комбинировать массивы индексов со скалярами для других индексов. Эффект состоит в том, что скалярное значение используется для всех соответствующих значений массивов индексов:
>>> 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. Порядок поиска будет строчный, C-стиль. Ошибка индекса будет поднята, если форма 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.])
В общем случае, если индекс включает массив Boolean, результат будет идентичен вставке 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-я строки выбираются из индексируемого массива и объединяются, чтобы сформировать двумерный массив.
Пример
Из массива выберите все строки, сумма элементов которых меньше или равна двум:
>>> 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) с четырьмя истинными элементами для выбора строк из трёхмерного массива формы (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]])
Самый простой способ понять комбинацию нескольких расширенных индексов – это подумать о форме результата. Есть две части операции индексирования: подпространство, определённое базовым индексированием (исключая целые числа), и подпространство от расширенной части индексирования. Нужно различать два случая комбинации индексов:
- Расширенные индексы разделены срезом,
Ellipsisили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.
Пример
Срез можно комбинировать с векторизованными булевыми индексами:
>>> 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-contiguous, где последний индекс изменяется быстрее). Этот объект итератора также может быть индексирован с помощью базового или расширенного индексирования, если объект выбора не является кортежем. Это должно быть ясно из того, что 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])
Где люди ожидают, что первая позиция увеличится на 3. На самом деле, она увеличится только на 1. Причина в том, что из оригинального массива извлекается новый массив (как временный), содержащий значения в позициях 1, 1, 3, 1, затем к временному значению добавляется 1, а затем временный массив присваивается обратно исходному массиву. Таким образом, значение массива в x[1] + 1 присваивается x[1] три раза, а не увеличивается три раза.
Работа с переменным числом индексов в программах
Синтаксис индексирования очень мощный, но ограничен, когда речь идёт о переменном числе индексов. Например, если вы хотите написать функцию, которая может обрабатывать аргументы с различным числом измерений, не написав специальный код для каждого из возможных измерений, как это можно сделать? Если вы передаёте в индекс кортеж, кортеж будет интерпретирован как список индексов. Например:
>>> 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[()]возвращает скаляр, еслиxнульмерный, и представление в противном случае. С другой стороны,x[...]всегда возвращает представление. - Если в индексе присутствует нульмерный массив и это полный целочисленный индекс, результат будет скалярным, а не нульмерным массивом. (Расширенный индексирование не срабатывает.)
- Когда присутствует многоточие (
...), но не имеет размера (т.е. заменяет ноль:), результат всё равно всегда будет массивом. Представление, если расширенный индекс отсутствует, в противном случае копия. - Эквивалент
nonzeroдля булевых массивов не сохраняется для нульмерных булевых массивов. - Когда результат операции расширенного индексирования не имеет элементов, но отдельный индекс выходит за пределы, подразумевается, что исключение
IndexErrorбудет или нет (например,x[[], [123]]с123выходящим за пределы). - Когда во время присваивания возникает ошибка преобразования (например, при обновлении числового массива с помощью последовательности строк), массив, которому присваивается значение, может оказаться в непредсказуемом частично обновлённом состоянии. Однако если возникает любая другая ошибка (например, индекс выходит за пределы), массив останется неизменным.
- Макет памяти результата расширенного индексирования оптимизирован для каждой операции индексирования, и нельзя предполагать конкретный порядок памяти.
- При использовании подкласса (особенно того, который манипулирует своей формой), поведение
ndarray.__setitem__по умолчанию вызовет__getitem__для базового индексирования, но не для расширенного индексирования. Для такого подкласса может быть предпочтительнее вызватьndarray.__setitem__с представлением базового класса ndarray на данных. Это обязательно нужно сделать, если подклассы__getitem__не возвращают представления.
© 2005–2024 NumPy Developers
Licensed under the 3-clause BSD License.
https://numpy.org/doc/2.0/user/basics.indexing.html