numpy 求秩_Numpy小记——有关axis/axes的理解

【先声明:本文尽量用简单直观的方式解释说明,可能会有些许错误——欢迎指正交流】

  • NumPy‘s array type augments the Python language with an efficient data structure useful for numerical work, e.g., manipulating matrices. NumPy also provides basic numerical routines, such as tools for finding eigenvectors.

Numpy作为Python基础科学计算库,可以很方便地用来处理多维数组的numerical work。里面包含了强大的多维数组类型:array(homogeneous)、各种可直接用于矩阵计算的函数(比如求矩阵的秩、求特征值特征向量、SVD分解等等)、用于集成C/C++或Fortran代码的工具,以及用于生成计算随机数、傅里叶变换、线性代数等等数据处理的tools。

经常用Numpy的猿应该都了解axis/axes对于array的重要性,许多对于多维数组的操作函数都涉及到这个参数,axis取值不同往往会计算出不同的结果,但axis的存在也让array的计算更加方便灵活,所以这篇文章的主要内容就是把我对于axis/axes的理解写出来,帮助大家更好地grasp这个概念,迎娶白富美走上人生巅峰(雾

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

首先,在Numpy中,维数(dimensions)通过轴(axes)来扩展,轴的个数被称作rank。这里的rank不是线性代数中的rank(秩),它指代的依旧是维数(number of dimensions)

The number of axes (dimensions) of the array. In the Python world, the number of dimensions is referred to as rank.

在数学或者物理的概念中,dimensions被认为是在空间中表示一个点所需要的最少坐标个数,但是在Numpy中,dimensions指代的是axes。也就是说,axes、dimensions、rank这几个概念是相通的。

下面举例子,比如我们在3-D空间中定义三个点,用array的形式表达:

>>> 

按照以往的理解,我们可能会认为这个array是三维的,但实际上在Numpy中,这个array的dimensions,也就是rank的值是等于2的:

>>> 

因为它只有两个axis,只不过这两个axis的长度(length)均为3:

>>> 

同时,这个矩阵在线性代数中的rank(秩)依旧是3(这里要加以区分):

>>> 

也就是说,在Numpy中,任何vector都被看作具有不同长度(length)的一维array。

~~~~~~~~~~~~~~~~~~~~~~~~~~

下面继续聊聊axis/axes,我们依旧先定义一个3-D的array:

>>> 

我们定义了一个shape为(3, 2, 4)的array,这个shape(用tuple表示)可以理解为在每个轴(axis)上的size,也即占有的长度(length),三个值的含义:axis = 0对应轴上的元素length = 3,axis = 1对应轴上的元素length = 2,axis = 2对应轴上的元素length = 4,也就是说,当我们在array中选定某个元素时,可能用到的最大索引(index)范围,举例:

>>> 

这里的b[1]和b[1, :, :]表示在axis = 0的轴上选取第一个元素,同时选取axis = 1和axis = 2上的全部元素。我们可以暂时把多个axes想象成多层layers,当然也可以想象成树结构,虽然这么说并不准确,但是为了方便想象可以暂时这么理解:

axis = 0表示第一层layer:在代码中看到的效果就是b从外向里数第一层 [ ],对应的元素(length = 3)就是第一层 [ ] 中用逗号分隔的全部元素(注意:不要关注第二层及以上 [ ] 里的逗号),很明显这一层的元素数目为3;同理,axis = 1表示第二层layer:第一层layer中用逗号分隔开的任意一个 [ ] 中,再由逗号分隔开的全部元素(length = 2);axis = 2同理,表示最后一层layer中的元素(length = 4)。这么说还是有些乱,我把刚才的array重新布置一下:

7be35042c0a85a8cd9e65bc9e385c37e.png

像这样用猿们写c/c++代码时候的风格排布下,黑色代表第一层layer,可以看到有三个元素,红色代表第二层layer,在第一层layer相同的前提下有两个元素,蓝色代表第三层layer,在第一层和第二层layer都相同的前提下有4个元素。这样也就对应上了b.shape = (3, 2, 4)的结果。

这时候如果我们想定位到元素8应该怎么索引?可以看到8在第一层layer的第二个元素内,同时在第二层layer的第二个元素内、第三层layer的第一个元素,也就是说:8是axis = 0轴上的第二个位置、axis = 1轴上的第二个位置,axis = 2的第一个位置共同retrieve到的元素。写成代码:

>>> 

噔噔噔~就酱。同时也可以发现,array有几维,我们就可以用几个integer去索引到(index)它的元素

进一步看下,对于b这个array来说,最后一个元素对应的位置就是可以用到的最大index(从0开始):

>>> 

任一axis上再大一点就会出现IndexError了:

>>> 

~~~~~~~~~~~~~~~~~~~~

之所以要设置不同的axis,是因为对于数据我们可以进行不同维度的处理。例如现在我们收集了四个同学对苹果、榴莲、西瓜这三种水果的喜爱程度进行打分的数据(总分为10),每个同学都有三个特征(嗯我一定是第四个同学- -、、):

>>> 

每一行包含了同一个人的三个特征,如果我们想看看哪个同学最喜欢吃水果,那就可以用:

>>> 

可以大概看出来同学4最喜欢吃水果。

如果我们想看看哪种水果最受欢迎,那就可以用:

>>> 

可以看出基本是榴莲最受欢迎~~~耶~~~咳、这就是axis存在的意义- -、为了方便地从不同角度对数据进行处理。

~~~~~~~~~~~~~~~~~~~~~~~

接下来我们将对更高维度的array进行计算,会用到提供axis这个parameter的一些函数对轴这个概念进一步说明:

依旧是求和:ndarray.sum(axis=None, dtype=None, out=None, keepdims=False),等价于numpy.sum(a, axis=None, dtype=None, out=None, keepdims=False):

>>> 

不设置axis的时候,sum返回所有元素的加和;当指定axis时,意味着对某一轴内的所有元素对应求和,同时这一层layer就collapsed掉了,因为这个过程是一个reduction operation。例如求b.sum(axis = 1)时,我们还是回到这个图:

8bce03acd612697655fa6dcb42a61033.png

axis = 1,直观上看,意味着我们需要关注红色layer之间的元素,顺次对axis = 0、axis = 2这两个轴上相同位置的不同红框之间的元素求和(好拗口),也就是下图中连线的元素对应求和:

1744855299733a96f5a20aa3f766877f.png

可以得到(非规范写法):

array([
        [ 2,  5,  7,  9],
        [10,  8, 10, 10],
        [ 3, 10, 10, 10]
      ])

也就是说,假设求和后的array为b_sum,就有:

b_sum[0, 0] = b[0, 0, 0]+b[0, 1, 0]; b_sum[0, 1] = b[0, 0, 1]+b[0, 1, 1]; b_sum[0, 2] = b[0, 0, 2]+b[0, 1, 2]; b_sum[0, 3] = b[0, 0, 3]+b[0, 1, 3];

b_sum[1, 0] = b[1, 0, 0]+b[1, 1, 0]; b_sum[1, 1] = b[1, 0, 1]+b[1, 1, 1]; b_sum[1, 2] = b[1, 0, 2]+b[1, 1, 2]; b_sum[1, 3] = b[1, 0, 3]+b[1, 1, 3];

b_sum[2, 0] = b[2, 0, 0]+b[2, 1, 0]; b_sum[2, 1] = b[2, 0, 1]+b[2, 1, 1]; b_sum[2, 2] = b[2, 0, 2]+b[2, 1, 2]; b_sum[2, 3] = b[2, 0, 3]+b[2, 1, 3];

即在axis = 0、axis = 2相同的情况下,对axis = 1上的全部元素进行求和所得的结果。

同时,在对这个轴上的元素求和之后,这个axis就消失了,我们可以再检查一下求和后的shape:

>>> 

可以看到,对axis = 1上的元素进行求和后的shape由原来的(3, 2, 4)坍塌到(3, 4),其他轴可以自行验证。

接下来再看排序函数:ndarray.sort(axis=-1, kind='quicksort', order=None),也可以用numpy.sort(a, axis=-1, kind='quicksort', order=None),但是前者会改变b后者不会改变b:

>>> 

第一种情况,sort默认的axis值为-1,对于我们这个case来说,相当于对axis = 2轴上的元素进行排序(也就是倒数第一个轴)。

还是拿其中一种情况举例子,对于axis = 0,先直观来看,我们需要关注黑色框之间对应的元素:

700db06e9a63e141b00f1a41761e0f8a.png

我只标注出来了在轴axis = 0上进行排序的过程中,第一组元素之间和最后一组元素之间的对应关系,其他组元素同理。

从索引的角度也可以表示:

对b[0, 0, 0]、b[1, 0, 0]、b[2, 0, 0]进行排序,并将结果由小到大重新排列;

对b[0, 0, 1]、b[1, 0, 1]、b[2, 0, 1]进行排序,并将结果由小到大重新排列;

对b[0, 0, 2]、b[1, 0, 2]、b[2, 0, 2]进行排序,并将结果由小到大重新排列;

对b[0, 0, 3]、b[1, 0, 3]、b[2, 0, 3]进行排序,并将结果由小到大重新排列;

对b[0, 1, 0]、b[1, 1, 0]、b[2, 1, 0]进行排序,并将结果由小到大重新排列;

其余同理……

嗯就先举这两个例子吧,其他带有参数axis的函数也可以从这两个角度去分析。

~~~~~~~~~~~~~~~~~~~

嘛,刚开始碰到各种axis/axes的时候也是一脸doge,所以这几天感觉get差不多了就把自己的理解写出来了,既是帮自己屡下思路也是希望这篇文章能给同样有些疑惑的你带来一点帮助、

最后,谢谢阅读~~ (〃∀〃)ゞ~~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值