在上一篇《手把手陪您学Python》51——数组的生成方法2中,我们学习了几种新的生成数组的方法,也基本上掌握了NumPy中常用的生成数组的方法。

接下来的几篇,我们将会在之前学习的数组生成方法和属性的基础上,学习几种操作数组变形、展开、翻转、拼接、拆分,以及对数组元素进行新增和删除的方法。

今天,我们先来学习一下数组的变形。

1、reshape函数

数组变形的方法非常简单,使用的就是我们已经熟悉的reshape。

但与之前学习的reshape有所不同,今天将要学习的reshape不是一种方法,更像是一个函数,其语法格式为:

numpy.reshape(arr, newshape, order='C')

其中:

arr为要修改形状的数组;

newshape为新数组的形状元组,其元素个数应与原数组相同;

order代表取值的顺序,有四个参数值,分别为'C'按行取值,'F'按列取值,'A'按照原顺序取值,以及'K'按照元素在内存中出现的顺序取值。

2、二维数组

前两个参数很好理解,关键是order参数,不同的参数值有不同的取数方法。让我们先看看几个二维数组的实例。

a、按行取值

In [1]: import numpy as np

In [2]: arr1 = np.arange(24).reshape(6, 4)
        arr1
Out[2]: array([[ 0,  1,  2,  3],
               [ 4,  5,  6,  7],
               [ 8,  9, 10, 11],
               [12, 13, 14, 15],
               [16, 17, 18, 19],
               [20, 21, 22, 23]])

In [3]: arr2 = np.reshape(arr1,(4, 6))
        arr2
Out[3]: array([[ 0,  1,  2,  3,  4,  5],
               [ 6,  7,  8,  9, 10, 11],
               [12, 13, 14, 15, 16, 17],
               [18, 19, 20, 21, 22, 23]])

In [4]: arr3 = np.reshape(arr1,(4, 6), order = 'C')   # 与省略order参数结果相同,order参数默认值为C
        arr3
Out[4]: array([[ 0,  1,  2,  3,  4,  5],
               [ 6,  7,  8,  9, 10, 11],
               [12, 13, 14, 15, 16, 17],
               [18, 19, 20, 21, 22, 23]])

按行取值C是order参数的默认值,也是我们大多数人首先会想到的数组变形和元素排序的方法。就是将原数组中的元素,按行逐个取值,并根据新数组的形状,再按行逐个写入。

b、按列取值

以此类推,按列取值就是将原数组中的元素,按列逐个取值,并根据新数组的形状,再按列逐个写入。

这样说可能有些抽象,让我们先看一个例子。

In [5]: arr4 = np.reshape(arr1,(4, 6),order = 'F')
        arr4
Out[5]: array([[ 0, 16,  9,  2, 18, 11],
               [ 4, 20, 13,  6, 22, 15],
               [ 8,  1, 17, 10,  3, 19],
               [12,  5, 21, 14,  7, 23]])

与按行取值不同,当按列取值时,元素的取值顺序就从原数组第一行0、1、2、3、4、5、6的开始方向,变成了从原数组第一列0、4、8、12、16、20的取值顺序,并根据新数组的形状,从第一列开始逐个元素纵向写入,而不再按行写入了。

举一个更加形象的例子。

按行取值就像我们现代人将一页文字誊写在另一页大小不同的纸上的过程。根据新纸的大小,逐行誊写原来纸上的文字,一行一行看,一行一行誊写。

按列取值可以想象成古人将一页文字誊写在另一页大小不同的纸上的过程。因为古人都是竖着写字的,所以在誊写的时候,就要一竖列一竖列地看,并一竖列一竖列地誊写在新纸上。当然,这里是不考虑古人从右向左的书写习惯的,而是假定阅读和书写都是纵向并从左向右的,这样就容易理解了。

此外,我们还可以将reshape的过程理解为先把原来的数组按照一定的规则打散或者说按顺序排成一排,然后再按照新的形状重新排列或者说摆放。

3、多维数组

上面的实例和说明都是以二维数组为例的,因为可以按照行和列的方式进行描述,和输出的结果的展现形式也是一致的,所以很好理解。

但如果将其扩展到三维或者更高的维度,可能就需要我们更加深入地理解C和F的取值规则了。

这里我们就以三维数组为例,对于更高维的数组来说,原理都是一样的。

a、按行取值

In [6]: arr5 = np.arange(24).reshape(3, 4, 2)
        arr5
Out[6]: array([[[ 0,  1],
                [ 2,  3],
                [ 4,  5],
                [ 6,  7]],

               [[ 8,  9],
                [10, 11],
                [12, 13],
                [14, 15]],

               [[16, 17],
                [18, 19],
                [20, 21],
                [22, 23]]])

In [7]: arr6 = np.reshape(arr5,(2, 3, 4), order = 'C')
        arr6
Out[7]: array([[[ 0,  1,  2,  3],
                [ 4,  5,  6,  7],
                [ 8,  9, 10, 11]],

               [[12, 13, 14, 15],
                [16, 17, 18, 19],
                [20, 21, 22, 23]]])

因为维度已经扩展到了三维,所以我们就不能再用行列进行描述了。

根据arr5的shape属性可以看到,arr5是一个外层有3个元素,中间层有4个元素,内层有2个元素的三维数组。

当按照C来取值时:

先取的是第一个外层中的第一个中层中的内层的第一个元素0、第二个元素1,

然后是第一个外层中的第二个中层中的内层的第一个元素2、第二个元素3,

再然后是第一个外层中的第三个中层中的内层的第一个元素4、第二个元素5,

。。。

以此类推,直到第三个外层中的第四个中层中的内层的第一个元素22、第二个元素23。

也就是说,取值的顺序是按照先内层、再中层、再外层的顺序,由内及外逐个取值。

当在新数组重新摆放时,也同样是:

先在第一个外层中的第一个中层中的内层4个元素位置进行摆放,分别放置0、1、2、3,

直到第二个外层中的第三个中层中的内层4个元素位置进行摆放,分别放置20、21、22、23。

也就是说,重新排序的顺序,也是按照先内层、再中层、再外层的顺序,由内及外逐个排列。

基于从内到外的取值和排序规则,我们回头再看,二维数组其实也是遵循这样的规则。

行就是内层,列就是外层。

当按行取值时,先行后列逐行取值,也就是先内后外。

b、按列取值

按列取值时会稍微复杂一些,而且与按行取值先内后外的顺序不同,按列取值是先外后内。

也就是先逐个取外层的元素,再逐个取中层的元素,最后才是内层的元素。

这样说肯定不好理解,让我们还是先看下实例。

In [8]: arr5 = np.arange(24).reshape(3, 4, 2)
        arr5
Out[8]: array([[[ 0,  1],
                [ 2,  3],
                [ 4,  5],
                [ 6,  7]],

               [[ 8,  9],
                [10, 11],
                [12, 13],
                [14, 15]],

               [[16, 17],
                [18, 19],
                [20, 21],
                [22, 23]]])

In [9]: arr7 = np.reshape(arr5,(2, 3, 4), order = 'F')
        arr7
Out[9]: array([[[ 0,  4,  1,  5],
                [16, 20, 17, 21],
                [10, 14, 11, 15]],

               [[ 8, 12,  9, 13],
                [ 2,  6,  3,  7],
                [18, 22, 19, 23]]])

在按列取值时:

首先取的是第一个外层的第一个中层的内层的第一个元素0,

之后是第二个外层的第一个中层的内层的第一个元素8,

之后是第三个外层的第一个中层的内层的第一个元素16,

再之后是第一个外层的第二个中层的内层的第一个元素2,

再之后是第二个外层的第二个中层的内层的第一个元素10,

再之后是第三个外层的第二个中层的内层的第一个元素18。。。

当按列摆放时:

先将0放在第一个外层的第一个中层的内层的第一个元素,

再将8放在第二个外层的第一个中层的内层的第一个元素,

再将16放在第一个外层的第二个中层的内层的第一个元素,

再将2放在第二个外层的第二个中层的内层的第一个元素,

再将10放在第一个外层的第三个中层的内层的第一个元素,

再将18放在第二个外层的第三个中层的内层的第一个元素。。。

对于二维数组按列取值时也是一样的,所谓按列取值,其实就是先依次取外层(第一列)的第一个元素,再一次取外层(第二列)的第二元素,以此类推。

c、取值顺序的比较

上面的文字描述可能不太容易看出来从内向外取值以及从外向内取值的区别,这里再用两组数组做对比,加深大家对于取值顺序的理解。

如果我们将数组的每一个维度看做一个坐标轴,每一个维度的第几个元素就是坐标轴上的值,那么所元素的位置就能够用一个坐标来表示,那么123就可以代表第一个外层的第二个中层的第三个内层元素。

按照这种方法,当按行取值时,取值元素的顺序及坐标为:

111

112

121

122

131

132

141

142

211

212

221

222

231

232

241

242

311

312

321

322

331

332

341

342

按行取值时,元素摆放的顺序及坐标为:

111

112

113

114

121

122

123

124

131

132

133

134

211

212

213

214

221

222

223

224

231

232

233

234

按列取值时,取值元素的顺序及坐标为:

111

211

311

121

221

321

131

231

331

141

241

341

112

212

312

122

222

322

132

232

332

142

242

342

按列取值时,元素摆放的顺序及坐标为:

111

211

121

221

131

231

112

212

122

222

132

232

113

213

123

223

133

233

114

214

124

224

134

234

从数字的变化规律中可以看到:

当按行取值时,是先遍历内层的各个元素,然后是中层,最后是外层,就是我们所谓的先内后外。

当按列取值时,是先遍历外层的各个元素,然后是中层,最后是内层,就是我们所谓的先外后内。

由于按行取值C以及按列取值F会在我们后面的内容中多次出现,所以大家一定要将这个取值和摆放的顺序弄清楚。

对于二维数组的情况,可以很容易地按照行列的顺序进行思考,而出现多维数组的情况,则要稍微复杂一点,需要按照从内向外,以及从外向内的顺序去思考。

虽然看上去比较复杂,但只要弄明白一次,能自己准确地进行取值和摆放一次,相信就不是什么问题了,对于理解后面的展开、翻转等操作时,也非常有帮助。

上面是order的参数值为C和F的情况,因为是NumPy的基础规则,而且后面会经常用到,所以用了较大的篇幅进行了详细讲解。

对于order的参数值为A和K的情况,由于涉及底层内存的信息,我们暂时接触不上,这里就不过多介绍了。到时如有涉及类似参数的地方,我们再进行讲解。

以上就是对于数组变形方法的介绍,并主要说明了order参数值为C和F时的取值规则,请大家务必重点理解。