Рубрики
Без рубрики

Numpy массивы в Lightspeed ⚡ Часть 2

Итак, в первой части этой серии я освещал самые основы использования массивов Numpy. Этот пост будет … с меткой Python.

Итак, в первой части этой серии я освещал самые основы использования массивов Numpy. Этот пост будет о более продвинутых и сложных конструкциях, возможных с Ndarrays.

Вещание

Массивы, которые почти имеют одинаковую форму, могут быть сформированы в разные формы для простоты использования UFUNCS, так что все массивы, передаваемые в UFUNC, имеют одинаковый размер. Вот правила, которые Numpy использует, чтобы определить, как транслируется массив:

  1. Если все массивы не имеют одинакового количества размеров, размеры размера 1 добавляются к меньшему измерному массиву.
  2. Любые размеры, имеющие длину 1, остальные измерения распространяются вдоль этого размера 1 длины. Таким образом, измерение 1 длины имеет совершенно идентичные элементы (это операция, которая называется вещанием, и правило 1 на самом деле является особым случаем этого).

После использования этих правил размер обоих массивов должен соответствовать. Другими словами, вещание может помочь вам только расширить размеры 1 длины. Он не может изменить массив 2×6 в какую -то другую форму, например.

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

Это пример операции без вещания:

>>> a = np.array([1.0, 2.0, 3.0])
>>> b = np.array([2.0, 2.0, 2.0])
>>> a * b
array([ 2.,  4.,  6.])

Вот как будет выглядеть простая операция с вещанием:

>>> a = np.array([1.0, 2.0, 3.0])
>>> b = 2.0
>>> a * b
array([ 2.,  4.,  6.])

Так что, как вы можете видеть, не имело значения, b был [2, 2, 2] или просто 2 , потому что последний был расширен (транслируется) до первого.

Неверная попытка вещания бросит ValueError: операнды нельзя транслировать вместе Анкет

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

Image  (3d array): 256 x 256 x 3
Scale  (1d array):             3
Result (3d array): 256 x 256 x 3

В этом примере каждый из цветов изображения (красный, зеленый и синий, размер длины-3) масштабируется соответствующим значением в трехэлементном одномерном массиве, который будет выглядеть как-то вроде [2.0, 1.3, 2.2] Например.

Возможно, этот пример поможет лучше визуализировать операцию вещания:

A      (4d array):  8 x 1 x 6 x 1
B      (3d array):      7 x 1 x 5
Result (4d array):  8 x 7 x 6 x 5

Вы можете видеть, что размеры длины-1 здесь отменены. Дополнительные измерения добавляются в меньший массив, если это необходимо. Это применение обоих правил вещания на практике.

Другим практическим примером вещания является взятие внешнего продукта двух массивов:

>>> a = np.array([0.0, 10.0, 20.0, 30.0])
>>> b = np.array([1.0, 2.0, 3.0])
>>> a[:, np.newaxis] + b
array([[  1.,   2.,   3.],
       [ 11.,  12.,  13.],
       [ 21.,  22.,  23.],
       [ 31.,  32.,  33.]])

np.newaxis добавил еще одно измерение в A , создавая двумерный массив, когда он был одномерным. Потому что мы не назначать это в A , с = В это все еще одномерный.

Больше индексации – целочисленные массивы

Вы можете индексировать массивы Numpy с массивами целых чисел и массивами логических значений. Другие научные языки, такие как Matlab, имеют такую возможность, и причина, по которой кто -то захочет сделать это, заключается в том, что они хотят поставить один и тот же индекс несколько раз в один и тот же срез.

Сначала поговорить об индексации с массивами целых чисел:

>>> a = np.arange(12)**2                       # the first 12 square numbers
>>> i = np.array( [ 1,1,3,8,5 ] )              # an array of indices
>>> a[i]                                       # the elements of a at the positions i
array([ 1,  1,  9, 64, 25])
>>>
>>> j = np.array( [ [ 3, 4], [ 9, 7 ] ] )      # a bidimensional array of indices
>>> a[j]                                       # the same shape as j
array([[ 9, 16],
       [81, 49]])

Итак, вы видите, индексировать [1] Несколько раз несколько раз забирает позицию в этом индексе, вот где первые два 1 В выводе исходят. Оттуда вы можете сформировать массив любым способом.

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

>>> palette = np.array( [ [0,0,0],                # black
...                       [255,0,0],              # red
...                       [0,255,0],              # green
...                       [0,0,255],              # blue
...                       [255,255,255] ] )       # white
>>> image = np.array( [ [ 0, 1, 2, 0 ],           # each value corresponds to a color in the palette
...                     [ 0, 3, 4, 0 ]  ] )
>>> palette[image]                            # the (2,4,3) color image
array([[[  0,   0,   0],
        [255,   0,   0],
        [  0, 255,   0],
        [  0,   0,   0]],
       [[  0,   0,   0],
        [  0,   0, 255],
        [255, 255, 255],
        [  0,   0,   0]]])

*Каждый индекс относится к элементам в первом измерении массива. В приведенном выше примере первым измерением палитры являются ряды, так как вы можете ясно видеть целые массивы, такие как [0, 0, 0] индексируются. Массив Палитра это двумерный массив. Вот пример, который индексирует 3D -массив, используя этот метод:

In [1]: palette = np.array( [ [ [0,0,0],              # black 
   ...:                       [255,0,0],              # red 
   ...:                       [0,255,0],              # green 
   ...:                       [0,0,255],              # blue 
   ...:                       [255,255,255] ], 
   ...:                     [ [1,1,1],                # black 
   ...:                       [255,1,1],              # red  
   ...:                       [1,255,1],              # green  
   ...:                       [1,1,255],              # blue  
   ...:                       [255,255,255] ] ])      # white      

In [2]: image = np.array( [ [ 0, 1 ],           # each value corresponds to a 2D array across depth dimension
   ...:                     [ 1, 1 ]  ] )                                      

In [3]: palette[image]                                                         
Out[3]: 
array([[[[  0,   0,   0],
         [255,   0,   0],
         [  0, 255,   0],
         [  0,   0, 255],
         [255, 255, 255]],

        [[  1,   1,   1],
         [255,   1,   1],
         [  1, 255,   1],
         [  1,   1, 255],
         [255, 255, 255]]],


       [[[  1,   1,   1],
         [255,   1,   1],
         [  1, 255,   1],
         [  1,   1, 255],
         [255, 255, 255]],

        [[  1,   1,   1],
         [255,   1,   1],
         [  1, 255,   1],
         [  1,   1, 255],
         [255, 255, 255]]]])

Как вы можете видеть, первое измерение теперь является измерением глубины, так что именно здесь относятся индексы, а также именно поэтому на этот раз индексируются большие 2D -массивы.

Хорошая индексация матрицы

Вы думали, что я забуду этот? 😉 Очевидно, что массив для численной библиотеки должен иметь матричную поддержку индексации. И есть такая функция, это [i, j] где i и J числа или целочисленные массивы (оба должны иметь одинаковую форму). Ни в коем случае не ограничивается индексацией двухмерных. Напротив, вы используете этот формат индексации с массивами любого измерения, например [i1, i2, i3, i4, ..., in] , с целочисленными массивами поддержки. Опять же, если вы используете целочисленные массивы для их индексации, все они должны быть одинакового размера.

Слис/диапазон нотации ( : ) работает с любым из индексов.

Больше примеров индексации

Надеемся, что, показывая вам больше примеров, которые находятся в документации Numpy, это даст понять, как работает индексация целочисленного массива.

>>> a = np.arange(12).reshape(3,4)
>>> a
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])
>>> i = np.array( [ [0,1],                        # indices for the first dim of a
...                 [1,2] ] )
>>> j = np.array( [ [2,1],                        # indices for the second dim
...                 [3,3] ] )
>>>
>>> a[i,j]                                     # i and j must have equal shape
array([[ 2,  5],
       [ 7, 11]])
>>>
>>> a[i,2]
array([[ 2,  6],
       [ 6, 10]])
>>>
>>> a[:,j]                                     # i.e., a[ : , j]
array([[[ 2,  1],
        [ 3,  3]],
       [[ 6,  5],
        [ 7,  7]],
       [[10,  9],
        [11, 11]]])

Здесь я могу взять массив индексов для первого измерения A ( i ) и массив индексов для его второго измерения ( J ) и получить элементы массива в любой форме, которую я хочу Анкет Это очень гибкий способ индексации.

Мы также можем пройти [i, j] как список (но не ndarray) и получить те же результаты.

>>> l = [i,j]
>>> a[l]                                       # equivalent to a[i,j]
array([[ 2,  5],
       [ 7, 11]])

Несколько разделов выше, мы отметили, что индексация с использованием ndarray будет принимать элементы первый Размер только массива. Поэтому ожидается результат следующей операции индексации.

>>> s = np.array( [i,j] )
>>> a[s]                                       # not what we want
Traceback (most recent call last):
  File "", line 1, in ?
IndexError: index (3) out of range (0<=index<=2) in dimension 0
>>>
>>> a[tuple(s)]                                # same as a[i,j]
array([[ 2,  5],
       [ 7, 11]])

Обратите внимание, как индексация работала должным образом, когда мы создали кортеж из Ndarray. Это потому, что конструктор кортежа преобразовано ndarray в кортеж, который эквивалентно индексирует список.

Вы можете выборочно назначить несколько элементов в массиве с помощью целочисленных массивов.

>>> a = np.arange(5)
>>> a
array([0, 1, 2, 3, 4])
# Don't put the same index multiple times or it will assign to that value
# repeatedly, old values that were assigned will be lost.
>>> a[[1,3,4]] = 0
>>> a
array([0, 0, 2, 0, 0])

Избегайте использования целочисленных присвоений массива с операторами операции в соответствии с такими операторами, как +=,-=, другие, потому что они не будут работать по дублирующим индексам несколько раз. Например:

>>> a = np.arange(5)
>>> a[[0,0,2]]+=1      # Don't do this, += will ignore the second 0
>>> a
array([1, 1, 3, 3, 4])

Больше индексации – логические массивы

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

>>> a = np.arange(12).reshape(3,4)
>>> b = a > 4                           # See what just happened there?
>>> b                                          # b is a boolean with a's shape
array([[False, False, False, False],
       [False,  True,  True,  True],
       [ True,  True,  True,  True]])
>>> a[b]                                       # 1d array with the selected elements
array([ 5,  6,  7,  8,  9, 10, 11])
>>> a[b] = 0                                   # All elements of 'a' higher than 4 become 0
>>> a
array([[0, 1, 2, 3],
       [4, 0, 0, 0],
       [0, 0, 0, 0]])

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

Чтобы указать с логическими массивами, используя матричную индексацию, вы делаете набор логических 1D -массивов, каждый из которых будет индексировать оси, и просто индексируйте массив, используя эти логические массивы вместо чисел:

>>> a = np.arange(12).reshape(3,4)
>>> b1 = np.array([False,True,True])             # first dim selection
>>> b2 = np.array([True,False,True,False])       # second dim selection
>>>
>>> a[b1,:]                                   # selecting rows
array([[ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])
>>>
>>> a[b1]                                     # same thing
array([[ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])
>>>
>>> a[:,b2]                                   # selecting columns
array([[ 0,  2],
       [ 4,  6],
       [ 8, 10]])
>>>
>>> a[b1,b2]                                  # a weird thing to do
array([ 4, 10])

Не обманывайте себя последним заявлением. Интуитивно вы ожидаете B1 и B2 с той формой, которую они имеют, выбрать:

False ~ 0 ~ ~ 1 ~ ~ 2 ~
True 4 ~ 5 ~ 6
True 8 ~ 9 ~ 10

Но на самом деле это не работает. Он займет позиции первых истинных значений во всех массивах и создаст Матричный индекс из этих позиций, чтобы выбрать первый элемент, а затем сделать то же самое для второго, третьего и т. Д. Элементы. Таким образом, все 1D -логические массивы должны иметь одинаковое количество истинных значений. Это то, как выглядит 1D-логический индексирование трехмерного массива:

In [38]: a = np.arange(24).reshape(3,4,2)                                       

In [40]: a                                                                      
Out[40]: 
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]]])


In [42]: a[np.array([True, True, False]), np.array([True, True, False, False]), 
    ...: np.array([True, True])]                                                
Out[42]: array([ 0, 11])

Опять же, это не Выберите конкретные элементы измерения на основе соответствующего 1D массива. Он использует положение NTH True во всех 1D массивах для получения n -го значения полученного массива.

Создание «сетки» из 1D массивов с использованием _ix ()

Если у вас есть набор векторов (в частности, 1D массивы), вам нужно делать алгебру, и вы будете работать столько же Габаритные размеры Как у вас есть векторы, _ix () Функция может помочь вам растянуть форму каждого вектора в n-размерный массив.

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

Я имею в виду, что у вас есть следующие три вектора, которые вы будете вбивать в 3D -массивы:

>>> a = np.array([2,3,4,5])
>>> b = np.array([8,5,4])
>>> c = np.array([5,4,6,8,3])
>>> ax,bx,cx = np.ix_(a,b,c)    # _ix() returns a tuple of n-dimensional arrays

Тогда Ax.Shape будет (4, 1, 1) , bx.shape будет (1, 3, 1) и CX.Shape будет (1, 1, 5) . Затем вы можете объединить массивы в 3D:

>>> result = ax+bx*cx
>>> result
array([[[42, 34, 50, 66, 26],
        [27, 22, 32, 42, 17],
        [22, 18, 26, 34, 14]],
       [[43, 35, 51, 67, 27],
        [28, 23, 33, 43, 18],
        [23, 19, 27, 35, 15]],
       [[44, 36, 52, 68, 28],
        [29, 24, 34, 44, 19],
        [24, 20, 28, 36, 16]],
       [[45, 37, 53, 69, 29],
        [30, 25, 35, 45, 20],
        [25, 21, 29, 37, 17]]])

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

Уменьшить функции

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

(Немного функционального программирования впереди …)

Таким образом, для любой операции вы можете уменьшить ее, начиная с конца массива или с начала массива. Если операция является ассоциативной, то начиная с любого конца даст тот же результат. Существует также начальное значение, которое в сочетании с первым сложенным элементом с использованием этой операции. Этот элемент обычно является идентификационным элементом, для которого применение операции к любому другому элементу возвращает этот другой элемент. Например, 0 – это элемент идентификации добавления (потому что все плюс 0 сама 0), 1 – элемент идентификации умножения и экспоненты, а также есть элемент идентификации для умножения матрицы. На этом этапе я должен прояснить, что начиная с начала массива известно как Складывание влево и начиная с конца называется Склад справа Анкет

В Python (3) функции могут быть уменьшены с помощью functools.reduce (func, list, initval) Складывание влево, или functools.reduce (rambda x, y: func (y, x), reversed (list), initval) складываться вправо.

Вернуться к _ix ()

У Ufuncs есть свой способ определения процесса уменьшения:

# No functools here
# Fold left
>>> def ufunc_reduce(ufct, *vectors):
...    vs = np.ix_(*vectors)
...    r = ufct.identity
...    for v in vs:
...        r = ufct(r,v)
...    return r
>>> ufunc_reduce(np.add,a,b,c)
array([[[15, 14, 16, 18, 13],
        [12, 11, 13, 15, 10],
        [11, 10, 12, 14,  9]],
       [[16, 15, 17, 19, 14],
        [13, 12, 14, 16, 11],
        [12, 11, 13, 15, 10]],
       [[17, 16, 18, 20, 15],
        [14, 13, 15, 17, 12],
        [13, 12, 14, 16, 11]],
       [[18, 17, 19, 21, 16],
        [15, 14, 16, 18, 13],
        [14, 13, 15, 17, 12]]])

Только операции Numpy имеют идентичность Участник, обычные операции на питоне, нет, или другие специальные свойства Numpy.

Некоторые линейные свойства алгебры и методы ndarray

Этот раздел не является исчерпывающим охватом всех линейных членов алгебры в Ndarray, просто потому, что линейная алгебра является большим предметом. Я надеюсь, что смогу уточнить их больше в будущих постах. Предполагая A это ndarray:

  • a.t и a.transpose () это транспонирование массива
  • np.linalg.inv (a) это обратное а
  • np.eye (n) это матрица идентификации формы (n, n)
  • @ Оператор – умножение матрицы (Python 3.5+)
  • np.trace (a) это след а
  • np.linalg.solve (a, y) Решает систему уравнений, то есть формирует уравнение @ и попытки найти вектор решения x Анкет
  • np.linalg.eig (a) Находит собственные значения A Анкет

Я не проверял, работают ли эти функции только на 2D массивах. Если это правда, это не удивительно, так как многие линейные операции алгебры не определены для массивов N-D.

Джим Хеффрон, линейная алгебра это хороший свободный урок по линейной алгебре, если вы еще не знаете об этом.

И были сделаны

Вступает больше постов Numpy, так что следите за обновлениями в ближайшие недели.

Много материала было получено из Numpy Manual Анкет

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

Изображение по Arek socha от Pixabay

Оригинал: “https://dev.to/zenulabidin/numpy-arrays-at-lightspeed-part-2-7ci”