Spec-Zone.ru › NumPy 1.21

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

См. также

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

Процедуры индексирования

Индексирование массивов относится к любому использованию квадратных скобок ([ ]) для индексирования значений массива. Существует множество вариантов индексирования, которые придают индексированию NumPy большую мощь, но с мощью приходит и некоторая сложность, а также потенциальная путаница. Этот раздел представляет собой лишь обзор различных вариантов и проблем, связанных с индексированием. Помимо индексирования отдельных элементов, подробности о большинстве этих вариантов можно найти в соответствующих разделах.

Присваивание против ссылок

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

Индексирование одного элемента

Индексирование одного элемента для одномерного массива соответствует ожиданиям. Оно работает точно так же, как и для других стандартных последовательностей Python. Оно основано на нулевом индексе и принимает отрицательные индексы для индексирования с конца массива.

>>> x = np.arange(10)
>>> x[2]
2
>>> x[-2]
8

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

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

Примечание для тех, кто знаком с порядком памяти IDL или Fortran, относящимся к индексированию. NumPy использует индексирование в порядке C. Это означает, что последний индекс обычно представляет наиболее быстро изменяющееся местоположение в памяти, в отличие от Fortran или IDL, где первый индекс представляет наиболее быстро изменяющееся местоположение в памяти. Это различие представляет собой большую потенциальную путаницу.

Другие варианты индексирования

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

>>> x = np.arange(10)
>>> x[2:5]
array([2, 3, 4])
>>> x[:-7]
array([0, 1, 2])
>>> x[1:7:2]
array([1, 3, 5])
>>> y = np.arange(35).reshape(5,7)
>>> y[1:5:2,::3]
array([[ 7, 10, 13],
       [21, 24, 27]])

Обратите внимание, что срезы массивов не копируют внутренние данные массива, а только создают новые представления исходных данных. Это отличается от срезов списков или кортежей, и явное copy() рекомендуется, если исходные данные больше не требуются.

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

Можно использовать специальные функции для эффективного увеличения числа измерений в массиве с помощью индексирования, чтобы полученный массив приобрел форму, необходимую для использования в выражении или со специфической функцией.

Массивы индексов

Массивы NumPy могут индексироваться другими массивами (или любым другим объектом последовательности, который может быть преобразован в массив, таким как списки, за исключением кортежей; см. конец этого документа, чтобы узнать, почему это так). Использование массивов индексов варьируется от простых и понятных случаев до сложных, трудно понимаемых. Во всех случаях с массивами индексов возвращается копия исходных данных, а не представление, как в случае со срезами.

Массивы индексов должны быть целого типа. Каждое значение в массиве указывает, какое значение в массиве использовать вместо индекса. Чтобы проиллюстрировать:

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

Массив индексов, состоящий из значений 3, 3, 1 и 8 соответственно, создает массив длины 4 (такой же, как и массив индексов), где каждый индекс заменяется значением, которое имеет массив индексов в индексируемом массиве.

Допускаются отрицательные значения, и они работают так же, как и с одиночными индексами или срезами:

>>> x[np.array([3,3,-3,8])]
array([7, 7, 4, 2])

Ошибка возникает при использовании значений индексов, выходящих за пределы допустимого диапазона:

>>> x[np.array([3, 3, 20, 8])]
<type 'exceptions.IndexError'>: index 20 out of bounds 0<=index<9

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

>>> x[np.array([[1,1],[2,3]])]
array([[9, 9],
       [8, 7]])

Индексирование многомерных массивов

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

>>> 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])]
<type 'exceptions.ValueError'>: shape mismatch: objects cannot be
broadcast to a single shape

Механизм трансляции позволяет объединять массивы индексов со скалярами для других индексов. Эффект заключается в том, что скалярное значение используется для всех соответствующих значений массивов индексов:

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

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

Пример, где это может быть полезно, - таблица поиска цветов, где мы хотим отобразить значения изображения в тройки RGB для отображения. Таблица поиска может иметь форму (nlookup, 3). Индексирование такого массива изображением с формой (ny, nx) с типом dtype=np.uint8 (или любым целым типом, пока значения находятся в пределах таблицы поиска) приведет к массиву формы (ny, nx, 3), где тройка значений RGB связана с каждым пиксельным местоположением.

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

Булевы или «масочные» массивы индексов

Булевы массивы, используемые в качестве индексов, обрабатываются совершенно по-другому, чем массивы индексов. Булевы массивы должны иметь такую же форму, как и начальные размерности индексируемого массива. В самом простом случае булевой массив имеет такую же форму:

>>> b = y>20
>>> y[b]
array([21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34])

В отличие от случая с массивами индексов, в булевом случае результатом является одномерный массив, содержащий все элементы индексируемого массива, соответствующие всем истинным элементам в булевом массиве. Элементы индексируемого массива всегда итерируются и возвращаются в порядке строчной адресации (порядке C). Результат также идентичен y[np.nonzero(b)]. Как и в случае с массивами индексов, возвращается копия данных, а не представление, как в случае со срезами.

Результат будет многомерным, если y имеет больше измерений, чем b. Например:

>>> b[:,5] # use a 1-D boolean whose first dim agrees with the first dim of y
array([False, False, False,  True,  True])
>>> y[b[:,5]]
array([[21, 22, 23, 24, 25, 26, 27],
       [28, 29, 30, 31, 32, 33, 34]])

Здесь выбираются 4-я и 5-я строки из индексируемого массива и объединяются для создания двумерного массива.

В общем случае, когда булевой массив имеет меньше измерений, чем индексируемый массив, это эквивалентно y[b, …], что означает, что y индексируется b, за которым следуют столько двоеточий, сколько необходимо для заполнения ранга y. Таким образом, форма результата - это одна размерность, содержащая количество истинных элементов булевого массива, за которой следуют оставшиеся размерности индексируемого массива.

Например, использование двумерного булевого массива формы (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]])

Для получения более подробной информации обратитесь к справочной документации NumPy по индексированию массивов.

Объединение массивов индексов со срезами

Массивы индексов можно объединять со срезами. Например:

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

Аналогично, срезы можно объединять с транслируемыми булевыми индексами:

>>> b = y > 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]])
>>> y[b[:,5],1:3]
array([[22, 23],
       [29, 30]])

Инструменты структурного индексирования

Для облегчения сопоставления форм массивов с выражениями и в присваиваниях можно использовать объект np.newaxis внутри индексов массива для добавления новых измерений размером 1. Например:

>>> y.shape
(5, 7)
>>> y[:,np.newaxis,:].shape
(5, 1, 7)

Обратите внимание, что новых элементов в массиве нет, просто увеличивается размерность. Это может быть полезно для объединения двух массивов способом, который в противном случае потребовал бы явных операций изменения формы. Например:

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

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

>>> z = np.arange(81).reshape(3,3,3,3)
>>> z[1,...,2]
array([[29, 32, 35],
       [38, 41, 44],
       [47, 50, 53]])

Это эквивалентно:

>>> z[1,:,:,2]
array([[29, 32, 35],
       [38, 41, 44],
       [47, 50, 53]])

Присваивание значений индексированным массивам

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

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

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

© 2005–2022 NumPy Developers
Licensed under the 3-clause BSD License.
https://numpy.org/doc/1.21/user/basics.indexing.html

Spec-Zone.ru

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