Spec-Zone.ru › NumPy 1.16

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

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]]

Обратите внимание, что форма результата идентична формам массивов индексирования (с учётом расширения) 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_, будут выбраны только диагональные элементы, как и в предыдущем примере. Это различие является самым важным моментом, который нужно запомнить при индексировании с помощью нескольких расширенных индексов.

Объединение расширенного и базового индексирования

Когда в индексе есть хотя бы один срез (:), эллипс (...) или newaxis (или массив имеет больше измерений, чем расширенных индексов), поведение может быть более сложным. Это похоже на конкатенацию результатов индексирования для каждого элемента расширенного индекса.

В простейшем случае есть только один расширенный индекс. Один расширенный индекс может, например, заменить срез, и результирующий массив будет таким же, однако он является копией и может иметь другое расположение в памяти. Срез предпочтительнее, когда это возможно.

Пример

>>> x[1:2, 1:3]
array([[4, 5]])
>>> x[1:2, [1, 2]]
array([[4, 5]])

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

  • Расширенные индексы разделены срезом, Ellipsis или newaxis. Например x[arr1, :, arr2].
  • Расширенные индексы расположены друг за другом. Например x[..., arr1, arr2, :] но не x[arr1, :, 1] так как 1 в этом случае является расширенным индексом.

В первом случае измерения, полученные в результате расширенного индексирования, идут первыми в результирующем массиве, а после них — измерения подпространства. Во втором случае измерения от расширенных индексирующих операций вставляются в результирующий массив в той же позиции, что и в исходном массиве (последняя логика делает простое расширенное индексирование похожим на срезы).

Пример

Предположим, что x.shape имеет форму (10,20,30) и ind — это индексирующий массив формы (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 — это массив типа Boolean, например, тот, который может быть возвращён операторами сравнения. Один булев индексный массив практически идентичен 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.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. Таким образом, форма любого возвращенного массива соответствует форме объекта целочисленного индексирования.

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

Spec-Zone.ru

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