如何解读numpy中ndarray数组的order存储顺序参数?
发布于 作者:苏南大叔 来源:程序如此灵动~
作为目前python代码中强大又常见的第三方数组类型,ndarray类型,在创建数组的时候有个order参数,可以控制元素在内存中的存储顺序。就是说在ndarray形成的时候,输入的元素的实际位置,受到order参数的控制,控制其元素的实际摆放位置。

苏南大叔的"程序如此灵动"博客,记录苏南大叔的编程经验内容。本文测试环境:win10,python@3.11.0,numpy@1.24.2。
以前有首歌叫做“都选C”来着吧?其实很适合本文,对于和ndarray扯上关系的order参数,选择C是大概率没有问题的。选择F大概率是会出一些意外的。
前文回顾
本文内容的正确理解,可能需要您了解如下内容:
- 如何定义一个
ndarray:https://newsn.net/say/numpy-ndarray.html - 使用
range()生成一个范围:https://newsn.net/say/ndarray-arange.html ndarray类型的扁平化:https://newsn.net/say/ndarray-flatten.html- 使用
reshape()重塑数组形状:https://newsn.net/say/ndarray-reshape.html zeros()和ones()生成ndarray:https://newsn.net/say/numpy-zeros-ones.html
重要结论:order!!!
对于python的ndarray,相关选项就一个:fortran_order!取值是True或者False!所以,不论本文中的后续内容如何说明,极有可能的真相就是:就是二分法,是不是按列读取!fortran_order!
可以把ndarray保存到.npy或者.npz文件里面,就可以看到其描述类似为:
{'descr': '<U1', 'fortran_order': False, 'shape': (2, 4), }有哪些order?
可用的order有四个,但是常见的就两个。
| 参数 | 用途 | 说明 |
|---|---|---|
| C | 按行读取 | 最常见 |
| F | 按列读取 | 相对不常见 |
| K | 按内存读取 | -- |
| A | 按行或列读取 | 对于python来说,A就等于C |
使用order参数的函数有如下几个,默认order参数都是C。
numpy.copy()numpy.ravel()numpy.flatten()numpy.reshape()(无法使用K)numpy.zeros()numpy.ones()- 其它
只有定义numpy.array()中的order默认是K(内存)。
order不影响ndarray的视图
order不影响ndarray的视图。也就是说,无论order写的是什么值,最终展现出来的视图(通俗的说,print出来的数据)都是一样的。只是影响成员在内存中的存放顺序,但是这个对于大多数人来说,是无感的。
下面的代码中(不限于这些),虽然order设置不同,但是,这些变量的打印值一模一样。
import numpy as np
a = np.array([["苏", "南", "大", "叔"], ["技", "术", "博", "客"]], order='C')
b = np.array([["苏", "南", "大", "叔"], ["技", "术", "博", "客"]], order='F')
c = np.array([["苏", "南", "大", "叔"], ["技", "术", "博", "客"]], order='K')
d = np.array([["苏", "南", "大", "叔"], ["技", "术", "博", "客"]], order='A')
print("a:", a)
print("b:", b)
print("c:", c)
print("d:", d)输出:
a: [['苏' '南' '大' '叔']
['技' '术' '博' '客']]
b: [['苏' '南' '大' '叔']
['技' '术' '博' '客']]
c: [['苏' '南' '大' '叔']
['技' '术' '博' '客']]
d: [['苏' '南' '大' '叔']
['技' '术' '博' '客']]
使用.strides可以做部分区分
因为实际情况下,order参数A效果等于C,K效果也很可能等于C。所以,使用.strides是可以区分F的情况出来。
strides:进展,步法。正常情况下来说,一个元素占四个字节(也不一定,这里就是举个简单的例子。)
测试代码:
import numpy as np
a = np.array([["苏", "南", "大", "叔"], ["技", "术", "博", "客"]], order='C')
b = np.array([["苏", "南", "大", "叔"], ["技", "术", "博", "客"]], order='F')
c = np.array([["苏", "南", "大", "叔"], ["技", "术", "博", "客"]], order='K')
d = np.array([["苏", "南", "大", "叔"], ["技", "术", "博", "客"]], order='A')
print("C:", a.strides)
print("F:", b.strides)
print("K:", c.strides)
print("A:", d.strides)输出:
C: (16, 4)
F: (4, 8)
K: (16, 4)
A: (16, 4)
可以看到:
| order | 取值 | 第一维度(行)内存差值 | 第二维度(列)内存差值 |
|---|---|---|---|
| C/K/A | (16,4) | 4个元素*4个字节=16字节 | 4个字节 |
| F | (4,8) | 4个字节 | 8个字节 |
查看内存中的实际排序
可以利用了数据扁平化函数的order='K'参数来进行查看。[用魔法打败魔法]。
这里可以使用.zeval()和.flatten()来实现数据的扁平化,但是不能使用reshape(-1),会得到错误提示信息:
d2 = d.reshape(-1,order="K")
^^^^^^^^^^^^^^^^^^^^^^^
ValueError: order 'K' is not permitted for reshaping参考文章:

测试order='C/K/A'
import numpy as np
a = np.array([["苏", "南", "大", "叔"], ["技", "术", "博", "客"]], order='C')
c = np.array([["苏", "南", "大", "叔"], ["技", "术", "博", "客"]], order='K')
d = np.array([["苏", "南", "大", "叔"], ["技", "术", "博", "客"]], order='A')
a2 = a.flatten(order="K")
print(a2, a2.strides)
c2 = c.flatten(order="K")
print(c2, c2.strides)
d2 = d.flatten(order="K")
print(d2, d2.strides)输出:
['苏' '南' '大' '叔' '技' '术' '博' '客'] (4,)
['苏' '南' '大' '叔' '技' '术' '博' '客'] (4,)
['苏' '南' '大' '叔' '技' '术' '博' '客'] (4,)
测试order='F'
import numpy as np
b = np.array([["苏", "南", "大", "叔"], ["技", "术", "博", "客"]], order='F')
b2 = b.ravel(order="K")
print(b2, b2.strides)
# b2 = b.flatten(order="K")
# print(b2, b2.strides)输出:
['苏' '技' '南' '术' '大' '博' '叔' '客'] (4,)上面的代码显示:内存中的值是按着一列一列的顺序存储的。

import numpy as np
b0 = [["苏", "南", "大", "叔"], ["技", "术", "博", "客"]]
b = np.array(b0, order='F').swapaxes(1,0)
print(b, b.strides)
b2 = b.flatten(order="K")
print(b2, b2.strides)输出:
[['苏' '技']
['南' '术']
['大' '博']
['叔' '客']] (8, 4)
['苏' '技' '南' '术' '大' '博' '叔' '客'] (4,)这个代码显示:虽然中间值b被转置了,但是内存中的存储顺序依然没有发生改变。
相关文章:

结论
可见,order参数在定义的时候,没有什么大的影响的。但是对其成员进行读取的时候,根据order的不同,可能会有不同的结果出现。
更多苏南大叔的python经验文章,请点击: