Spec-Zone.ru › NumPy 1.18

Быстрый старт

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

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

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

Основы

Основным объектом 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)
<type 'numpy.ndarray'>
>>> b = np.array([6, 7, 8])
>>> b
array([6, 7, 8])
>>> type(b)
<type '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
>>> 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, output may vary
array([[  3.73603959e-262,   6.02658058e-154,   6.55490914e-260],
       [  5.30498948e-313,   3.14673309e-307,   1.00000000e+000]])

Для создания последовательностей чисел NumPy предоставляет функцию, аналогичную 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.RandomState.rand, numpy.random.RandomState.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]])

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

>>> a = np.ones((2,3), dtype=int)
>>> b = np.random.random((2,3))
>>> a *= 3
>>> a
array([[3, 3, 3],
       [3, 3, 3]])
>>> b += a
>>> b
array([[ 3.417022  ,  3.72032449,  3.00011437],
       [ 3.30233257,  3.14675589,  3.09233859]])
>>> a += b                  # b is not automatically converted to integer type
Traceback (most recent call last):
  ...
TypeError: 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 = np.random.random((2,3))
>>> a
array([[ 0.18626021,  0.34556073,  0.39676747],
       [ 0.53881673,  0.41919451,  0.6852195 ]])
>>> a.sum()
2.5718191614547998
>>> a.min()
0.1862602113776709
>>> a.max()
0.6852195003967595

По умолчанию эти операции применяются к массиву так, как будто он является списком чисел, независимо от его формы. Однако, указав параметр 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, inv, 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])
>>> a[:6:2] = -1000    # equivalent to a[0:6:2] = -1000; from start to position 6, exclusive, set every 2nd element to -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.))
...
nan
1.0
nan
3.0
nan
5.0
6.0
7.0
8.0
9.0

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

>>> 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*np.random.random((3,4)))
>>> a
array([[ 2.,  8.,  0.,  6.],
       [ 4.,  5.,  1.,  1.],
       [ 8.,  9.,  3.,  6.]])
>>> a.shape
(3, 4)

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

>>> a.ravel()  # returns the array, flattened
array([ 2.,  8.,  0.,  6.,  4.,  5.,  1.,  1.,  8.,  9.,  3.,  6.])
>>> a.reshape(6,2)  # returns the array with a modified shape
array([[ 2.,  8.],
       [ 0.,  6.],
       [ 4.,  5.],
       [ 1.,  1.],
       [ 8.,  9.],
       [ 3.,  6.]])
>>> a.T  # returns the array, transposed
array([[ 2.,  4.,  8.],
       [ 8.,  5.,  9.],
       [ 0.,  1.,  3.],
       [ 6.,  1.,  6.]])
>>> 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([[ 2.,  8.,  0.,  6.],
       [ 4.,  5.,  1.,  1.],
       [ 8.,  9.,  3.,  6.]])
>>> a.resize((2,6))
>>> a
array([[ 2.,  8.,  0.,  6.,  4.,  5.],
       [ 1.,  1.,  8.,  9.,  3.,  6.]])

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

>>> a.reshape(3,-1)
array([[ 2.,  8.,  0.,  6.],
       [ 4.,  5.,  1.,  1.],
       [ 8.,  9.,  3.,  6.]])

См. также

ndarray.shape, reshape, resize, ravel

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

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

>>> a = np.floor(10*np.random.random((2,2)))
>>> a
array([[ 8.,  8.],
       [ 0.,  0.]])
>>> b = np.floor(10*np.random.random((2,2)))
>>> b
array([[ 1.,  8.],
       [ 0.,  4.]])
>>> np.vstack((a,b))
array([[ 8.,  8.],
       [ 0.,  0.],
       [ 1.,  8.],
       [ 0.,  4.]])
>>> np.hstack((a,b))
array([[ 8.,  8.,  1.,  8.],
       [ 0.,  0.,  0.,  4.]])

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

>>> from numpy import newaxis
>>> np.column_stack((a,b))     # with 2D arrays
array([[ 8.,  8.,  1.,  8.],
       [ 0.,  0.,  0.,  4.]])
>>> 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]               # this allows to have a 2D columns 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.]])

С другой стороны, функция ma.row_stack эквивалентна vstack для любых входных массивов. В общем случае, для массивов с более чем двумя измерениями, 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*np.random.random((2,12)))
>>> a
array([[ 9.,  5.,  6.,  3.,  6.,  8.,  0.,  7.,  9.,  7.,  2.,  7.],
       [ 1.,  4.,  9.,  2.,  2.,  1.,  0.,  6.,  2.,  2.,  4.,  0.]])
>>> np.hsplit(a,3)   # Split a into 3
[array([[ 9.,  5.,  6.,  3.],
       [ 1.,  4.,  9.,  2.]]), array([[ 6.,  8.,  0.,  7.],
       [ 2.,  1.,  0.,  6.]]), array([[ 9.,  7.,  2.,  7.],
       [ 2.,  2.,  4.,  0.]])]
>>> np.hsplit(a,(3,4))   # Split a after the third and the fourth column
[array([[ 9.,  5.,  6.],
       [ 1.,  4.,  9.]]), array([[ 3.],
       [ 2.]]), array([[ 6.,  8.,  0.,  7.,  9.,  7.,  2.,  7.],
       [ 2.,  1.,  0.,  6.,  2.,  2.,  4.,  0.]])]

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

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

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

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

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

>>> a = np.arange(12)
>>> b = a            # no new object is created
>>> b is a           # a and b are two names for the same ndarray object
True
>>> b.shape = 3,4    # changes the shape of a
>>> a.shape
(3, 4)

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

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

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

Различные объекты массивов могут совместно использовать одни и те же данные. Метод 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.shape = 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» до тех пор, пока все массивы не будут иметь одинаковое количество измерений.

Второе правило расширения гарантирует, что массивы с размером 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]]])

Естественно, мы можем поместить i и j в последовательность (например, список) и затем выполнить индексирование с помощью списка.

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

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

>>> s = np.array( [i,j] )
>>> a[s]                                       # not what we want
Traceback (most recent call last):
  File "<stdin>", 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]])

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

>>> 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]])
>>>
>>> ind = data.argmax(axis=0)                  # index of the maxima for each series
>>> ind
array([2, 0, 3, 1])
>>>
>>> time_max = time[ind]                       # times corresponding to the maxima
>>>
>>> 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))
>>> plt.show()
../_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.00000000-0.70710678j,  0.00000000+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)
>>> a.shape = 2,-1,3  # -1 means "whatever is needed"
>>> a.shape
(2, 5, 3)
>>> a
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)                     # x=([0,2,4,6,8])
y = np.arange(5)                          # y=([0,1,2,3,4])
m = np.vstack([x,y])                      # m=([[0,2,4,6,8],
                                          #     [0,1,2,3,4]])
xy = np.hstack([x,y])                     # xy =([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
>>> 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 = np.random.normal(mu,sigma,10000)
>>> # Plot a normalized histogram with 50 bins
>>> plt.hist(v, bins=50, density=1)       # matplotlib version (plot)
>>> plt.show()
../_images/quickstart-2_00_00.png
>>> # 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)
>>> plt.show()
../_images/quickstart-2_01_00.png

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

  • Документация по Python
  • Справочник NumPy
  • Руководство по SciPy
  • Заметки по лекциям SciPy
  • Словарь MATLAB, R, IDL, NumPy/SciPy

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

Spec-Zone.ru

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