Spec-Zone.ru › NumPy 1.21

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

См. также

Основы индексирования

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

Комбинирование расширенной и базовой индексации

Когда в индексе присутствует хотя бы один срез (:), эллипсис (...) или 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 — массив индексов типа intp формы (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]])

Комбинирование нескольких булевых массивов индексов или булева массива с целочисленным массивом индексов лучше всего понять с помощью аналогии с 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]])

Подробные замечания

Это некоторые подробные замечания, которые не важны для ежедневной индексации (в произвольном порядке):

  • Родной тип индексации 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–2022 NumPy Developers
Licensed under the 3-clause BSD License.
https://numpy.org/doc/1.21/reference/arrays.indexing.html

Spec-Zone.ru

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