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

Введение#

MATLAB® и NumPy имеют много общего, но NumPy был создан для работы с Python, а не как клон MATLAB. Это руководство поможет пользователям MATLAB начать работу с NumPy.

Некоторые ключевые различия#

В MATLAB базовый тип, даже для скаляров, является многомерным массивом. Присваивания массивов в MATLAB хранятся как 2D массивы чисел с плавающей точкой двойной точности, если вы не укажете количество измерений и тип. Операции над 2D экземплярами этих массивов моделируются на основе матричных операций в линейной алгебре.

В NumPy базовым типом является многомерный array. Присваивания массивов в NumPy обычно хранятся как n-мерные массивы с минимальным типом, необходимым для хранения объектов в последовательности, если вы не укажете количество измерений и тип. NumPy выполняет операции поэлементно, поэтому умножение 2D-массивов с * не является умножением матриц – это поэлементное умножение. ( @ оператор, доступный с Python 3.5, может использоваться для обычного матричного умножения.)

MATLAB нумерует индексы с 1; a(1) является первым элементом. См. примечание INDEXING

NumPy, как и Python, нумерует индексы с 0; a[0] является первым элементом.

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

NumPy основан на Python, универсальном языке. Преимущество NumPy заключается в доступе к библиотекам Python, включая: SciPy, Matplotlib, Pandas, OpenCV, и другие. Кроме того, Python часто встроен как язык сценариев в другом программном обеспечении, что позволяет использовать NumPy и там.

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

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

Приблизительные эквиваленты#

В таблице ниже приведены приблизительные эквиваленты для некоторых распространённых выражений MATLAB. Это похожие выражения, а не эквиваленты. Подробности см. в документация.

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

import numpy as np
from scipy import io, integrate, linalg, signal
from scipy.sparse.linalg import cg, eigs

Также предполагайте ниже, что если в примечаниях говорится о «матрице», то аргументы являются двумерными сущностями.

Универсальные эквиваленты#

MATLAB

NumPy

Примечания

help func

info(func) или help(func) или func? (в IPython)

получить справку по функции функция

which func

см. примечание HELP

выяснить, где функция определен

type func

np.source(func) или func?? (в IPython)

вывести исходный код для функция (если не нативная функция)

% comment

# comment

закомментируйте строку кода текстом comment

for i=1:3
    fprintf('%i\n',i)
end
for i in range(1, 4):
   print(i)

используйте цикл for для вывода чисел 1, 2 и 3 с помощью range

a && b

a and b

логический оператор И с коротким замыканием (Нативный оператор Python); только скалярные аргументы

a || b

a or b

логический оператор ИЛИ с коротким замыканием (Нативный оператор Python); только скалярные аргументы

>> 4 == 4
ans = 1
>> 4 == 5
ans = 0
>>> 4 == 4
True
>>> 4 == 5
False

The логические объекты в Python являются True и False, в отличие от логических типов MATLAB 1 и 0.

a=4
if a==4
    fprintf('a = 4\n')
elseif a==5
    fprintf('a = 5\n')
end
a = 4
if a == 4:
    print('a = 4')
elif a == 5:
    print('a = 5')

создать оператор if-else для проверки, если a равно 4 или 5 и вывести результат

1*i, 1*j, 1i, 1j

1j

комплексные числа

eps

np.finfo(float).eps или np.spacing(1)

расстояние от 1 до следующего большего представимого вещественного числа в двойной точности

load data.mat

io.loadmat('data.mat')

Загрузить переменные MATLAB, сохранённые в файле data.mat. (Примечание: При сохранении массивов в data.mat в MATLAB/Octave используйте недавний бинарный формат. scipy.io.loadmat создаст словарь с сохраненными массивами и дополнительной информацией.)

ode45

integrate.solve_ivp(f)

интегрировать ОДУ методом Рунге-Кутта 4,5

ode15s

integrate.solve_ivp(f, method='BDF')

интегрировать ОДУ методом BDF

Эквиваленты линейной алгебры#

MATLAB

NumPy

Примечания

ndims(a)

np.ndim(a) или a.ndim

количество измерений массива a

numel(a)

np.size(a) или a.size

количество элементов массива a

size(a)

np.shape(a) или a.shape

«размер» массива a

size(a,n)

a.shape[n-1]

получить количество элементов n-го измерения массива a. (Обратите внимание, что MATLAB использует индексацию с 1, а Python использует индексацию с 0, См. примечание ИНДЕКСАЦИЯ)

[ 1 2 3; 4 5 6 ]

np.array([[1., 2., 3.], [4., 5., 6.]])

определить 2D массив 2x3

[ a b; c d ]

np.block([[a, b], [c, d]])

построить матрицу из блоков a, b, c, и d

a(end)

a[-1]

доступ к последнему элементу в векторе MATLAB (1xn или nx1) или 1D массиве NumPy a (длина n)

a(2,5)

a[1, 4]

доступ к элементу во второй строке, пятом столбце в 2D массиве a

a(2,:)

a[1] или a[1, :]

вся вторая строка 2D массива a

a(1:5,:)

a[0:5] или a[:5] или a[0:5, :]

первые 5 строк 2D массива a

a(end-4:end,:)

a[-5:]

последние 5 строк двумерного массива a

a(1:3,5:9)

a[0:3, 4:9]

Первая-третья строки и пятый-девятый столбцы двумерного массива, a.

a([2,4,5],[1,3])

a[np.ix_([1, 3, 4], [0, 2])]

строки 2,4 и 5 и столбцы 1 и 3. Это позволяет изменять матрицу и не требует регулярного среза.

a(3:2:21,:)

a[2:21:2,:]

каждая вторая строка a, начиная с третьего и до двадцать первого

a(1:2:end,:)

a[::2, :]

каждая вторая строка a, начиная с первого

a(end:-1:1,:) или flipud(a)

a[::-1,:]

a со строками в обратном порядке

a([1:end 1],:)

a[np.r_[:len(a),0]]

a с копией первой строки, добавленной в конец

a.'

a.transpose() или a.T

транспонирование a

a'

a.conj().transpose() или a.conj().T

сопряжённая транспонированная матрица a

a * b

a @ b

matrix multiply

a .* b

a * b

поэлементное умножение

a./b

a/b

поэлементное деление

a.^3

a**3

поэлементное возведение в степень

(a > 0.5)

(a > 0.5)

матрица, чей i,j-й элемент (a_ij > 0.5). Результат MATLAB — массив логических значений 0 и 1. Результат NumPy — массив булевых значений False и True.

find(a > 0.5)

np.nonzero(a > 0.5)

найти индексы, где (a > 0.5)

a(:,find(v > 0.5))

a[:,np.nonzero(v > 0.5)[0]]

извлечь столбцы a где вектор v > 0.5

a(:,find(v>0.5))

a[:, v.T > 0.5]

извлечь столбцы a где вектор-столбец v > 0.5

a(a<0.5)=0

a[a < 0.5]=0

a с элементами меньше 0.5, обнуленными

a .* (a>0.5)

a * (a > 0.5)

a с элементами меньше 0.5, обнуленными

a(:) = 3

a[:] = 3

установить все значения в одно и то же скалярное значение

y=x

y = x.copy()

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

y=x(2,:)

y = x[1, :].copy()

Срезы NumPy передаются по ссылке

y=x(:)

y = x.flatten()

преобразовать массив в вектор (обратите внимание, что это приводит к копированию). Чтобы получить тот же порядок данных, что и в MATLAB, используйте x.flatten('F').

1:10

np.arange(1., 11.) или np.r_[1.:11.] или np.r_[1:10:10j]

создать возрастающий вектор (см. примечание ДИАПАЗОНЫ)

0:9

np.arange(10.) или np.r_[:10.] или np.r_[:9:10j]

создать возрастающий вектор (см. примечание ДИАПАЗОНЫ)

[1:10]'

np.arange(1.,11.)[:, np.newaxis]

создать вектор-столбец

zeros(3,4)

np.zeros((3, 4))

3x4 двумерный массив, полностью состоящий из 64-битных чисел с плавающей запятой нулей

zeros(3,4,5)

np.zeros((3, 4, 5))

трёхмерный массив 3x4x5, полностью заполненный нулями с плавающей точкой двойной точности

ones(3,4)

np.ones((3, 4))

3x4 двумерный массив, полностью заполненный единицами с плавающей точкой 64-бит

eye(3)

np.eye(3)

единичная матрица 3x3

diag(a)

np.diag(a)

возвращает вектор диагональных элементов 2D массива, a

diag(v,0)

np.diag(v, 0)

возвращает квадратную диагональную матрицу, ненулевые значения которой — элементы вектора, v

rng(42,'twister')
rand(3,4)
from numpy.random import default_rng
rng = default_rng(42)
rng.random((3, 4))

или более старая версия: random.rand((3, 4))

сгенерировать случайный массив 3x4 с генератором случайных чисел по умолчанию и seed = 42

linspace(1,3,4)

np.linspace(1,3,4)

4 равномерно распределенных выборки между 1 и 3 включительно

[x,y]=meshgrid(0:8,0:5)

np.mgrid[0:9.,0:6.] или np.meshgrid(r_[0:9.],r_[0:6.])

два 2D массива: один со значениями x, другой со значениями y

ogrid[0:9.,0:6.] или np.ix_(np.r_[0:9.],np.r_[0:6.]

лучший способ вычисления функций на сетке

[x,y]=meshgrid([1,2,4],[2,4,5])

np.meshgrid([1,2,4],[2,4,5])

np.ix_([1,2,4],[2,4,5])

лучший способ вычисления функций на сетке

repmat(a, m, n)

np.tile(a, (m, n))

создать m на n копий a

[a b]

np.concatenate((a,b),1) или np.hstack((a,b)) или np.column_stack((a,b)) или np.c_[a,b]

объединить столбцы a и b

[a; b]

np.concatenate((a,b)) или np.vstack((a,b)) или np.r_[a,b]

объединить строки a и b

max(max(a))

a.max() или np.nanmax(a)

максимальный элемент a (с ndims(a)<=2 для MATLAB, если присутствуют NaN) nanmax проигнорирует их и вернет наибольшее значение)

max(a)

a.max(0)

максимальный элемент каждого столбца массива a

max(a,[],2)

a.max(1)

максимальный элемент каждой строки массива a

max(a,b)

np.maximum(a, b)

сравнивает a и b поэлементно и возвращает максимальное значение из каждой пары

norm(v)

np.sqrt(v @ v) или np.linalg.norm(v)

Норма L2 вектора v

a & b

logical_and(a,b)

поэлементный оператор AND (NumPy ufunc) См. примечание LOGICOPS

a | b

np.logical_or(a,b)

поэлементный оператор OR (NumPy ufunc) См. примечание LOGICOPS

bitand(a,b)

a & b

побитовый оператор AND (нативный Python и ufunc NumPy)

bitor(a,b)

a | b

побитовый оператор ИЛИ (нативный Python и ufunc NumPy)

inv(a)

linalg.inv(a)

обратная матрица квадратного 2D массива a

pinv(a)

linalg.pinv(a)

псевдообратная матрица 2D массива a

rank(a)

np.linalg.matrix_rank(a)

ранг матрицы 2D массива a

a\b

linalg.solve(a, b) if a является квадратной; linalg.lstsq(a, b) в противном случае

решение a x = b для x

b/a

Решить a.T x.T = b.T вместо

решение x a = b для x

[U,S,V]=svd(a)

U, S, Vh = linalg.svd(a); V = Vh.T

сингулярное разложение a

chol(a)

linalg.cholesky(a)

Разложение Холецкого двумерного массива

[V,D]=eig(a)

D,V = linalg.eig(a)

собственные значения \(\lambda\) и собственные векторы \(v\) of a, где \(\mathbf{a} v = \lambda v\)

[V,D]=eig(a,b)

D,V = linalg.eig(a, b)

собственные значения \(\lambda\) и собственные векторы \(v\) of a, b где \(\mathbf{a} v = \lambda \mathbf{b} v\)

[V,D]=eigs(a,3)

D,V = eigs(a, k=3)

найти k=3 наибольшие собственные значения и собственные векторы 2D массива, a

[Q,R]=qr(a,0)

Q,R = linalg.qr(a)

QR-разложение

[L,U,P]=lu(a) где a==P'*L*U

P,L,U = linalg.lu(a) где a == P@L@U

LU-разложение с частичным выбором ведущего элемента (примечание: P(MATLAB) == transpose(P(NumPy)))

conjgrad

cg

решатель сопряжённых градиентов

fft(a)

np.fft.fft(a)

Преобразование Фурье от a

ifft(a)

np.fft.ifft(a)

обратное преобразование Фурье a

sort(a)

np.sort(a) или a.sort(axis=0)

отсортировать каждый столбец двумерного массива, a

sort(a, 2)

np.sort(a, axis=1) или a.sort(axis=1)

сортировать каждую строку 2D массива, a

[b,I]=sortrows(a,1)

I = np.argsort(a[:, 0]); b = a[I,:]

сохранить массив a как массив b со строками, отсортированными по первому столбцу

x = Z\y

x = linalg.lstsq(Z, y)

выполнить линейную регрессию вида \(\mathbf{Zx}=\mathbf{y}\)

decimate(x, q)

signal.resample(x, np.ceil(len(x)/q))

понижение частоты дискретизации с фильтрацией нижних частот

unique(a)

np.unique(a)

вектор уникальных значений в массиве a

squeeze(a)

a.squeeze()

удалить одноэлементные размерности массива a. Обратите внимание, что MATLAB всегда возвращает массивы размерности 2D или выше, в то время как NumPy возвращает массивы размерности 0D или выше

Примечания#

Подматрица: Присваивание подматрице может быть выполнено с помощью списков индексов, используя ix_ команда. Например, для 2D массива a, можно сделать: ind=[1, 3]; a[np.ix_(ind, ind)] += 100.

ПОМОЩЬ: Нет прямого эквивалента MATLAB which команда, но команды help обычно указывает имя файла, где находится функция. Python также имеет inspect модуль (do import inspect), который предоставляет getfile что часто работает.

ИНДЕКСАЦИЯ: MATLAB использует индексацию с единицы, поэтому начальный элемент последовательности имеет индекс 1. Python использует индексацию с нуля, поэтому начальный элемент последовательности имеет индекс 0. Путаница и споры возникают потому что у каждого подхода есть преимущества и недостатки. Индексация с единицы согласуется с обычным использованием в человеческом языке, где «первый» элемент последовательности имеет индекс 1. Индексация с нуля упрощает индексацию. См. также текст проф. др. Эдсгера В. Дейкстры.

ДИАПАЗОНЫ: В MATLAB, 0:5 может использоваться как как литерал диапазона, так и как индекс 'среза' (внутри скобок); однако в Python конструкции, такие как 0:5 может only может использоваться как индекс среза (внутри квадратных скобок). Отсюда несколько необычный r_ объект был создан, чтобы позволить NumPy иметь аналогичный краткий механизм построения диапазонов. Обратите внимание, что r_ не вызывается как функция или конструктор, а скорее индексированный используя квадратные скобки, что позволяет использовать синтаксис срезов Python в аргументах.

LOGICOPS: & или | в NumPy побитовое И/ИЛИ, тогда как в MATLAB & и | являются логическими И/ИЛИ. Оба могут казаться работающими одинаково, но есть важные различия. Если бы вы использовали MATLAB's & или | операторы, вы должны использовать универсальные функции NumPy logical_and/logical_or. Основные различия между MATLAB и NumPy & и | операторы:

  • Нелогичные входные данные {0,1}: вывод NumPy — это побитовое И входных данных. MATLAB рассматривает любое ненулевое значение как 1 и возвращает логическое И. Например (3 & 4) в NumPy — это 0, тогда как в MATLAB оба 3 и 4 считаются логически истинными и (3 & 4) возвращает 1.

  • Приоритет: оператор & NumPy имеет более высокий приоритет, чем логические операторы, такие как < и >; в MATLAB наоборот.

Если вы знаете, что у вас есть булевы аргументы, вы можете обойтись использованием побитовых операторов NumPy, но будьте осторожны со скобками, например: z = (x > 1) & (x < 2). Отсутствие форм операторов NumPy для logical_and и logical_or является неудачным следствием дизайна Python.

RESHAPE И ЛИНЕЙНАЯ ИНДЕКСАЦИЯ: MATLAB всегда позволяет обращаться к многомерным массивам с использованием скалярных или линейных индексов, NumPy — нет. Линейные индексы распространены в программах MATLAB, например find() на матрице возвращает их, тогда как find в NumPy ведет себя иначе. При преобразовании кода MATLAB может потребоваться сначала преобразовать матрицу в линейную последовательность, выполнить некоторые операции индексирования, а затем преобразовать обратно. Поскольку reshape (обычно) создает представления одного и того же хранилища, это должно быть достаточно эффективно. Обратите внимание, что порядок сканирования, используемый reshape в NumPy, по умолчанию является порядком 'C', тогда как MATLAB использует порядок Fortran. Если вы просто преобразуете в линейную последовательность и обратно, это не имеет значения. Но если вы преобразуете reshape из кода MATLAB, который зависит от порядка сканирования, то этот код MATLAB: z = reshape(x,3,4); должно стать z = x.reshape(3,4,order='F').copy() в NumPy.

‘array’ или ‘matrix’? Что мне использовать?#

Исторически NumPy предоставлял специальный тип матрицы, np.matrix, который является подклассом ndarray, делающим бинарные операции операциями линейной алгебры. Вы можете увидеть его использование в некотором существующем коде вместо np.array. Итак, какой использовать?

Краткий ответ#

Используйте массивы.

  • Они поддерживают многомерную алгебру массивов, которая поддерживается в MATLAB

  • Это стандартный тип вектора/матрицы/тензора в NumPy. Многие функции NumPy возвращают массивы, а не матрицы.

  • Существует четкое различие между поэлементными операциями и операциями линейной алгебры.

  • Вы можете использовать стандартные векторы или векторы-строки/столбцы, если хотите.

До Python 3.5 единственным недостатком использования типа массива было то, что приходилось использовать dot вместо * для умножения (редукции) двух тензоров (скалярное произведение, умножение матрицы на вектор и т.д.). Начиная с Python 3.5 вы можете использовать умножение матриц @ оператор.

Учитывая вышесказанное, мы намерены устаревать matrix в конечном итоге.

Подробный ответ#

NumPy содержит как array класс и matrix класс. The array класс предназначен для использования в качестве универсального n-мерного массива для многих видов численных вычислений, в то время как matrix предназначен для облегчения вычислений линейной алгебры в частности. На практике существует лишь несколько ключевых различий между ними.

  • Операторы * и @, функции dot(), и multiply():

    • Для array, * означает поэлементное умножение, в то время как @ означает умножение матриц; у них есть связанные функции multiply() и dot().

    • Для matrix, * означает умножение матриц, и для поэлементного умножения необходимо использовать multiply() функция.

  • Обработка векторов (одномерных массивов)

    • Для array, векторные формы 1xN, Nx1 и N — это разные вещи. Операции, такие как A[:,1] возвращает одномерный массив формы N, а не двумерный массив формы Nx1. Транспонирование одномерного array ничего не делает.

    • Для matrix, одномерные массивы всегда преобразуются в матрицы 1xN или Nx1 (векторы строк или столбцов). A[:,1] возвращает двумерную матрицу формы Nx1.

  • Обработка многомерных массивов (ndim > 2)

    • array объекты может иметь количество измерений > 2;

    • matrix объекты всегда имеют ровно два измерения.

  • Удобные атрибуты

    • array имеет атрибут .T, который возвращает транспонированные данные.

    • matrix также имеет атрибуты .H, .I и .A, которые возвращают сопряжённую транспонированную матрицу, обратную матрицу и asarray() матрицы, соответственно.

  • Удобный конструктор

    • The array конструктор принимает (вложенные) последовательности Python в качестве инициализаторов. Как в, array([[1,2,3],[4,5,6]]).

    • The matrix конструктор дополнительно принимает удобный строковый инициализатор. Как в matrix("[1 2 3; 4 5 6]").

Есть плюсы и минусы использования обоих:

  • array

    • :) Поэлементное умножение просто: A*B.

    • :( Нужно помнить, что умножение матриц имеет свой собственный оператор, @.

    • :) Вы можете обрабатывать одномерные массивы как либо векторы строк или столбцов. A @ v обрабатывает v как вектор-столбец, в то время как v @ A обрабатывает v как вектор-строка. Это может избавить вас от необходимости вводить много транспонирований.

    • :) array является «стандартным» типом NumPy, поэтому он получает наибольшее тестирование и является типом, который с наибольшей вероятностью будет возвращён сторонним кодом, использующим NumPy.

    • :) Отлично справляется с данными любого количества измерений.

    • :) Ближе по семантике к тензорной алгебре, если вы с ней знакомы.

    • :) Все операции (*, /, +, - и т.д.) выполняются поэлементно.

    • :( Разреженные матрицы из scipy.sparse не так хорошо взаимодействуют с массивами.

  • matrix

    • :\\ Поведение больше похоже на поведение матриц MATLAB.

    • <:( Максимум двумерный. Для хранения трёхмерных данных вам нужно array или, возможно, список Python из matrix.

    • <:( Минимум двумерный. Нельзя использовать векторы. Они должны быть приведены как матрицы с одним столбцом или одной строкой.

    • <:( Поскольку array является значением по умолчанию в NumPy, некоторые функции могут возвращать array даже если вы дадите им matrix в качестве аргумента. Этого не должно происходить с функциями NumPy (если происходит, это ошибка), но сторонний код на основе NumPy может не соблюдать сохранение типов, как это делает NumPy.

    • :) A*B — это умножение матриц, поэтому выглядит так же, как вы записываете его в линейной алгебре (для Python >= 3.5 обычные массивы имеют то же удобство с @ оператор).

    • <:( Поэлементное умножение требует вызова функции, multiply(A,B).

    • <:( Использование перегрузки операторов немного нелогично: * не работает поэлементно, а / делает.

    • Взаимодействие с scipy.sparse немного чище.

The array гораздо более предпочтительно использовать. Действительно, мы намерены устарешить matrix в конечном итоге.

Настройка вашего окружения#

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

NumPy, или скорее Python, имеет аналогичные возможности.

  • Чтобы изменить путь поиска Python для включения местоположений ваших собственных модулей, определите PYTHONPATH переменная окружения.

  • Чтобы определенный файл скрипта выполнялся при запуске интерактивного интерпретатора Python, определите PYTHONSTARTUP переменная окружения для указания имени вашего стартового скрипта.

В отличие от MATLAB, где любой элемент на вашем пути может быть вызван немедленно, в Python вам нужно сначала выполнить оператор 'import', чтобы сделать функции в определенном файле доступными.

Например, вы можете создать стартовый скрипт, который выглядит так (Примечание: это всего лишь пример, а не утверждение «лучших практик»):

# Make all numpy available via shorter 'np' prefix
import numpy as np
#
# Make the SciPy linear algebra functions available as linalg.func()
# e.g. linalg.lu, linalg.eig (for general l*B@u==A@u solution)
from scipy import linalg
#
# Define a Hermitian function
def hermitian(A, **kwargs):
    return np.conj(A,**kwargs).T
# Make a shortcut for hermitian:
#    hermitian(A) --> H(A)
H = hermitian

Чтобы использовать устаревший матрица и другие matlib функции:

# Make all matlib functions accessible at the top level via M.func()
import numpy.matlib as M
# Make some matlib functions accessible directly at the top level via, e.g. rand(3,3)
from numpy.matlib import matrix,rand,zeros,ones,empty,eye