2. 矩阵的创建与基本操作
在矩阵计算中,第一件事不是算,而是“把矩阵正确地造出来,并能准确地取到想要的那一块”。Python(以 NumPy 的 ndarray 和 numpy.matrix 为代表)与 MATLAB 在创建语法、索引基准、切片规则、拼接与变形接口上都有明显差异。把这些差异弄清楚,后续的运算、广播和线性代数调用才不会踩坑。本节围绕“创建—索引—切片—拼接—变形—属性”这条主线,用同一组数据在两种环境中对照操作。
一、创建:字面量语法与函数构造
MATLAB 的矩阵字面量非常直观:行内元素用空格或逗号分隔,行与行之间用分号分隔,例如 A = [1 2 3; 4 5 6] 得到一个 2×3 矩阵。Python 没有内置的矩阵字面量,通常用嵌套列表交给 np.array:A = np.array([[1,2,3],[4,5,6]])。两者语义一致,但 Python 的方括号层级更多,且逗号不可省略。
特殊矩阵的构造函数也值得对照记忆:单位阵 MATLAB 用 eye(n),Python 用 np.eye(n);全零、全一分别用 zeros/ones 与 np.zeros/np.ones;等差序列 MATLAB 用 1:0.5:3(起点:步长:终点),Python 用 np.arange(1, 3.5, 0.5)(起点, 终点(不含), 步长),注意 Python 的终点默认不包含,且参数顺序是“起点、终点、步长”,与 MATLAB 的“起点、步长、终点”正好错位,这是初学者最常写错的地方。若需要按点数等分,MATLAB 用 linspace(a,b,n),Python 用 np.linspace(a,b,n),两者一致。
关于 numpy.matrix:它是历史遗留类型,强制二维、* 表示矩阵乘法。官方已不推荐在新代码中使用,建议统一用 ndarray 配合 @ 做矩阵乘法。本节示例均以 ndarray 为准。
二、索引:0 基与 1 基的根本差异
这是两种环境最本质的区别:Python 索引从 0 开始,MATLAB 从 1 开始。取 A 的第 1 行第 2 列元素,MATLAB 写 A(1,2),Python 写 A[0,1]。Python 用方括号、逗号分隔维度;MATLAB 用圆括号、逗号分隔。Python 还支持负索引,A[-1,-1] 取右下角元素,MATLAB 的 end 关键字承担类似作用,如 A(end,end)。
整行整列的提取:MATLAB 用冒号占位,A(2,:) 取第 2 行,A(:,3) 取第 3 列;Python 用 A[1,:] 与 A[:,2]。需要特别提醒:Python 中 A[1] 取的是第 2 行(一维视图),而 MATLAB 中 A(1) 是按列优先的线性索引,取的是第 1 个元素,二者含义完全不同,切勿混用。
三、切片:区间端点是否包含
MATLAB 切片 A(2:4, :) 取第 2 到第 4 行,两端都包含。Python 切片 A[1:4, :] 取索引 1、2、3 行,即第 2 到第 4 行,左闭右开。因此把 MATLAB 的 2:4 翻译成 Python 要写成 1:4,终点不变、起点减一。步长切片同理:MATLAB A(1:2:end,:) 取奇数行,Python 写作 A[0::2,:]。
另一个高频陷阱是视图与副本。Python 的基本切片返回的是视图,修改切片会影响原数组:
import numpy as np
A = np.arange(12).reshape(3, 4)
B = A[0:2, 1:3] # 视图
B[0, 0] = 99
print(A[0, 1]) # 输出 99,原数组被改动
C = A[0:2, 1:3].copy() # 显式副本,互不影响
MATLAB 的切片赋值语义是“写回原数组”,不存在视图概念上的歧义,但 Python 若不注意这一点,很容易在无意中污染原始数据。需要独立副本时务必调用 .copy()。
四、拼接与变形
水平拼接:MATLAB 用 [A B] 或 horzcat(A,B),Python 用 np.hstack((A,B)) 或 np.concatenate((A,B), axis=1)。垂直拼接:MATLAB 用 [A; B],Python 用 np.vstack((A,B)) 或 np.concatenate((A,B), axis=0)。拼接要求除拼接轴外的维度一致,否则报错,这一点两边相同。
变形方面,MATLAB 用 reshape(A, m, n),Python 用 A.reshape(m, n)。关键差异在填充顺序:MATLAB 默认列优先(先填满一列再填下一列),Python 默认行优先(先填满一行)。对 1:6 变形为 2×3,MATLAB 得到 [1 3 5; 2 4 6],Python 的 np.arange(1,7).reshape(2,3) 得到 [[1,2,3],[4,5,6]]。若要在 Python 中复现 MATLAB 的列优先结果,可加参数 order='F'。转置则两边都简单:MATLAB 用 A',Python 用 A.T。
五、常用属性:shape / size / ndim
MATLAB 用 size(A) 返回各维长度向量,size(A,1) 取行数,ndims(A) 取维数,numel(A) 取元素总数。Python 对应:A.shape 返回元组如 (2,3),A.ndim 返回维数,A.size 返回元素总数。注意命名错位:MATLAB 的 size 对应 Python 的 shape,而 Python 的 size 对应 MATLAB 的 numel。写代码时按“我要的是形状还是元素个数”来选,而不是按名字硬套。
六、一次完整的对照练习
下面用同一目标验证上述差异:从 1 到 12 构造 3×4 矩阵,取第 2 行、第 2 到 3 列的子块,再把它垂直拼接到原矩阵下方。
import numpy as np
A = np.arange(1, 13).reshape(3, 4) # 行优先
sub = A[1:2, 1:3] # 第2行,第2~3列 → [[6,7]]
result = np.vstack((A, sub)) # 4×4
print(A.shape, A.ndim, A.size) # (3, 4) 2 12
print(result)
对应 MATLAB:
A = reshape(1:12, 3, 4); % 列优先,内容与上面不同
sub = A(2, 2:3);
result = [A; sub];
注意这里 reshape 的填充顺序不同,导致两个 A 的内容并不相同——这正是需要显式确认的地方。判断结果是否正确,可以看三点:result 的行数是否等于原行数加子块行数;子块内容是否与 A 对应位置一致;shape/size 是否与预期维度吻合。若 Python 中 sub 被后续修改而 A 也跟着变,说明拿到的是视图,应改用 .copy()。
a:b 翻译成 Python 时,写成 a-1:b 即可。numpy.matrix。它会让 * 变成矩阵乘法、强制二维,并与 ndarray 的广播规则冲突。统一用 ndarray 加 @ 运算符,可避免大量隐式行为带来的排错成本。把创建、索引、切片、拼接、变形和属性这六件事在两种环境中各写一遍并对照输出,是掌握矩阵操作最有效的方式。真正需要养成的习惯是:每次写索引前先确认基准是 0 还是 1,每次切片后确认拿到的是视图还是副本,每次变形后确认填充顺序是否符合预期。