NumPy面向MATLAB用户#

简介#

MATLAB® 和 NumPy 有很多共同点,但创建 NumPy 是为了与 Python 协作,而不是成为 MATLAB 的克隆.本指南将帮助 MATLAB 用户开始使用 NumPy.

一些主要区别#

在MATLAB中,即使对于标量,基本类型也是多维数组.除非您指定维度和类型,否则MATLAB中的数组赋值将存储为双精度浮点数的二维数组.这些数组的二维实例上的运算是根据线性代数中的矩阵运算建模的.

在 NumPy 中,基本类型是多维 array .除非您指定维度和类型,否则 NumPy 中的数组赋值通常存储为 n-dimensional arrays ,其具有保存序列中对象所需的最小类型. NumPy逐个元素地执行操作,因此用 * 乘以二维数组不是矩阵乘法,而是逐个元素的乘法.(自 Python 3.5 起可用的 @ 运算符可用于常规矩阵乘法.)

MATLAB 从 1 开始对索引编号; a(1) 是第一个元素. See note INDEXING

NumPy 和 Python 一样,从 0 开始对索引编号; a[0] 是第一个元素.

MATLAB 的脚本语言是为线性代数创建的,因此某些数组操作的语法比 NumPy 更简洁.另一方面,添加 GUI 和创建功能齐全的应用程序的 API 或多或少是事后才想到的.

NumPy基于通用语言Python. NumPy 的优势在于可以访问 Python 库,包括: SciPy , Matplotlib , Pandas , OpenCV 等.此外,Python 通常 embedded as a scripting language 在其他软件中,允许 NumPy 也被使用.

MATLAB 数组切片使用传值语义,并采用延迟写入方案,以防止在需要之前创建副本.切片操作会复制数组的部分内容.

NumPy 数组切片使用传引用,不会复制参数.切片操作是数组的视图.

大致等价#

下表给出了一些常见 MATLAB 表达式的大致等价形式.这些是类似的表达式,并非完全等价.有关详细信息,请参见 documentation .

在下表中,假设您已在 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 中)

获取有关函数 func 的帮助

which func

see note HELP

找出 func 的定义位置

type func

np.source(func)func?? (在 IPython 中)

打印 func 的源代码(如果不是原生函数)

% comment

# comment

用文本 comment 注释一行代码

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

使用 for 循环,用 range 打印数字 1,2 和 3

a && b

a and b

短路逻辑 AND 运算符 ( Python native operator );仅限标量参数

a || b

a or b

短路逻辑 OR 运算符 ( Python native operator );仅限标量参数

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

Python 中的 boolean objectsTrueFalse ,而不是 MATLAB 的逻辑类型 10 .

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 并打印结果

1i , 1j , 1i , 1j

1j

复数

eps

np.finfo(float).epsnp.spacing(1)

双精度浮点数中,1 到下一个较大的可表示实数之间的距离

load data.mat

io.loadmat('data.mat')

加载保存到文件 data.mat 的 MATLAB 变量.(注意:在 MATLAB/Octave 中将数组保存到 data.mat 时,请使用最新的二进制格式. scipy.io.loadmat 将创建一个包含已保存数组和其他信息的字典.)

ode45

integrate.solve_ivp(f)

使用 Runge-Kutta 4,5 积分 ODE

ode15s

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

使用 BDF 方法积分 ODE

线性代数等价#

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]

获取数组 a 的第n个维度的元素个数.(请注意,MATLAB使用基于1的索引,而Python使用基于0的索引,请参阅 INDEXING )

[ 1 2 3; 4 5 6 ]

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

定义一个2x3的二维数组

[ a b; c d ]

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

从块 a , b , cd 构造一个矩阵

a(end)

a[-1]

访问 MATLAB 向量 (1xn 或 nx1) 或 1D NumPy 数组 a (长度 n) 的最后一个元素

a(2,5)

a[1, 4]

访问二维数组 a 中第二行,第五列的元素

a(2,:)

a[1]a[1, :]

二维数组 a 的整个第二行

a(1:5,:)

a[0:5]a[:5]a[0:5, :]

二维数组 a 的前5行

a(end-4:end,:)

a[-5:]

二维数组 a 的最后5行

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

矩阵乘法

a . b

a * b

元素乘法

a./b

a/b

元素除法

a.^3

a3

元素求幂

(a > 0.5)

(a > 0.5)

矩阵的第i,j个元素是(a_ij > 0.5).MATLAB结果是逻辑值0和1的数组.NumPy结果是布尔值 FalseTrue 的数组.

find(a > 0.5)

np.nonzero(a > 0.5)

查找 ( a > 0.5) 的索引

a(:,find(v > 0.5))

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

提取向量 v > 0.5 的 a 的列

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.) or np.r_[1.:11.] or np.r_[1:10:10j]

创建一个递增的向量 (参见 RANGES 注释)

0:9

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

创建一个递增的向量 (参见 RANGES 注释)

[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 的三维数组,填充 64 位浮点零

ones(3,4)

np.ones((3, 4))

3x4 的二维数组,填充 64 位浮点一

eye(3)

np.eye(3)

3x3 的单位矩阵

diag(a)

np.diag(a)

返回二维数组 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)

1 和 3 之间(包括 1 和 3)的 4 个等间距采样

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

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

两个二维数组:一个包含 x 值,另一个包含 y 值

ogrid[0:9.,0:6.] or 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))

创建 a 的 m x n 份副本

[a b]

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

连接 ab 的列

[a; b]

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

连接 ab 的行

max(max(a))

a.max()np.nanmax(a)

a 的最大元素 (对于 MATLAB,如果 ndims(a)<=2,如果有 NaN, nanmax 会忽略它们并返回最大值)

max(a)

a.max(0)

数组 a 的每一列的最大元素

max(a,[],2)

a.max(1)

数组 a 的每一行的最大元素

max(a,b)

np.maximum(a, b)

逐元素比较 ab ,并返回每对中的最大值

norm(v)

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

向量 v 的L2范数

a & b

logical_and(a,b)

逐元素AND运算符 (NumPy ufunc) See note LOGICOPS

a | b

np.logical_or(a,b)

逐元素OR运算符 (NumPy ufunc) See note LOGICOPS

bitand(a,b)

a & b

按位AND运算符 (Python native 和 NumPy ufunc)

bitor(a,b)

a | b

按位OR运算符 (Python native 和 NumPy ufunc)

inv(a)

linalg.inv(a)

二维方阵 a 的逆矩阵

pinv(a)

linalg.pinv(a)

二维数组 a 的伪逆矩阵

rank(a)

np.linalg.matrix_rank(a)

二维数组 a 的矩阵秩

a\b

如果 a 是方阵,则为 linalg.solve(a, b) ; 否则为 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)

二维数组的 Cholesky 分解

[V,D]=eig(a)

D,V = linalg.eig(a)

a 的特征值 \(\lambda\) 和特征向量 \(v\) ,其中 \(\mathbf{a} v = \lambda v\)

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

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

a , b 的特征值 \(\lambda\) 和特征向量 \(v\) ,其中 \(\mathbf{a} v = \lambda \mathbf{b} v\)

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

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

找到二维数组 ak=3 个最大特征值和特征向量

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

Q,R = linalg.qr(a)

QR分解

[L,U,P]=lu(a) where a==P'LU

P,L,U = linalg.lu(a) where 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)

对二维数组 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 或更高维度的数组

注释#

子矩阵:可以使用索引列表将值赋给子矩阵.例如,对于 2D 数组 a ,可以这样做: ind=[1, 3]; a[np.ix_(ind, ind)] += 100 .

HELP: 没有与 MATLAB 的 which 命令直接对应的命令,但是 help 命令通常会列出函数所在的文件名.Python 还有一个 inspect 模块(执行 import inspect ),它提供了一个通常有效的 getfile .

INDEXING:MATLAB 使用从 1 开始的索引,因此序列的初始元素的索引为 1.Python 使用从 0 开始的索引,因此序列的初始元素的索引为 0.由于每种方式都有优点和缺点,因此会引起混淆和争论.从 1 开始的索引与常用的人类语言用法一致,即序列的“第一个”元素的索引为 1.从 0 开始的索引 simplifies indexing .另请参阅 a text by prof.dr. Edsger W. Dijkstra .

RANGES:在 MATLAB 中, 0:5 既可以用作范围文字,也可以用作“切片”索引(在括号内);但是,在 Python 中,诸如 0:5 之类的构造只能用作切片索引(在方括号内).因此,创建了有点古怪的 r_ 对象,以使 NumPy 具有类似简洁的范围构造机制.请注意, r_ 不像函数或构造函数那样被调用,而是使用方括号进行索引,这允许在参数中使用 Python 的切片语法.

LOGICOPS:NumPy 中的 &| 是按位 AND/OR,而在 MATLAB 中,& 和 | 是逻辑 AND/OR.两者看起来似乎可以正常工作,但是存在重要的差异.如果您曾经使用过 MATLAB 的 &| 运算符,则应使用 NumPy 的 ufuncs logical_and / logical_or .MATLAB 和 NumPy 的 &| 运算符之间的显着区别是:

  • 非逻辑 {0,1} 输入:NumPy 的输出是输入的按位 AND.MATLAB 将任何非零值视为 1,并返回逻辑 AND.例如,NumPy 中的 (3 & 4)0 ,而在 MATLAB 中, 34 都被认为是逻辑真,并且 (3 & 4) 返回 1 .

  • 优先级:NumPy 的 & 运算符优先级高于像 <> 这样的逻辑运算符;MATLAB 则相反.

如果您知道您有布尔参数,您可以使用 NumPy 的按位运算符,但要注意括号,像这样: z = (x > 1) & (x < 2) .缺乏 NumPy 运算符形式的 logical_andlogical_or 是 Python 设计的一个不幸后果.

RESHAPE 和线性索引:MATLAB 始终允许使用标量或线性索引访问多维数组,NumPy 则不然.线性索引在 MATLAB 程序中很常见,例如,矩阵上的 find() 返回它们,而 NumPy 的 find 表现不同.在转换 MATLAB 代码时,可能需要首先将矩阵重塑为线性序列,执行一些索引操作,然后再重塑回来.由于 reshape(通常)产生对同一存储的视图,因此应该可以相对有效地执行此操作.请注意,NumPy 中 reshape 使用的扫描顺序默认为“C”顺序,而 MATLAB 使用 Fortran 顺序.如果您只是转换为线性序列然后再转换回来,这无关紧要.但是,如果您要从 MATLAB 代码转换依赖于扫描顺序的 reshapes,那么此 MATLAB 代码: z = reshape(x,3,4); 在 NumPy 中应变为 z = x.reshape(3,4,order='F').copy() .

‘array’ 还是 ‘matrix’?我应该使用哪个?#

从历史上看,NumPy 提供了一种特殊的矩阵类型 np.matrix ,它是 ndarray 的一个子类,它使二元运算成为线性代数运算.您可能会在一些现有代码中看到它,而不是 np.array .那么,应该使用哪一个呢?

简短回答#

使用数组.

  • 它们支持 MATLAB 中支持的多维数组代数

  • 它们是 NumPy 的标准向量/矩阵/张量类型.许多 NumPy 函数返回数组,而不是矩阵.

  • 逐元素操作和线性代数操作之间有明显的区别.

  • 如果您愿意,您可以拥有标准向量或行/列向量.

在 Python 3.5 之前,使用数组类型的唯一缺点是您必须使用 dot 而不是 * 来乘(约简)两个张量(标量积,矩阵向量乘法等).从 Python 3.5 开始,您可以使用矩阵乘法 @ 运算符.

鉴于以上所述,我们打算最终弃用 matrix .

长篇回答#

NumPy 包含一个 array 类和一个 matrix 类. 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() .

  • 便捷构造函数

    • array 构造函数将(嵌套的)Python 序列作为初始值设定项.例如, array([[1,2,3],[4,5,6]]) .

    • matrix 构造函数此外还接受方便的字符串初始值设定项.例如 matrix("[1 2 3; 4 5 6]") .

使用两者各有利弊:

  • array

    • :) 逐元素乘法很容易: AB .

    • :( 您必须记住,矩阵乘法有自己的运算符 @ .

    • :) 您可以将一维数组视为行向量或列向量. A @ vv 视为列向量,而 v @ Av 视为行向量. 这样可以省去您键入大量转置的时间.

    • :) array 是 NumPy 的“默认”类型,因此它获得了最多的测试,并且是最有可能被使用 NumPy 的第三方代码返回的类型.

    • :) 非常擅长处理任意数量维度的数据.

    • :) 如果您熟悉张量代数,则在语义上更接近张量代数.

    • :) 所有操作( * , / , + , - 等)都是逐元素的.

    • :( 来自 scipy.sparse 的稀疏矩阵与数组的交互效果不佳.

  • matrix

    • :\\ 行为更像 MATLAB 矩阵.

    • <:( 最多二维. 要保存三维数据,您需要 array 或许是 matrix 的 Python 列表.

    • <:( 至少二维. 您不能有向量. 它们必须强制转换为单列或单行矩阵.

    • <:( 由于 array 是 NumPy 中的默认值,因此即使您给它们一个 matrix 作为参数,某些函数也可能会返回一个 array . NumPy 函数不应发生这种情况(如果确实发生了,那就是一个错误),但是基于 NumPy 的第三方代码可能不会像 NumPy 那样遵守类型保留.

    • :) AB 是矩阵乘法,因此它看起来就像您在线性代数中编写的那样(对于 Python >= 3.5,普通数组使用 @ 运算符具有相同的便利性).

    • <:( 逐元素乘法需要调用函数 multiply(A,B) .

    • <:( 运算符重载的使用有点不合逻辑: * 不起逐元素作用,但 / 起.

    • scipy.sparse 的交互更加简洁.

因此,更建议使用 array . 实际上,我们打算最终弃用 matrix .

自定义您的环境#

在 MATLAB 中,用于自定义环境的主要工具是使用您喜欢的函数的位置修改搜索路径. 您可以将此类自定义项放入 MATLAB 启动时将运行的启动脚本中.

NumPy,或者更确切地说是 Python,具有类似的功能.

  • 要修改您的 Python 搜索路径以包含您自己的模块的位置,请定义 PYTHONPATH 环境变量.

  • 要使特定的脚本文件在交互式 Python 解释器启动时执行,请定义 PYTHONSTARTUP 环境变量以包含您的启动脚本的名称.

与 MATLAB 不同,在 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

要使用已弃用的 matrix 和其他 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