Spec-Zone.ru › NumPy 1.19

Быстрый старт. Руководство

Предварительные требования

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

Если вы хотите выполнить примеры в этом руководстве, вам также необходимо установить некоторое программное обеспечение на вашем компьютере. Обратитесь к https://scipy.org/install.html за инструкциями.

Профиль обучающегося

Это руководство представляет собой краткий обзор алгебры и массивов в NumPy и призвано помочь понять, как представлены и могут быть преобразованы многомерные (n>=2) массивы. В частности, если вы не знаете, как применять общие функции к многомерным массивам (без использования циклов for), или если хотите понять свойства осей и формы многомерных массивов, это руководство может быть полезным.

Цели обучения

После этого руководства вы сможете:

  • Понять разницу между одномерными, двумерными и многомерными массивами в NumPy;
  • Понять, как применять некоторые операции линейной алгебры к многомерным массивам без использования циклов for;
  • Понять свойства осей и формы многомерных массивов.

Основы

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

Например, координаты точки в 3D-пространстве [1, 2, 1] имеют одну ось. Эта ось содержит 3 элемента, поэтому мы говорим, что ее длина равна 3. На примере ниже массив имеет 2 оси. Первая ось имеет длину 2, вторая ось имеет длину 3.

[[ 1., 0., 0.],
 [ 0., 1., 2.]]

Класс массивов NumPy называется ndarray. Он также известен псевдонимом array. Обратите внимание, что numpy.array не совпадает с классом стандартной библиотеки Python array.array, который обрабатывает только одномерные массивы и предлагает меньше функциональности. Более важные атрибуты объекта ndarray:

ndarray.ndim

количество осей (измерений) массива.

ndarray.shape

размеры массива. Это кортеж целых чисел, указывающих размер массива в каждом измерении. Для матрицы с n строками и m столбцами, shape будет (n,m). Длина кортежа shape поэтому равна количеству осей, ndim.

ndarray.size

общее количество элементов массива. Это равно произведению элементов shape.

ndarray.dtype

объект, описывающий тип элементов в массиве. Можно создавать или указывать dtype, используя стандартные типы Python. Кроме того, NumPy предоставляет собственные типы. numpy.int32, numpy.int16 и numpy.float64 — некоторые примеры.

ndarray.itemsize

размер в байтах каждого элемента массива. Например, массив элементов типа float64 имеет размер itemsize 8 (=64/8), а массив элементов типа complex32 имеет размер itemsize 4 (=32/8). Он эквивалентен ndarray.dtype.itemsize.

ndarray.data

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

Пример

>>> import numpy as np
>>> a = np.arange(15).reshape(3, 5)
>>> a
array([[ 0,  1,  2,  3,  4],
       [ 5,  6,  7,  8,  9],
       [10, 11, 12, 13, 14]])
>>> a.shape
(3, 5)
>>> a.ndim
2
>>> a.dtype.name
'int64'
>>> a.itemsize
8
>>> a.size
15
>>> type(a)
<class 'numpy.ndarray'>
>>> b = np.array([6, 7, 8])
>>> b
array([6, 7, 8])
>>> type(b)
<class 'numpy.ndarray'>

Создание массивов

Существует несколько способов создания массивов.

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

>>> import numpy as np
>>> a = np.array([2,3,4])
>>> a
array([2, 3, 4])
>>> a.dtype
dtype('int64')
>>> b = np.array([1.2, 3.5, 5.1])
>>> b.dtype
dtype('float64')

Частая ошибка заключается в вызове array с несколькими аргументами вместо предоставления одной последовательности в качестве аргумента.

>>> a = np.array(1,2,3,4)    # WRONG
Traceback (most recent call last):
  ...
TypeError: array() takes from 1 to 2 positional arguments but 4 were given
>>> a = np.array([1,2,3,4])  # RIGHT

array преобразует последовательности последовательностей в двумерные массивы, последовательности последовательностей последовательностей в трехмерные массивы и так далее.

>>> b = np.array([(1.5,2,3), (4,5,6)])
>>> b
array([[1.5, 2. , 3. ],
       [4. , 5. , 6. ]])

Тип массива также может быть явно указан при создании:

>>> c = np.array( [ [1,2], [3,4] ], dtype=complex )
>>> c
array([[1.+0.j, 2.+0.j],
       [3.+0.j, 4.+0.j]])

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

Функция zeros создает массив, заполненный нулями, функция ones создает массив, заполненный единицами, а функция empty создает массив, начальное содержимое которого является случайным и зависит от состояния памяти. По умолчанию dtype создаваемого массива — float64.

>>> np.zeros((3, 4))
array([[0., 0., 0., 0.],
       [0., 0., 0., 0.],
       [0., 0., 0., 0.]])
>>> np.ones( (2,3,4), dtype=np.int16 )                # dtype can also be specified
array([[[1, 1, 1, 1],
        [1, 1, 1, 1],
        [1, 1, 1, 1]],

       [[1, 1, 1, 1],
        [1, 1, 1, 1],
        [1, 1, 1, 1]]], dtype=int16)
>>> np.empty( (2,3) )                                 # uninitialized
array([[  3.73603959e-262,   6.02658058e-154,   6.55490914e-260],  # may vary
       [  5.30498948e-313,   3.14673309e-307,   1.00000000e+000]])

Для создания последовательностей чисел NumPy предоставляет функцию arange, которая аналогична встроенной функции Python range, но возвращает массив.

>>> np.arange( 10, 30, 5 )
array([10, 15, 20, 25])
>>> np.arange( 0, 2, 0.3 )                 # it accepts float arguments
array([0. , 0.3, 0.6, 0.9, 1.2, 1.5, 1.8])

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

>>> from numpy import pi
>>> np.linspace( 0, 2, 9 )                 # 9 numbers from 0 to 2
array([0.  , 0.25, 0.5 , 0.75, 1.  , 1.25, 1.5 , 1.75, 2.  ])
>>> x = np.linspace( 0, 2*pi, 100 )        # useful to evaluate function at lots of points
>>> f = np.sin(x)

См. также

array, zeros, zeros_like, ones, ones_like, empty, empty_like, arange, linspace, numpy.random.Generator.rand, numpy.random.Generator.randn, fromfunction, fromfile

Вывод массивов

При выводе массива NumPy отображает его аналогично вложенным спискам, но с следующим расположением:

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

Одномерные массивы выводятся в виде строк, двумерные — в виде матриц, трехмерные — в виде списков матриц.

>>> a = np.arange(6)                         # 1d array
>>> print(a)
[0 1 2 3 4 5]
>>>
>>> b = np.arange(12).reshape(4,3)           # 2d array
>>> print(b)
[[ 0  1  2]
 [ 3  4  5]
 [ 6  7  8]
 [ 9 10 11]]
>>>
>>> c = np.arange(24).reshape(2,3,4)         # 3d array
>>> print(c)
[[[ 0  1  2  3]
  [ 4  5  6  7]
  [ 8  9 10 11]]

 [[12 13 14 15]
  [16 17 18 19]
  [20 21 22 23]]]

См. ниже для получения дополнительных подробностей о reshape.

Если массив слишком большой для вывода, NumPy автоматически пропускает центральную часть массива и выводит только углы:

>>> print(np.arange(10000))
[   0    1    2 ... 9997 9998 9999]
>>>
>>> print(np.arange(10000).reshape(100,100))
[[   0    1    2 ...   97   98   99]
 [ 100  101  102 ...  197  198  199]
 [ 200  201  202 ...  297  298  299]
 ...
 [9700 9701 9702 ... 9797 9798 9799]
 [9800 9801 9802 ... 9897 9898 9899]
 [9900 9901 9902 ... 9997 9998 9999]]

Чтобы отключить это поведение и заставить NumPy выводить весь массив, можно изменить параметры вывода, используя set_printoptions.

>>> np.set_printoptions(threshold=sys.maxsize)       # sys module should be imported

Основные операции

Арифметические операторы с массивами применяются поэлементно. Создается и заполняется новый массив результатом.

>>> a = np.array( [20,30,40,50] )
>>> b = np.arange( 4 )
>>> b
array([0, 1, 2, 3])
>>> c = a-b
>>> c
array([20, 29, 38, 47])
>>> b**2
array([0, 1, 4, 9])
>>> 10*np.sin(a)
array([ 9.12945251, -9.88031624,  7.4511316 , -2.62374854])
>>> a<35
array([ True,  True, False, False])

В отличие от многих языков матриц, оператор умножения * в NumPy работает поэлементно. Матричное произведение можно выполнить, используя оператор @ (в python >=3.5) или функцию или метод dot:

>>> A = np.array( [[1,1],
...                [0,1]] )
>>> B = np.array( [[2,0],
...                [3,4]] )
>>> A * B                       # elementwise product
array([[2, 0],
       [0, 4]])
>>> A @ B                       # matrix product
array([[5, 4],
       [3, 4]])
>>> A.dot(B)                    # another matrix product
array([[5, 4],
       [3, 4]])

Некоторые операции, такие как += и *=, изменяют существующий массив на месте, а не создают новый.

>>> rg = np.random.default_rng(1)     # create instance of default random number generator
>>> a = np.ones((2,3), dtype=int)
>>> b = rg.random((2,3))
>>> a *= 3
>>> a
array([[3, 3, 3],
       [3, 3, 3]])
>>> b += a
>>> b
array([[3.51182162, 3.9504637 , 3.14415961],
       [3.94864945, 3.31183145, 3.42332645]])
>>> a += b                            # b is not automatically converted to integer type
Traceback (most recent call last):
    ...
numpy.core._exceptions.UFuncTypeError: Cannot cast ufunc 'add' output from dtype('float64') to dtype('int64') with casting rule 'same_kind'

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

>>> a = np.ones(3, dtype=np.int32)
>>> b = np.linspace(0,pi,3)
>>> b.dtype.name
'float64'
>>> c = a+b
>>> c
array([1.        , 2.57079633, 4.14159265])
>>> c.dtype.name
'float64'
>>> d = np.exp(c*1j)
>>> d
array([ 0.54030231+0.84147098j, -0.84147098+0.54030231j,
       -0.54030231-0.84147098j])
>>> d.dtype.name
'complex128'

Многие унарные операции, такие как вычисление суммы всех элементов в массиве, реализованы как методы класса ndarray.

>>> a = rg.random((2,3))
>>> a
array([[0.82770259, 0.40919914, 0.54959369],
       [0.02755911, 0.75351311, 0.53814331]])
>>> a.sum()
3.1057109529998157
>>> a.min()
0.027559113243068367
>>> a.max()
0.8277025938204418

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

>>> b = np.arange(12).reshape(3,4)
>>> b
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])
>>>
>>> b.sum(axis=0)                            # sum of each column
array([12, 15, 18, 21])
>>>
>>> b.min(axis=1)                            # min of each row
array([0, 4, 8])
>>>
>>> b.cumsum(axis=1)                         # cumulative sum along each row
array([[ 0,  1,  3,  6],
       [ 4,  9, 15, 22],
       [ 8, 17, 27, 38]])

Универсальные функции

NumPy предоставляет знакомые математические функции, такие как sin, cos и exp. В NumPy они называются «универсальными функциями» (ufunc). В NumPy эти функции действуют поэлементно на массиве, производя массив в качестве выходных данных.

>>> B = np.arange(3)
>>> B
array([0, 1, 2])
>>> np.exp(B)
array([1.        , 2.71828183, 7.3890561 ])
>>> np.sqrt(B)
array([0.        , 1.        , 1.41421356])
>>> C = np.array([2., -1., 4.])
>>> np.add(B, C)
array([2., 0., 6.])

См. также

all, any, apply_along_axis, argmax, argmin, argsort, average, bincount, ceil, clip, conj, corrcoef, cov, cross, cumprod, cumsum, diff, dot, floor, inner, invert, lexsort, max, maximum, mean, median, min, minimum, nonzero, outer, prod, re, round, sort, std, sum, trace, transpose, var, vdot, vectorize, where

Индексирование, срезы и итерация

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

>>> a = np.arange(10)**3
>>> a
array([  0,   1,   8,  27,  64, 125, 216, 343, 512, 729])
>>> a[2]
8
>>> a[2:5]
array([ 8, 27, 64])
# equivalent to a[0:6:2] = 1000;
# from start to position 6, exclusive, set every 2nd element to 1000
>>> a[:6:2] = 1000
>>> a
array([1000,    1, 1000,   27, 1000,  125,  216,  343,  512,  729])
>>> a[ : :-1]                                 # reversed a
array([ 729,  512,  343,  216,  125, 1000,   27, 1000,    1, 1000])
>>> for i in a:
...     print(i**(1/3.))
...
9.999999999999998
1.0
9.999999999999998
3.0
9.999999999999998
4.999999999999999
5.999999999999999
6.999999999999999
7.999999999999999
8.999999999999998

Многомерные массивы могут иметь по одному индексу на ось. Эти индексы задаются в кортеже, разделённом запятыми:

>>> def f(x,y):
...     return 10*x+y
...
>>> b = np.fromfunction(f,(5,4),dtype=int)
>>> b
array([[ 0,  1,  2,  3],
       [10, 11, 12, 13],
       [20, 21, 22, 23],
       [30, 31, 32, 33],
       [40, 41, 42, 43]])
>>> b[2,3]
23
>>> b[0:5, 1]                       # each row in the second column of b
array([ 1, 11, 21, 31, 41])
>>> b[ : ,1]                        # equivalent to the previous example
array([ 1, 11, 21, 31, 41])
>>> b[1:3, : ]                      # each column in the second and third row of b
array([[10, 11, 12, 13],
       [20, 21, 22, 23]])

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

>>> b[-1]                                  # the last row. Equivalent to b[-1,:]
array([40, 41, 42, 43])

Выражение в скобках в b[i] обрабатывается как i , за которым следуют столько экземпляров : , сколько необходимо для представления оставшихся осей. NumPy также позволяет записывать это с помощью точек, как b[i,...].

Точки (...) представляют столько двоеточий, сколько нужно для получения полного кортежа индексирования. Например, если x представляет собой массив с 5 осями, то

  • x[1,2,...] эквивалентно x[1,2,:,:,:],
  • x[...,3] — x[:,:,:,:,3] и
  • x[4,...,5,:] — x[4,:,:,5,:].
>>> c = np.array( [[[  0,  1,  2],               # a 3D array (two stacked 2D arrays)
...                 [ 10, 12, 13]],
...                [[100,101,102],
...                 [110,112,113]]])
>>> c.shape
(2, 2, 3)
>>> c[1,...]                                   # same as c[1,:,:] or c[1]
array([[100, 101, 102],
       [110, 112, 113]])
>>> c[...,2]                                   # same as c[:,:,2]
array([[  2,  13],
       [102, 113]])

Итерация по многомерным массивам выполняется относительно первой оси:

>>> for row in b:
...     print(row)
...
[0 1 2 3]
[10 11 12 13]
[20 21 22 23]
[30 31 32 33]
[40 41 42 43]

Однако, если нужно выполнить операцию над каждым элементом в массиве, можно использовать атрибут flat, который является итератором по всем элементам массива:

>>> for element in b.flat:
...     print(element)
...
0
1
2
3
10
11
12
13
20
21
22
23
30
31
32
33
40
41
42
43

См. также

Индексирование, Индексирование (справочник), newaxis, ndenumerate, indices

Изменение формы массива

Изменение формы массива

У массива есть форма, задаваемая числом элементов вдоль каждой оси:

>>> a = np.floor(10*rg.random((3,4)))
>>> a
array([[3., 7., 3., 4.],
       [1., 4., 2., 2.],
       [7., 2., 4., 9.]])
>>> a.shape
(3, 4)

Форму массива можно изменить с помощью различных команд. Обратите внимание, что следующие три команды все возвращают изменённый массив, но не изменяют исходный массив:

>>> a.ravel()  # returns the array, flattened
array([3., 7., 3., 4., 1., 4., 2., 2., 7., 2., 4., 9.])
>>> a.reshape(6,2)  # returns the array with a modified shape
array([[3., 7.],
       [3., 4.],
       [1., 4.],
       [2., 2.],
       [7., 2.],
       [4., 9.]])
>>> a.T  # returns the array, transposed
array([[3., 1., 7.],
       [7., 4., 2.],
       [3., 2., 4.],
       [4., 2., 9.]])
>>> a.T.shape
(4, 3)
>>> a.shape
(3, 4)

Порядок элементов в массиве, полученном с помощью ravel(), обычно «C-стиль», то есть индекс справа изменяется быстрее всего, поэтому элемент после a[0,0] — это a[0,1]. Если массив переформатирован в другую форму, снова массив рассматривается как «C-стиль». NumPy обычно создаёт массивы, хранящиеся в таком порядке, поэтому ravel() обычно не нуждается в копировании своего аргумента, но если массив был создан путём взятия срезов из другого массива или создан с необычными параметрами, ему может потребоваться копирование. Функции ravel() и reshape() также могут быть указаны, используя необязательный аргумент, для использования массивов в стиле FORTRAN, в котором индекс слева изменяется быстрее всего.

Функция reshape возвращает свой аргумент с изменённой формой, а метод ndarray.resize изменяет сам массив:

>>> a
array([[3., 7., 3., 4.],
       [1., 4., 2., 2.],
       [7., 2., 4., 9.]])
>>> a.resize((2,6))
>>> a
array([[3., 7., 3., 4., 1., 4.],
       [2., 2., 7., 2., 4., 9.]])

Если размерность задана как -1 в операции преобразования формы, другие размерности автоматически вычисляются:

>>> a.reshape(3,-1)
array([[3., 7., 3., 4.],
       [1., 4., 2., 2.],
       [7., 2., 4., 9.]])

См. также

ndarray.shape, reshape, resize, ravel

Объединение различных массивов

Несколько массивов можно объединить вдоль разных осей:

>>> a = np.floor(10*rg.random((2,2)))
>>> a
array([[9., 7.],
       [5., 2.]])
>>> b = np.floor(10*rg.random((2,2)))
>>> b
array([[1., 9.],
       [5., 1.]])
>>> np.vstack((a,b))
array([[9., 7.],
       [5., 2.],
       [1., 9.],
       [5., 1.]])
>>> np.hstack((a,b))
array([[9., 7., 1., 9.],
       [5., 2., 5., 1.]])

Функция column_stack объединяет одномерные массивы в виде столбцов в двумерный массив. Она эквивалентна hstack только для двумерных массивов:

>>> from numpy import newaxis
>>> np.column_stack((a,b))     # with 2D arrays
array([[9., 7., 1., 9.],
       [5., 2., 5., 1.]])
>>> a = np.array([4.,2.])
>>> b = np.array([3.,8.])
>>> np.column_stack((a,b))     # returns a 2D array
array([[4., 3.],
       [2., 8.]])
>>> np.hstack((a,b))           # the result is different
array([4., 2., 3., 8.])
>>> a[:,newaxis]               # view `a` as a 2D column vector
array([[4.],
       [2.]])
>>> np.column_stack((a[:,newaxis],b[:,newaxis]))
array([[4., 3.],
       [2., 8.]])
>>> np.hstack((a[:,newaxis],b[:,newaxis]))   # the result is the same
array([[4., 3.],
       [2., 8.]])

С другой стороны, функция row_stack эквивалентна vstack для любых входных массивов. Фактически, row_stack — это псевдоним для vstack:

>>> np.column_stack is np.hstack
False
>>> np.row_stack is np.vstack
True

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

Примечание

В сложных случаях r_ и c_ полезны для создания массивов путем стека чисел вдоль одной оси. Они позволяют использовать литералы диапазонов (“:”)

>>> np.r_[1:4,0,4]
array([1, 2, 3, 0, 4])

При использовании массивов в качестве аргументов, r_ и c_ аналогичны vstack и hstack в своем поведении по умолчанию, но позволяют использовать необязательный аргумент, определяющий номер оси для конкатенации.

См. также

hstack, vstack, column_stack, concatenate, c_, r_

Разбиение одного массива на несколько меньших

Используя hsplit, вы можете разбить массив вдоль горизонтальной оси, либо указав количество массивов с одинаковой формой, либо указав столбцы, после которых должно произойти деление:

>>> a = np.floor(10*rg.random((2,12)))
>>> a
array([[6., 7., 6., 9., 0., 5., 4., 0., 6., 8., 5., 2.],
       [8., 5., 5., 7., 1., 8., 6., 7., 1., 8., 1., 0.]])
# Split a into 3
>>> np.hsplit(a,3)
[array([[6., 7., 6., 9.],
       [8., 5., 5., 7.]]), array([[0., 5., 4., 0.],
       [1., 8., 6., 7.]]), array([[6., 8., 5., 2.],
       [1., 8., 1., 0.]])]
# Split a after the third and the fourth column
>>> np.hsplit(a,(3,4))
[array([[6., 7., 6.],
       [8., 5., 5.]]), array([[9.],
       [7.]]), array([[0., 5., 4., 0., 6., 8., 5., 2.],
       [1., 8., 6., 7., 1., 8., 1., 0.]])]

vsplit разбивает по вертикальной оси, а array_split позволяет указать по какой оси производить разбиение.

Копии и представления

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

Отсутствует копирование

Простые присваивания не создают копий объектов или их данных.

>>> a = np.array([[ 0,  1,  2,  3],
...               [ 4,  5,  6,  7],
...               [ 8,  9, 10, 11]])
>>> b = a            # no new object is created
>>> b is a           # a and b are two names for the same ndarray object
True

Python передает изменяемые объекты по ссылке, поэтому вызовы функций не создают копий.

>>> def f(x):
...     print(id(x))
...
>>> id(a)                           # id is a unique identifier of an object
148293216  # may vary
>>> f(a)
148293216  # may vary

Представление или поверхностная копия

Различные объекты массивов могут использовать одни и те же данные. Метод view создает новый объект массива, который ссылается на те же данные.

>>> c = a.view()
>>> c is a
False
>>> c.base is a                        # c is a view of the data owned by a
True
>>> c.flags.owndata
False
>>>
>>> c = c.reshape((2, 6))                      # a's shape doesn't change
>>> a.shape
(3, 4)
>>> c[0, 4] = 1234                      # a's data changes
>>> a
array([[   0,    1,    2,    3],
       [1234,    5,    6,    7],
       [   8,    9,   10,   11]])

Вырезка массива возвращает представление:

>>> s = a[ : , 1:3]     # spaces added for clarity; could also be written "s = a[:, 1:3]"
>>> s[:] = 10           # s[:] is a view of s. Note the difference between s = 10 and s[:] = 10
>>> a
array([[   0,   10,   10,    3],
       [1234,   10,   10,    7],
       [   8,   10,   10,   11]])

Глубокая копия

Метод copy создает полную копию массива и его данных.

>>> d = a.copy()                          # a new array object with new data is created
>>> d is a
False
>>> d.base is a                           # d doesn't share anything with a
False
>>> d[0,0] = 9999
>>> a
array([[   0,   10,   10,    3],
       [1234,   10,   10,    7],
       [   8,   10,   10,   11]])

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

>>> a = np.arange(int(1e8))
>>> b = a[:100].copy()
>>> del a  # the memory of ``a`` can be released.

Если b = a[:100] используется вместо этого, a ссылается на b и останется в памяти даже после выполнения del a.

Обзор функций и методов

Вот список некоторых полезных функций и методов NumPy, упорядоченных по категориям. См. Процедуры для получения полного списка.

Создание массивов

arange, array, copy, empty, empty_like, eye, fromfile, fromfunction, identity, linspace, logspace, mgrid, ogrid, ones, ones_like, r_, zeros, zeros_like

Преобразования

ndarray.astype, atleast_1d, atleast_2d, atleast_3d, mat

Манипуляции

array_split, column_stack, concatenate, diagonal, dsplit, dstack, hsplit, hstack, ndarray.item, newaxis, ravel, repeat, reshape, resize, squeeze, swapaxes, take, transpose, vsplit, vstack

Вопросы

all, any, nonzero, where

Порядок

argmax, argmin, argsort, max, min, ptp, searchsorted, sort

Операции

choose, compress, cumprod, cumsum, inner, ndarray.fill, imag, prod, put, putmask, real, sum

Основные статистические характеристики

cov, mean, std, var

Основы линейной алгебры

cross, dot, outer, linalg.svd, vdot

Менее базовые

Правила векторизации

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

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

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

После применения правил векторизации размеры всех массивов должны совпадать. Более подробную информацию можно найти в Векторизация.

Усовершенствованные индексирование и трюки с индексами

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

Индексирование с помощью массивов индексов

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

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

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

Мы также можем указать индексы для нескольких измерений. Массивы индексов для каждого измерения должны иметь одинаковую форму.

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

В Python arr[i, j] точно так же, как arr[(i, j)]—поэтому мы можем поместить i и j в tuple и затем выполнить индексирование с этим.

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

Однако, мы не можем сделать это, поместив i и j в массив, потому что этот массив будет интерпретирован как индексирование первого измерения a.

>>> s = np.array([i, j])

# not what we want
>>> a[s]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: index 3 is out of bounds for axis 0 with size 3

# same as a[i, j]
>>> a[tuple(s)]
array([[ 2,  5],
       [ 7, 11]])

Другое распространённое использование индексирования с массивами — поиск максимального значения в временных рядах:

>>> time = np.linspace(20, 145, 5)                 # time scale
>>> data = np.sin(np.arange(20)).reshape(5,4)      # 4 time-dependent series
>>> time
array([ 20.  ,  51.25,  82.5 , 113.75, 145.  ])
>>> data
array([[ 0.        ,  0.84147098,  0.90929743,  0.14112001],
       [-0.7568025 , -0.95892427, -0.2794155 ,  0.6569866 ],
       [ 0.98935825,  0.41211849, -0.54402111, -0.99999021],
       [-0.53657292,  0.42016704,  0.99060736,  0.65028784],
       [-0.28790332, -0.96139749, -0.75098725,  0.14987721]])

# index of the maxima for each series
>>> ind = data.argmax(axis=0)
>>> ind
array([2, 0, 3, 1])

# times corresponding to the maxima
>>> time_max = time[ind]
>>>
>>> data_max = data[ind, range(data.shape[1])] # => data[ind[0],0], data[ind[1],1]...

>>> time_max
array([ 82.5 ,  20.  , 113.75,  51.25])
>>> data_max
array([0.98935825, 0.84147098, 0.99060736, 0.6569866 ])

>>> np.all(data_max == data.max(axis=0))
True

Вы также можете использовать индексирование с массивами в качестве цели для присваивания:

>>> a = np.arange(5)
>>> a
array([0, 1, 2, 3, 4])
>>> a[[1,3,4]] = 0
>>> a
array([0, 0, 2, 0, 0])

Однако, когда список индексов содержит повторения, присваивание выполняется несколько раз, оставляя последнюю из значений:

>>> a = np.arange(5)
>>> a[[0,0,2]]=[1,2,3]
>>> a
array([2, 1, 3, 3, 4])

Это достаточно разумно, но будьте осторожны, если вы хотите использовать конструкцию Python +=, так как она может не действовать так, как вы ожидаете:

>>> a = np.arange(5)
>>> a[[0,0,2]]+=1
>>> a
array([1, 1, 3, 3, 4])

Несмотря на то, что 0 встречается дважды в списке индексов, элемент с индексом 0 увеличивается только один раз. Это потому, что Python требует, чтобы «a+=1» было эквивалентно «a = a + 1».

Индексирование с помощью булевых массивов

Когда мы индексируем массивы с помощью массивов (целых) индексов, мы предоставляем список индексов для выбора. С булевыми индексами подход другой; мы явно выбираем, какие элементы массива нам нужны, а какие — нет.

Самый естественный способ представления булевого индексирования — использовать булевые массивы, которые имеют одинаковую форму с исходным массивом:

>>> a = np.arange(12).reshape(3,4)
>>> b = a > 4
>>> 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]])

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

>>> import numpy as np
>>> import matplotlib.pyplot as plt
>>> def mandelbrot( h,w, maxit=20 ):
...     """Returns an image of the Mandelbrot fractal of size (h,w)."""
...     y,x = np.ogrid[ -1.4:1.4:h*1j, -2:0.8:w*1j ]
...     c = x+y*1j
...     z = c
...     divtime = maxit + np.zeros(z.shape, dtype=int)
...
...     for i in range(maxit):
...         z = z**2 + c
...         diverge = z*np.conj(z) > 2**2            # who is diverging
...         div_now = diverge & (divtime==maxit)  # who is diverging now
...         divtime[div_now] = i                  # note when
...         z[diverge] = 2                        # avoid diverging too much
...
...     return divtime
>>> plt.imshow(mandelbrot(400,400))
../_images/quickstart-1.png

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

>>> 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 имеет длину 3 (количество строк в a), а b2 (длины 4) подходит для индексирования второй оси (столбцов) a.

Функция ix_()

Функция ix_ может использоваться для объединения различных векторов, чтобы получить результат для каждой n-уплета. Например, если вы хотите вычислить все a+b*c для всех троек, взятых из каждого из векторов a, b и c:

>>> 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)
>>> ax
array([[[2]],

       [[3]],

       [[4]],

       [[5]]])
>>> bx
array([[[8],
        [5],
        [4]]])
>>> cx
array([[[5, 4, 6, 8, 3]]])
>>> ax.shape, bx.shape, cx.shape
((4, 1, 1), (1, 3, 1), (1, 1, 5))
>>> 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]]])
>>> result[3,2,4]
17
>>> a[3]+b[2]*c[4]
17

Вы также могли бы реализовать reduce следующим образом:

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

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

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

См. Структурированные массивы.

Линейная алгебра

Работа в процессе. Основные операции линейной алгебры будут включены сюда.

Простые операции с массивами

См. linalg.py в папке numpy для получения дополнительной информации.

>>> import numpy as np
>>> a = np.array([[1.0, 2.0], [3.0, 4.0]])
>>> print(a)
[[1. 2.]
 [3. 4.]]

>>> a.transpose()
array([[1., 3.],
       [2., 4.]])

>>> np.linalg.inv(a)
array([[-2. ,  1. ],
       [ 1.5, -0.5]])

>>> u = np.eye(2) # unit 2x2 matrix; "eye" represents "I"
>>> u
array([[1., 0.],
       [0., 1.]])
>>> j = np.array([[0.0, -1.0], [1.0, 0.0]])

>>> j @ j        # matrix product
array([[-1.,  0.],
       [ 0., -1.]])

>>> np.trace(u)  # trace
2.0

>>> y = np.array([[5.], [7.]])
>>> np.linalg.solve(a, y)
array([[-3.],
       [ 4.]])

>>> np.linalg.eig(j)
(array([0.+1.j, 0.-1.j]), array([[0.70710678+0.j        , 0.70710678-0.j        ],
       [0.        -0.70710678j, 0.        +0.70710678j]]))
Parameters:
    square matrix
Returns
    The eigenvalues, each repeated according to its multiplicity.
    The normalized (unit "length") eigenvectors, such that the
    column ``v[:,i]`` is the eigenvector corresponding to the
    eigenvalue ``w[i]`` .

Трюки и советы

Здесь мы даём список коротких и полезных советов.

“Автоматическое” изменение формы

Чтобы изменить размер массива, можно опустить один из размеров, который затем будет вычислен автоматически:

>>> a = np.arange(30)
>>> b = a.reshape((2, -1, 3))  # -1 means "whatever is needed"
>>> b.shape
(2, 5, 3)
>>> b
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]]])

Укладка векторов

Как построить двумерный массив из списка одинаково размерных строчных векторов? В MATLAB это довольно просто: если x и y — два вектора одинаковой длины, вам нужно просто сделать m=[x;y]. В NumPy это работает с помощью функций column_stack, dstack, hstack и vstack, в зависимости от измерения, в котором выполняется укладка. Например:

>>> x = np.arange(0,10,2)
>>> y = np.arange(5)
>>> m = np.vstack([x,y])
>>> m
array([[0, 2, 4, 6, 8],
       [0, 1, 2, 3, 4]])
>>> xy = np.hstack([x,y])
>>> xy
array([0, 2, 4, 6, 8, 0, 1, 2, 3, 4])

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

См. также

NumPy для пользователей MATLAB

Гистограммы

Функция NumPy histogram при применении к массиву возвращает пару векторов: гистограмму массива и вектор границ бинов. Осторожно: matplotlib также имеет функцию построения гистограмм (называемую hist, как в Matlab), которая отличается от функции в NumPy. Основное отличие заключается в том, что pylab.hist автоматически отображает гистограмму, в то время как numpy.histogram только генерирует данные.

>>> import numpy as np
>>> rg = np.random.default_rng(1)
>>> import matplotlib.pyplot as plt
>>> # Build a vector of 10000 normal deviates with variance 0.5^2 and mean 2
>>> mu, sigma = 2, 0.5
>>> v = rg.normal(mu,sigma,10000)
>>> # Plot a normalized histogram with 50 bins
>>> plt.hist(v, bins=50, density=1)       # matplotlib version (plot)
>>> # Compute the histogram with numpy and then plot it
>>> (n, bins) = np.histogram(v, bins=50, density=True)  # NumPy version (no plot)
>>> plt.plot(.5*(bins[1:]+bins[:-1]), n)
../_images/quickstart-2.png

Дополнительные материалы

  • Руководство по Python https://docs.python.org/tutorial/
  • Справочник NumPy
  • Руководство SciPy
  • Заметки по лекциям SciPy
  • Словарь MATLAB, R, IDL, NumPy/SciPy http://mathesaurus.sf.net/

© 2005–2020 NumPy Developers
Licensed under the 3-clause BSD License.
https://numpy.org/doc/1.19/user/quickstart.html

Spec-Zone.ru

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