Быстрый старт. Руководство
Предварительные требования
Перед чтением этого руководства вы должны немного знать Python. Если вы хотите освежить свои знания, ознакомьтесь с руководством по Python.
Если вы хотите выполнить примеры в этом руководстве, вам также необходимо установить некоторое программное обеспечение на вашем компьютере. Обратитесь к https://scipy.org/install.html за инструкциями.
Профиль обучающегося
Это руководство представляет собой краткий обзор алгебры и массивов в NumPy и призвано помочь понять, как представлены и могут быть преобразованы многомерные () массивы. В частности, если вы не знаете, как применять общие функции к многомерным массивам (без использования циклов 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имеет размерitemsize8 (=64/8), а массив элементов типаcomplex32имеет размерitemsize4 (=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.]])
См. также
Объединение различных массивов
Несколько массивов можно объединить вдоль разных осей:
>>> 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 - Преобразования
- Манипуляции
-
array_split,column_stack,concatenate,diagonal,dsplit,dstack,hsplit,hstack,ndarray.item,newaxis,ravel,repeat,reshape,resize,squeeze,swapaxes,take,transpose,vsplit,vstack - Вопросы
- Порядок
- Операции
-
choose,compress,cumprod,cumsum,inner,ndarray.fill,imag,prod,put,putmask,real,sum - Основные статистические характеристики
- Основы линейной алгебры
-
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))
Второй способ индексирования с булевыми значениями более похож на индексирование с целыми числами; для каждого измерения массива мы задаем одномерный булевый массив, выбирающий необходимые слайсы:
>>> 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 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)
Дополнительные материалы
- Руководство по 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