【Python进阶】你真的明白NumPy中的ndarray吗?

欢迎来到专栏《Python进阶》。在这个专栏中,我们会讲述Python的各种进阶操作,包括Python对文件、数据的处理,Python各种好用的库如NumPy、Scipy、Matplotlib、Pandas的使用等等。我们的初心就是带大家更好的掌握Python这门语言,让它能为我所用。

今天是《Python进阶》专栏的第三期,在本期中,我们将主要介绍Numpy的一些进阶知识。

作者&编辑 | 汤兴旺

如果你想掌握Python,那么NumPy是你必须要精通的。NumPy实际上是Python语言的一个扩展程序库,支持高维数组与矩阵运算,提供了大量的数学函数库。

1 ndarray内存机制

我们知道NumPy最重要的一个特点是其N维数组对象ndarray。通常ndarray内部由以下内容组成。

数据指针:一个指向实际数据的指针;

数据类型(dtype):描述了数组中每个元素所占的字节数;

维度(shape):一个表示数组形状(各维度大小)的元组。

跨度(strides):一个表示从当前元素前进到下一个元素需要跨过的字节数。

我们通过下面的代码看下ndarray的内容:

import numpy as np

a = np.arange(1,25).reshape((2,2,2,3))

print(type(a))

print(a.shape)

print(a.dtype)

print(a.strides)

这里的shape是指每个维度元素的个数。这里的四维数组每个维度的元素个数分别为2、2、2和3。咦,这好像不对呀!2+2+2+3=9,这不等于24呀!难道我分析错了吗?

当然不是,我们首先将a输出来,结果如下:

这里最里面一层[ ]可以代表1个一维数组,里面有3个元素。加粗的红色一对[ ]代表二维数组,它里面有二个[ ],即二维数组中有2个一维数组。所以认为第三维度中元素个数为2;加粗的紫色一对[ ]代表三维数组,它里面有二个[ ],即三维数组中有2个二维数组。所以认为第二维度中元素个数为2。同理,第一维度中元素个数为2。

相信你现在应该明白了shape中的元组为啥是(2,2,2,3)了吧。

说完shape,我们再来看下dtype,它是指数组元素类型,注意,这里的数组元组就是指1,2,...,23,24这样的元素。另外数组中每个元素的类型都是相同的,在这个数组中,数组中每个元素类型都为int32。

最后我们再分析下跨度(strides)。它是指从当前元素前进到下一个元素需要跨过的字节数。

我们可以看出上面例子中的strides=(48,24,12,4)。那么这四个数是怎么来的呢?

我们在上面的四维数组中,dtype 为 int,而int 占 4个字节。而第四维度里面有3个元素,总字节数为12,所以从第四度跨到第三维度需要跨过的字节数为12;第三维度里面有2个元素(一维数组),每个一维数组的总字节数为12,所以从第三维度跨到第二维度需要跨过的字节数为24。同理,从第二维度跨到第一维度的字节数为48。

所以上面例子中的四维数组的跨度为(48,24,12,4),它在内存中的表示如下图所示:

从上图可以明显的看出从要想到达第三维度(轴2),必须跨过第四维度(轴3),需要跨过3个元素,字节数为12;要想到达第一维度(轴0),必须跨过第二、三、四维度,总共12个元素,字节数为12*4=48。

这也就是在NumPy 中数据存储的方式。它存储在一个均匀连续的内存块中,可以这么理解,NumPy 将多维数组在内部以一维数组的方式存储,我们只要知道了每个元素所占的字节数(dtype)以及每个维度中元素的个数(shape),就可以快速定位到任意维度的任意一个元素。

2 NumPy高维数组索引与转置

2.1 索引

当提到索引时,你可能觉得很简单,不就是通过索引获取某个元素吗?道理的确是这样的。但是在面对高维数组时,通过索引来获取某个元素还是比较麻烦的。

下面我们通过一个案例来分析下一个四维数组的索引。

如果我想取得上图中17这个元素,应该怎么办呢?

首先将这个四维数组用上图的轴的形式来表示。我们可以先把它看成四个块,其中第0轴和第1轴确定某个块的位置,第2轴和第3轴确定块中某个元素具体的位置。

图中的17在第3块,如下图的黄色部分,用0轴和1轴来表示的话,索引就是[1,0]。

现在块的位置确定了,接下来我们确定块中元素的位置。如下图所示:

17这个元素在上图中的索引为[1,1]。接下来我们只需要把确定块的索引[1,0]和确定块中元素的索引[]按照[第0轴,第1轴,第2轴,第3轴]这样的格式合并即可,在这个案例中,合并后17的索引为[1,0,1,1]。

当然,你可以用下面的代码确认下,我们的分析是否正确。

import numpy as np

a = np.arange(1,25).reshape((2,2,2,3))

print(a[1,0,1,1])

这个就是高维数组的索引,你已经明白了吗?

2.2 高维数组转置

高维数组的转置一直是学习NumPy的一个难点,尽管在NumPy中只需要调用numpy.transpose就可以完成转置操作,但是你真的能分析清楚为什么结果是这样的吗?尤其是高维数组。下面我们就举一个比较简单的例子来分析下。如下:

上图是原数组,我们经过下面的代码进行转置,会得到如下的结果:

import numpy as np
a = np.arange(16).reshape((2,2,4))

b = a.transpose((1,0,2))
print(b)

转置后的结果:

相信你已经看出了具体的差别了,那就是轴的索引顺序的互换。因为在代码中我们要求0轴和1轴互换,因此转置后的结果实际上就是a[1,0]会变成原数组a[0,1];a[0,1]会变成原数组a[1,0]。如果用图表示,就如下图所示:

相信你已经明白了其中的原理了,接下来留一个思考题,如下:

请问,从左到右怎么转置才能得到!

总结

本期我们介绍了ndarray的内存机制及高维数组的索引和转置。NumPy的知识还有很多,上面介绍的只是NumPy中比较难理解的几个问题,若想更加系统的学习NumPy及知道上面思考题的分析过程和答案,请移步我们的知识星球!

下期预告:Python库Scipy的高级应用

有三AI编程与开源框架知识星球

有三AI编程与开源框架知识星球创建了,欢迎加入,希望大家能借助这个平台,扎实自己的编程基础。

转载文章请后台联系

侵权必究

(0)

相关推荐