NumPy数组axis=-1含义及np.stack输出维度构成解析
Numpy stack 操作与 axis 参数问题解答
首先纠正一个认知误区:不存在通用的“三维数组维度=(行数,列数,维度数)”的固定规则,这个对应关系只在“存储二维平面点坐标、最后一维存xy值”的特定场景下成立。numpy根本不关心数组里存的是行、列还是别的内容,维度顺序完全由数组变换时的操作决定,每个维度的长度就是沿这个轴切分数组时,能拿到的元素总数。
前置基础
- 你调用
np.meshgrid(x,y,indexing="xy")返回的cart_indexing是长度为2的列表,内部两个元素均为shape是(5,5)的二维数组,分别对应笛卡尔坐标系下的x坐标网格、y坐标网格,两个数组形状完全一致,这是np.stack可以正常运行的必要条件。 np.stack的核心逻辑不是在已有维度上拼接数组,而是全新插入一个维度:新维度的长度等于你传入的待拼接数组的总个数(本例中就是2),axis参数的作用就是指定这个新维度插入在哪个位置。
问题1:np.stack(cart_indexing, axis=1)输出shape为(5,2,5)的推导逻辑
你可以把插入新维度的过程理解为给原有形状“插空位”:
原有两个待拼接数组的形状是(5,5),对应维度索引从左到右是0、1,插入新维度时共有3个可选位置,对应3种输出shape:
- 插在最前面(索引0位置):新形状为
(2,5,5),对应你运行的axis=0的结果:最外层长度2,分别放完整的x网格、完整的y网格,和输出结果完全匹配。 - 插在两个原有维度中间(索引1位置):新形状为
(5,2,5),也就是你疑惑的结果。这个形状对应的实际含义是:- 最外层维度长度5,对应原数组的第0维(也就是y轴的5个采样值:1、3.25、5.5、7.75、10)
- 中间新插入的维度长度2,分别对应固定y值下的整行x坐标、整行y坐标
- 最内层维度长度5,对应原数组的第1维(也就是x轴的5个采样值:-10、-7.5、-5、-2.5、0)
对照输出结果可以直接验证:第一个子块的两行分别是y=1时的整行x值、整行y值,第二个子块对应y=3.25的情况,以此类推。
- 插在最后面(索引2位置):新形状为
(5,5,2),也就是你运行axis=-1的结果。
问题2:axis=-1的实际作用
numpy所有支持axis参数的函数,都支持负索引写法,规则全局统一:负索引从数组的最后一个维度开始往前计数,等价换算公式为 实际正索引 = 数组总维度数 + 负索引值。
针对本例的stack场景:
- 待拼接的原数组是二维,拼接完成后输出是三维数组,总维度数为3,维度正索引从前往后是0、1、2
- 因此
axis=-1换算成正索引就是3 + (-1) = 2,也就是把新维度插在最后一位,得到shape为(5,5,2)的结果
这个输出正好符合你之前接触的“(行数,列数,维度数)”场景:每个(i,j)位置上长度为2的小数组,就是网格中对应坐标点的(x,y)值,比如[0,0]位置是[-10, 1],[0,1]位置是[-7.5, 1],这也是坐标网格处理中最常用的写法,可直接生成每个点的坐标对矩阵。
复现代码
import numpy as np x = np.linspace(start=-10, stop=0, num=5, endpoint=True) y = np.linspace(start=1, stop=10, num=5) cart_indexing = np.meshgrid(x, y, indexing="xy") # 笛卡尔索引生成网格 # axis=0 输出shape (2,5,5) res0 = np.stack(cart_indexing, axis=0) # axis=1 输出shape (5,2,5) res1 = np.stack(cart_indexing, axis=1) # axis=-1 输出shape (5,5,2) res_neg1 = np.stack(cart_indexing, axis=-1)
内容的提问来源于stack exchange,提问作者matt.aurelio
相关产品推荐
相关产品推荐

