日拱一卒之Python与matlab的内存读取区别
Python与matlab的内存读取区别
简单来说,Python (NumPy) 和 Matlab 在 reshape 时的核心区别在于"内存读取顺序"不同。
- Python (NumPy): 默认使用 行优先 (Row-Major Order) ,即"先填满第一行,再填第二行..."。
- Matlab: 默认使用 列优先 (Column-Major Order) ,即"先填满第一列,再填第二列..."。
1. 直观的区别:举个例子
假设我们有一个包含 1 到 6 的一维数组,想要把它转换成一个 2行 3列 的矩阵。
输入数据
1,2,3,4,5,6\] \[1, 2, 3, 4, 5, 6\] \[1,2,3,4,5,6
Python (NumPy) 的做法
Python 会横向填充数据。
Python
import numpy as np
a = np.array([1, 2, 3, 4, 5, 6])
b = a.reshape(2, 3)
print(b)
结果:
123456\] \\begin{bmatrix} 1 \& 2 \& 3 \\\\ 4 \& 5 \& 6 \\end{bmatrix} \[142536
特点: 数字 1,2,31, 2, 31,2,3 都在第一行。
Matlab 的做法
Matlab 会纵向填充数据。
Matlab
a = 1:6;
b = reshape(a, 2, 3);
disp(b)
结果:
135246\] \\begin{bmatrix} 1 \& 3 \& 5 \\\\ 2 \& 4 \& 6 \\end{bmatrix} \[123456
特点: 数字 1,21, 21,2 都在第一列。
2. 为什么会有这种区别?
这个区别源于两种编程语言的历史背景和底层实现标准。计算机内存是线性的(像一条长纸带),多维数组在内存中必须"拉直"存放。
Matlab (Fortran 流派)
- 来源: Matlab 最早是作为 LINPACK 和 EISPACK (线性代数库) 的接口编写的,这些库是用 Fortran 语言写的。
- 标准: Fortran 语言标准规定数组在内存中是 列优先 (Column-Major) 存储的。这意味着内存中相邻的两个地址,对应的是矩阵中同一列上下相邻的两个元素。
- 逻辑: 在科学计算中,很多线性代数操作是对列向量进行的,列优先存储在某些数学运算上(如访问列向量)效率更高。
Python/NumPy (C 流派)
- 来源: Python 的解释器以及 NumPy 的底层主要是用 C 语言 编写的。
- 标准: C 语言标准规定数组是 行优先 (Row-Major) 存储的。这意味着内存中相邻的两个地址,对应的是矩阵中同一行左右相邻的两个元素。
- 逻辑: 这更符合人类阅读文字的习惯(从左到右,换行)。
3. 如何在 Python 中模仿 Matlab 的行为?
如果你正在将 Matlab 代码移植到 Python,并且希望 reshape 的结果与 Matlab 完全一致,你不需要手动转置,只需要修改 NumPy 的 order 参数。
NumPy 的 reshape 接受一个 order 参数:
'C'(默认): C-style,即行优先。'F': Fortran-style,即列优先(模仿 Matlab)。
代码示例:
Python
import numpy as np
a = np.array([1, 2, 3, 4, 5, 6])
# 使用 order='F' 来模拟 Matlab 的行为
b_matlab_style = a.reshape((2, 3), order='F')
print(b_matlab_style)
输出:
Plaintext
[[1 3 5]
[2 4 6]]
在实际的使用中要多注意,这个是非常容易出错的地方。
numpy.fromfile函数的用法
numpy.fromfile 是 NumPy 库中一个非常高效的函数,专门用于从文件中读取数据并将其转换为 NumPy 数组。它最常用于读取 纯二进制文件 (Raw Binary Files)
1. 函数签名
python
numpy.fromfile(file, dtype=float, count=-1, sep='', offset=0, like=None)
2. 核心参数详解
file (必选):文件路径(字符串)或已打开的文件对象。
dtype (关键参数):数据类型 。指定文件中每个数据点占据多少字节以及如何解释这些字节。二进制文件没有"头文件 "来告诉程序数据是什么格式。如果你的文件存的是 16位整数 (int16),但你默认用 float 读取,读出来的数据全是乱码。
常用值:
np.int16: 2字节整数(雷达/音频常见)。np.float32: 4字节浮点数。np.complex64: 8字节复数。>f4/<i2: 指定大小端(Big-endian / Little-endian),如果数据是在不同架构的机器上生成的,这很重要。
count::要读取的数据项数量(不是字节数,是 dtype 指定的元素个数)。默认值 :-1,表示读取整个文件直到结束。如果只想读文件的前 1000 个点,设 count=1000。
sep:分隔符。 '' (空字符串,默认) :读取二进制文件 。这是该函数最主要的用途。' ' 或 ',':如果指定了分隔符,函数会把它当作文本文件读取(类似于 CSV)。但读文本通常推荐用 np.loadtxt 或 pandas,因为 fromfile 读文本不够灵活且速度优势不明显。
offset:从文件开头跳过的字节数 。如果文件有一个文件头(Header),比如前 1024 字节是描述信息,真正的数据在后面,你可以设置 offset=1024 来跳过头部直接读数据。
3. 关键注意事项(坑点)
- 返回永远是一维数组 :
np.fromfile读取的是字节流,它不知道数据的行和列。它只是把字节挨个读进来排成一排。因此,读取后几乎总是 需要接一个.reshape()函数来还原数据的真实维度。 - 不包含元数据 :
与np.load(读取.npy文件) 不同,fromfile读取的二进制文件不包含形状(shape)或数据类型(dtype)信息。必须预先知道 这个文件的格式(比如是int16还是float32,长宽是多少),否则无法正确还原。
整数索引(与切片的不同)
整数索引 (Integer Indexing) 是 NumPy(以及 Python 列表)中最基础但也最重要的概念之一。
简单来说,就是使用一个具体的整数(如 0 , 1 , -1 )来访问数组中的特定位置。
什么是整数索引?
它的核心特征是: "选中并提取" 。当对某个维度使用整数索引时,NumPy 会认为:"你想要这个确切位置的数据,这个维度本身对你来说已经没用了。"
后果: 该维度会从结果的形状(shape)中消失(降维)。
直观对比:整数索引 vs 切片 (Slicing)
假设有一个 2D 数组(3行3列):
python
import numpy as np
arr = np.array([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
])
# 形状是 (3, 3)
| 操作方式 | 代码 | 结果 | 形状 (Shape) | 含义 |
|---|---|---|---|---|
| 整数索引 | arr[0] |
[1, 2, 3] |
(3,) | 我要第0行的数据。(维度从2D降为1D,行维度消失了) |
| 切片 | arr[0:1] |
[[1, 2, 3]] |
(1, 3) | 我要包含第0行的一个子数组 。(维度保持2D,行维度还在,只是长度为1) |
通俗比喻:
- 切片 (
0:1 ) :像是在切面包。即使你切了非常薄的一片,它依然是一片面包(它还是三维物体,只是厚度很小)。 - 整数索引 (
0 ) :像是把那片面包上的图案印在一张纸上。它不再有"厚度"这个概念了,它变成了二维的纸。