numpy数组的轴和“约减”(聚合)
numpy数组的轴和“约减”(聚合)
- 以下摘自知乎玉来愈宏的文章
一、轴(axis)
-
如同笛卡尔坐标系一样,NumPy张量也有轴。现在我们先以熟悉二维向量为例来说明这个概念,二维向量的轴是沿行和列的方向
-
轴的编号是从0开始的,因此“第一轴”实际上是“axis 0”。“第二轴”是“axis 1”,依此类推。在可视化观感上,“axis 0”就是向下的行方向轴,“axis 1”是沿着水平方向的列轴,如下图所示
-
以维度为(3,4,5)的三维数组为例:它有3个维度,因此,它的轴有3个,即:“轴0”、“轴1”、“轴2”。
-
从轴0上看,该数组包含3个元素,进入到轴0中的任何1个元素的空间中,可以看到,这个元素又包含两个轴,对应于三维数组的轴1和轴2
二、数组基于轴的“约减”(聚合)操作
-
它表示将众多数据按照某种规则合并成一个或几个数据。“约减”之后,数据的个数在总量是减少的。
-
numpy中常用的”约减“方法有7种,不指定轴则默认求的是整个数组,注意:中值和极值这两个方法是numpy开头:
ndarray.sum(axis=None)
,求和ndarray.mean(axis=None)
,求平均值numpy.median(ndarray,axis=None)
,求中值(中位数)ndarray.max(axis=None)
,最大值ndarray.min(axis=None)
,最小值numpy.ptp(ndarray,axis=None)
,极值(最大值和最小值之差)ndarray.std(axis=None)
,标准差(反应一组数据离平均值的分散程度,值越大说明波动越大,越不稳定)
-
在这里,“约减”的“减”,并非减法之意,而是向量元素的减少。比如说,数组的加法操作,实际上就是一种“约减”操作,因为它对数组中的众多元素按照某些运算,最后合并为少数的一个或几个值。示例代码如下
In [1]:import numpy as np
In [2]: a = np.ones((2,3)) #创建形状为2×3,元素值均为1的矩阵
In [3]: a #显示该矩阵
Out[3]:
array([[1., 1., 1.],
[1., 1., 1.]])
In [4]: a.sum() #将6个矩阵元素求和后变成一个元素
Out[4]: 6.0
-
我们可以推而广之,求N维数组的均值(mean)、最大值(max)和最小值(min)等,这些操作都属于约减操作
-
但有时,我们会有这样的需求,对指定维度方向的值进行统计,如统计某一行(或列)的和、均值、最大值、最小值等。这个时候,就需要给“约减”指令指定方向
-
sum(求和)、min(最小值)、max(最大值),mean(均值)、median(中位数)等统计函数,它们都有一个名为操作轴(axis)的参数,其默认值为None,也就是不指定约减方向,它将所有数据都“约减”为一个元素
-
拿np.sum()方法来说,如果axis的值为0,可简单地理解为从垂直轴方向进行“约减”。如果axis的值为1,则可以简单理解为从水平轴方向进行“约减”
-
In [3]: a #显示该矩阵 Out[3]: array([[1., 1., 1.], [1., 1., 1.]]) In [5]: a.sum(axis = 0) #垂直方向约减 Out[5]: array([2., 2., 2.]) In [6]: a.sum(1) #垂直方向约减 Out[6]: array([3., 3.]) """ 在In [5]处,我们使用关键字参数axis = 0,显式给出了约减轴的方向为垂直方向,而在In [6]处,仅仅给出整数值1,它等同于axis = 1,即在水平方向约减,“axis=”做了省略 """
-
如下图所示
-
三、三维及以上数组的“约减”分析
-
二中的图的解释虽然直观,但也有很大的局限性。这是因为,这种可视化轴的概念在维度不超过2时比较容易理解,而且轴0表示垂直方向,轴1表示水平方向,是人为强加的。当维度>=3时,我们难以找到可直观理解的方向
-
所以,更加普适的解释,应该是按张量括号层次的方式来理解。张量括号由外到内,对应从小到大的维数。比如,对于一个三维的数组
[[[1, 1, 1], [2, 2, 2]], [[3, 3, 3],[4, 4, 4]]]
,它有三层括号,其轴序由外到内分别为 0,1,2 -
axis参数决定聚合哪个轴的数据。聚合意味着这个轴将坍缩(折叠),之所以会坍缩,就是因为执行了“约减”操作
-
下面以
np.sum
为例
1. 当axis = 0时
- axis = 0,就是在第0个维度的元素之间进行求和操作,即拆掉最外层括号后(即相当于这个轴坍缩了),对应有2个最大“颗粒度”的元素 [[1, 1, 1], [2, 2, 2]] 和 [[3, 3, 3], [4, 4, 4]] ,这两个元素都是二维数组,然后这两个二维数组实施对应索引位置元素的求和操作,其结果为[[4, 4, 4], [6, 6, 6]]。没有被“约减”的维度(轴),其括号层次保持不变
In [7]: a = np.array([[[1, 1, 1], [2, 2, 2]], [[3, 3, 3],[4, 4, 4]]])
In [8]: a #输出验证
Out[8]:
array([[[1, 1, 1],
[2, 2, 2]],
[[3, 3, 3],
[4, 4, 4]]])
In [9]: a.sum(axis = 0) #第0个轴方向约减求和
Out[9]:
array([[4, 4, 4],
[6, 6, 6]])
- 上面运算过程更直观的演示如下图
2. 当axis = 1时
-
axis=1,就是在第1个维度的元素之间进行求和操作,也就是拆掉中间层括号(即这个维度坍缩了),拆后对应的元素有[1, 1, 1], [2, 2, 2]和[3, 3, 3], [4, 4, 4]。
-
需要注意的是,“约减”操作的实施范围为,将坍缩后同一个括号层次内的最大“颗粒度”的张量进行约减操作,即[1, 1, 1]和[2, 2, 2]向量相加,[3, 3, 3]和[4, 4, 4]向量相加。
-
没有被“约减”的维度,其括号保持不变,结果得到[[3, 3, 3],[7, 7, 7]]
In [10]: a.sum(axis = 1)
Out[10]:
array([[3, 3, 3],
[7, 7, 7]])
- 上面运算过程更直观的演示如下图
3. 当axis = 2时
-
axis = 2,就是拆掉最内层括号(rank = 2),然后对最内层括号元素实施求和操作,即1+1+1=3,2+2+2=6,3+3+3=9,4+4+4=12。
实施“约减”操作之后,该层括号坍缩消失,其他维度的括号保留。结果得到[[3,6], [9,12]]
In [11]: a.ndim #查看张量a的维度
Out[11]: 3
In [12]: b = a.sum(axis = 2) #在第2个轴上约减
In [13]: b
Out[13]:
array([[ 3, 6],
[ 9, 12]])
In [14]: b.ndim #查看被约减后张量b的维度
Out[14]: 2
- 上面运算过程更直观的演示如下图
4. 小结
- 事实上,每个维度经过“约减”之后都会消失。 这个有点类似于刘慈欣在《三体》小说中的“降维打击”。被“降维打击”的维度,由axis参数来指定,比如axis为0时,就是把0维“干掉”。具体如何干掉呢?其实就是在这个维度上执行“约减“操作!完整表述就是对0维执行sum操作,从而达到约减第0维的效果。其他维度的解释类似,不再赘述
- 其他可实施约减的函数,如max(最大值)、min(最小值)和mean(均值)等,其轴方向的“约减”内涵与sum操作也是类似的,示例代码如下
In [15]: a = np.linspace(1,9,9).reshape(3,3)
In [16]: a
Out[16]:
array([[1., 2., 3.],
[4., 5., 6.],
[7., 8., 9.]])
In [17]: print(a.max(0),a.max(1),a.max()) #在第0轴、第1轴、不指定轴(全轴)进行最大值约减
[7. 8. 9.] [3. 6. 9.] 9.0
In [18]: print(a.mean(0),a.mean(1),a.mean()) #在第0轴、第1轴、不指定轴(全轴)进行均值约减
[4. 5. 6.] [2. 5. 8.] 5.0