第12章 NumPy高级应用

ndarray对象的内部机理

NumPy的ndarray提供了一种将同质数据块(可以是连续或跨越译注1的,稍后将详细讲解)解释为多维数组对象的方式。正如你之前所看到的那样,数据类型(dtype)决定了数据的解释方式,比如浮点数、整数、布尔值等。

ndarray如此强大的部分原因是所有数组对象都是数据块的一个跨度视图(strided view)。你可能想知道数组视图arr[::2,::-1]不复制任何数据的原因是什么。简单地说,ndarray不只是一块内存和一个dtype,它还有跨度信息,这使得数组能以各种步幅(step size)在内存中移动译注2。更准确地讲,ndarray内部由以下内容组成:

·一个指向数组(一个系统内存块)的指针。

·数据类型或dtype。

·一个表示数组形状(shape)的元组,例如,一个10×5的数组,其形状为(10,5)。

  1. In [8]: np.ones((10, 5)).shape
  2. Out[8]: (10, 5)

·一个跨度元组(stride),其中的整数指的是为了前进到当前维度下一个元素需要“跨过”的字节数,例如,一个典型的(C顺序,稍后将详细讲解)3×4×5的float64(8个字节)数组,其跨度为(160,40,8)。

  1. In [9]: np.ones((3, 4, 5), dtype=np.float64).strides
  2. Out[9]: (160, 40, 8)

虽然NumPy用户很少会对数组的跨度信息感兴趣,但它们却是构建非复制式数组视图的重要因素。跨度甚至可以是负数,这样会使数组在内存中后向移动,比如在切片obj[::-1]或obj[:,::-1]中就是这样的。

图12-1简单地说明了ndarray的内部结构。

第12章 NumPy高级应用 - 图1

图12-1:NumPy的ndarray对象

NumPy数据类型体系

你可能偶尔需要检查数组中所包含的是否是整数、浮点数、字符串或Python对象。因为浮点数的种类很多,判断dtype是否属于某个大类的工作非常繁琐。幸运的是,dtype都有一个超类(比如np.integer和np.floating),它们可以跟np.issubdtype函数结合使用:

  1. In [10]: ints = np.ones(10, dtype=np.uint16)
  2.  
  3. In [11]: floats = np.ones(10, dtype=np.float32)
  4.  
  5. In [12]: np.issubdtype(ints.dtype, np.integer)
  6. Out[12]: True
  7.  
  8. In [13]: np.issubdtype(floats.dtype, np.floating)
  9. Out[13]: True

调用dtype的mro方法即可查看其所有的父类:

  1. In [14]: np.float64.mro()
  2. Out[14]:
  3. [numpy.float64,
  4. numpy.floating,
  5. numpy.inexact,
  6. numpy.number,
  7. numpy.generic,
  8. float,
  9. object]

大部分NumPy用户完全不需要了解这些知识,但是这些知识偶尔还是能派上用场的。图12-2说明了dtype体系以及父子类关系注1

第12章 NumPy高级应用 - 图2

图12-2:NumPy的dtype体系

译注1:也就是非连续存储。

译注2:数组本身不能移动,这里实际上说的是指针。

注1:有些dtype的名称后面带有下划线,这是为了避免NumPy类型和Python类型之间的变量名冲突。