Spec-Zone.ru › NumPy 1.17

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

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).

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

Примечание

Выборка срезов 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]]])
    
  • 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 (типа данных целое число или bool) или кортежем, содержащим по крайней мере один объект последовательности или ndarray (типа данных целое число или bool). Существует два типа расширенного индексирования: целочисленное и булево.

Расширенное индексирование всегда возвращает копию данных (в отличие от базовой выборки срезов, которая возвращает вид).

Предупреждение

Определение расширенного индексирования означает, что 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 является массивом объекта булевого типа, например, таким, который может быть возвращен операторами сравнения. Один массив булевых индексов практически идентичен 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, то будет выброшено исключение IndexError. Если 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.17.0/reference/arrays.indexing.html

Spec-Zone.ru

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