1. ndarray 对象的内存结构

  1. ndarray对象在内存中的结构如下: array

    • ndarray.dtype:存储了数组保存的元素的类型。float32
    • ndarray.ndim:它是一个整数,保存了数组的维度,即多少个轴
    • ndarray.shape:它是一个整数的元组,每个元素一一对应地保存了数组某个维度的大小(即某个轴的长度)。
    • ndarray.strides:它是一个整数的元组,每个元素保存着每个轴上相邻两个元素的地址差。即当某个轴的下标增加1 时,数据存储区中的指针增加的字节数
    • ndarray.data:它指向数组的数据的存储区

    array_code 可以看到:该数组中元素类型为float32;该数组有2 个轴。每个轴的长度都是 3 个元素。第 0 轴增加1时,下标增加 12字节(也就是 3个元素,即一行的距离); 第 1 轴增加 1时,下标增加 4字节(也就是一个元素的距离)。

  2. 元素在数据存储区中的排列格式有两种:C语言格式和Fortran语言格式。

    • C语言中,多维数组的第 0 轴是最外层的。即 0 轴的下标增加 1时,元素的地址增加的字节数最多
    • Fortran语言中,多维数组的第 0 轴是最内层的。即 0 轴的下标增加 1时,元素的地址增加的字节数最少

    numpy中默认是以 C语言格式存储数据。如果希望改为Fortran格式,则只需要在创建数组时,设置order参数为"F" order

  3. 数组的flags属性描述了数据存储区域的一些属性。你可以直接查看flags属性,也可以单独获取其中某个标志值。

    • C_CONTIGUOUS:数据存储区域是否是C语言格式的连续区域
    • F_CONTIGUOUS:数据存储区域是否是F语言格式的连续区域
    • OWNDATA:数组是否拥有此数据存储区域。当一个数组是其他数组的视图时,它并不拥有数据存储区域,通过视图数组的base属性可以获取保存数据存储区域的那个原始数组。 flags
  4. 数组的转置可以通过其T属性获取。转置数组可以简单的将其数据存储区域看作是Fortran语言格式的连续区域,并且它不拥有数据存储区域。 ndarray_T

  5. 修改数组的内容时,会直接修改数据存储区域。所有使用该数据存储区域的数组都将被同时修改! ndarray_share

1.1 dtype

  1. numpy 有自己的浮点数类型: float16/float32/float64/float128等等。

    • 在需要指定dtype参数时,你可以使用numpy.float16,也可以传递一个表示数值类型的字符串。numpy中的每个数值类型都有几种字符串表示。字符串和类型之间的对应关系都存储在numpy.typeDict字典中。 typeDict
    • dtype是一种对象,它不同于数值类型。只有dtype.type才能获取对应的数值类型 dtype_type
    • 你可以通过np的数值类型np.float32来创建数值对象。但要注意:numpy的数值对象的运算速度比python内置类型的运算速度要慢很多。所以应当尽量避免使用numpy的数值对象 np_float32
  2. 使用ndarray.astype()方法可以对数组元素类型进行转换。 astype

1.2 shape

  1. 你可以使用ndarray.reshape()方法调整数组的维度。

    • 你可以在某个维度设置其长度为 -1,此时该维度的长度会被自动计算 reshape
  2. 你可以直接修改ndarryshape属性,此时直接修改原始数组。

    • 你可以在某个维度设置其长度为 -1,此时该维度的长度会被自动计算 shape

1.3 view

  1. 我们可以通过ndarray.view()方法,从同一块数据区创建不同的dtype数组。即使用不同的数值类型查看同一段内存中的二进制数据。它们使用的是同一块内存。 view
  2. 如果我们直接修改原始数组的dtype,则同样达到这样的效果,此时直接修改原始数组。 dtype_modified

1.4 strides

  1. 我们可以直接修改ndarray对象的strides属性。此时修改的是原始数组。 strides
  2. 你可以使用np.lib.stride_tricks.as_stride()函数创建一个不同strides的视图。 as_strides 注意:使用as_stride时并不会执行内存越界检查,因此shapestride设置不当可能会发生意想不到的错误。

1.5 拷贝和视图

  1. 当处理ndarray时,它的数据存储区有时被拷贝,但有时并不被拷贝。有三种情况。

    • 完全不拷贝:简单的赋值操作并不拷贝ndarray的任何数据,这种情况下是新的变量引用ndarray对象(类似于列表的简单赋值)

    • 视图和浅拷贝:不同的ndarray可能共享相同的数据存储区。如ndarray.view()方法创建一个新的ndarray但是与旧ndarray共享相同的数据存储区。新创建的那个数组称作视图数组。

      • 对于视图数组,ndarray.base返回的是拥有数据存储区的那个底层ndarray。而非视图数组的ndarray.base返回None
      • ndarray.flags.owndata返回数组是否拥有基础数据
      • 对于数组的分片操作返回的是一个ndarray的视图。对数组的索引返回的不是视图,而是含有基础数据。
    • 深拷贝:ndarray.copy()操作会返回一个完全的拷贝,不仅拷贝ndarray也拷贝数据存储区。 copy_view