Spec-Zone.ru › NumPy 1.15

Индексирование

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-го индекса n_i, допустимый диапазон — 0 \le n_i < d_i, где d_i — i-й элемент формы массива. Отрицательные индексы интерпретируются как счёт от конца массива (т.е., если n_i < 0, это означает n_i + d_i).

Все массивы, сгенерированные базовой нарезкой, всегда являются видами исходного массива.

Стандартные правила нарезки последовательностей применяются к базовой нарезке в каждом измерении (включая использование индекса шага). Некоторые полезные понятия для запоминания:

  • Синтаксис базовой нарезки — 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]]])
    
  • 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]]
, и итерируются как один:

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), тогда 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 — это массив типа bool, например, такой, который может быть возвращен операторами сравнения. Единственный булев массив-индекс практически идентичен x[obj.nonzero()], где, как описано выше, obj.nonzero() возвращает кортеж (длины obj.ndim) массивов целых индексов, показывающих True элементы obj. Однако это быстрее, когда obj.shape == x.shape.

Если obj.ndim == x.ndim, то x[obj] возвращает одномерный массив, заполненный элементами x, соответствующими True значениям obj. Порядок поиска будет строчно-главный, C-стиль. Если obj имеет True значения в позициях, выходящих за пределы x, то будет поднято исключение индекса. Если obj меньше x, это эквивалентно его заполнению False.

Пример

Распространённый случай использования этого — фильтрация для нужных значений элементов. Например, можно выбрать все элементы массива, которые не являются NaN:

>>> x = np.array([[1., 2.], [np.nan, 3.], [np.nan, np.nan]])
>>> x[~np.isnan(x)]
array([ 1.,  2.,  3.])

Или добавить константу ко всем отрицательным элементам:

>>> x = np.array([1., -1., -2., 3])
>>> x[x < 0] += 20
>>> x
array([  1.,  19.,  18.,   3.])

В общем случае, если индекс содержит булев массив, результат будет идентичен вставке obj.nonzero() в ту же позицию и использованию механизма индексирования целыми числами, описанного выше. x[ind_1, boolean_array, ind_2] эквивалентно x[(ind_1,) + boolean_array.nonzero() + (ind_2,)].

Если присутствует только один булев массив и нет массива целых индексов, это просто. Следует только позаботиться о том, чтобы булев индекс имел ровно столько измерений, сколько необходимо.

Пример

Из массива выберите все строки, сумма элементов которых меньше или равна двум:

>>> x = np.array([[0, 1], [1, 1], [2, 2]])
>>> rowsum = x.sum(-1)
>>> x[rowsum <= 2, :]
array([[0, 1],
       [1, 1]])

Но если 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])
>>> 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__ не возвращают представления.

Доступ к полям массивов

См. также

Объекты типа данных (dtype), Скаляры

Если объект ndarray представляет собой массив со структурой, к полям массива можно получить доступ, индексируя массив строками, как в словаре.

Индексирование x['field-name'] возвращает новое представление массива, которое имеет такую же форму, что и x (за исключением случая, когда поле является подмассивом), но с типом данных x.dtype['field-name'] и содержит только часть данных в указанном поле. Также скаляры массивов со структурой данных могут быть «индексированы» таким образом.

Индексирование в структурированный массив также может выполняться с помощью списка имен полей, напр. x[['field-name1','field-name2']]. В настоящее время это возвращает новый массив, содержащий копию значений в указанных в списке полях. Начиная с NumPy 1.7, возврат копии устаревает в пользу возвращения представления. Копия по-прежнему будет возвращаться, но при записи в копию будет выведено предупреждение FutureWarning. Если вы полагаетесь на текущее поведение, то мы рекомендуем явно скопировать возвращаемый массив, т.е. используйте x[[‘имя_поля1’,’имя_поля2’]].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. Таким образом, форма любого возвращаемого массива — это форма объекта целочисленного индексирования.

© 2005–2019 NumPy Developers
Licensed under the 3-clause BSD License.
https://docs.scipy.org/doc/numpy-1.15.4/reference/arrays.indexing.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API