numpy(numeric python)可以说是python最最基础的第三方库了,许多其它的库如scipy、pandas、sklearn都是搭建在它基础上的,这个库简单讲就是做数组(矩阵)运算用的,由于底层是用C语言和fortran实现的,所以性能非常强悍…写这篇文章也是视图回顾numpy的相关操作,并整理处numpy中一些最核心的知识点以便未来可以速查。

ndarray类型的属性

ndarray类是numpy中最核心的类型,简单讲他就是n纬数组,但是和python内置的列表嵌套结构不同,ndarray存储的元素要求数据类型都是一致的,并且数组大小也是固定的。常用的ndarray对象的属性如下:

  • T:对ndarray对象做转置操作得到的数组。
  • dtype:ndarry对象中存储的元素类型,python是弱语言,按理讲不需要声明数据类型,但是numpy底层是基于c和fortran实现的,所以需要声明。
  • flat:ndarray对对应的一维度数组的迭代器。
  • itemsize:ndarray中每个元素所占用的字节数。
  • size:ndarray中的元素数量。
  • nbytes:nbytes=sizeitemsize。
  • ndim:ndarray对象的维数。
  • shape:ndarray对象的形状。
  • imag:ndarray对象的虚部。
  • real:ndarray对象的实部。

ndarray对象在运算过程中的广播机制

广播机制是为我们节省代码量的强力工具,但如果不理解广播机制何时触发或者是如何进行广播,那么这个机制反而会成为我们使用numpy的阻碍,所以理解广播机制非常重要,简单讲,广播机制就是基于如下四条原则:

  1. 如果两个数组的ndim不同,则在ndim较小的数组的shape前加上一个1。
  2. (触发时机)当且仅当两个数组对应维度的shape相等或其中一者为1,广播机制才会被触发。
  3. (触发效果)输出数组的各个维度的shape取对应的最大值。
  4. (触发效果)对于shape为1的维,会在这个维上进行复制直到与最大值对齐。
广播机制的例子
a = np.array([1,2]) b = np.array([[1,2],[3,4]]) c = a+b # output(ndarray([[2,4],[4,6]]))

a的ndim是1,b的ndim是2,所根据原则1,首先讲a的shape前加上1,a.shape变为(1,2)。根据原则2检查a,b是否可以广播,a.shape=(1,2),b.shape=(2,2)所以可以进行广播。由原则3直到输出数组的shape应该是(max{1,2},max(2,2))=(2,2)。根据原则4知道a会在第一个维度上进行复制对齐,从而a被广播为ndarray([[1,2],[1,2]])。

numpy中各种函数涉及的axis到底是什么

在numpy中很多函数都有axis(坐标轴)这个参数,axis参数它实际上就是shape的索引。对于一个shape为(2,3)的数组,它在axis=0这个坐标轴就有2个元素,在axis=1这个坐标轴就有3个元素,反过来可以知道有两个元素的方向是axis=0的方向,有3个元素的方向是axis=1的方向。对于包含axis参数的函数,这个函数的运算就是按照指定坐标轴方向对维度进行相应削减或扩充。

含axis参数的函数的例子
a = np.array([[1,2,3],[4,5,6]]) # a.shape=(2,3) print(np.sum(a,axis=0)) # [5,7,9] print(np.sum(a,axis=1)) # [6,15]

当shape中各维对应shape相同,就没法通过上法判断axis了,怎么办?

随便举一个ndim相同但是各维对应shape不同的数组,然后再根据这个数组判断各个axis的指向。

numpy中的视图和副本

视图可以理解为ndarray对象的浅拷贝,而副本可以理解为ndarray对象的深拷贝。可以使用numpy的share_memory函数判断两个数组是视图关系还是副本关系。把副本错误当成视图问题不大,但是把视图错误当成副本危害很大,所以推荐在需要对数组做修改时检查它是不是某个数组的副本。

常见的返回视图和副本的操作

  1. 返回视图:切片、转置、reshape方法、ravel方法、view方法。
  2. 返回副本:花式索引、copy方法、对元素强制类型转换astype方法、flatten方法、数组运算结果、数组拼接concatenate。

numpy中与数组操作相关的重要函数和方法

  • 转换数组的shape:reshape中的参数shape中如果有-1,则对应维元素数量将会自动匹配。reshape和resize的区别在于——(1)前者是视图,后者是副本(2)如果元素数量不匹配reshape无法完成转换,会报错,而resize会进行强制转换,删除元素或复制元素。
    • np.reshape
    • ndarray.reshape
    • np.resize
  • 把数组追加到另一个数组中:它的行为类似与list的extend方法,最关键的是axis参数,如果不指定axis参数,会先将输入的两个数组都做扁平化处理,如果指定了axis参数则会在axis对应方向上对维度进行扩充,注意这时输入的两个数组的ndim必须是一样的,不然会报错。
    • np.append
  • 数组真假值的判断函数:最关键的还是axis参数,如果不指定axis参数则对整个数组进行运算,如果指定axis参数则在axis对应方向上对维度进行缩减。
    • np.all
    • ndarray.all
    • np.any
    • ndarray.any
  • 指定条件下元素索引的获取or三元运算:只给np.where提供逻辑数组时,返回的就是逻辑数组结果为True的元素对应位置的索引,如果提供x、y参数(会通过广播机制和提供的逻辑数组具有相同的shape),则可以实现python中三元运算的效果,将逻辑数组中为True的元素切换为x中对应元素,将为False的元素切换为y中对应的元素。
    • np.where
  • 获取数组中非零元素的索引:等价于np.where(数组!=0)
    • np.nonzero
    • ndarray.nonzero
  • 寻找数组中的最大/最小元素:最关键的是axis参数,如果不指定axis参数,默认是找整个数组中的最大/最小元素,如果指定了axis就按照axis的方向对维度进行缩减。
    • np.amax
    • ndarrray.max
    • np.amin
    • ndarray.min
  • 寻找数组中最大/最小元素的索引:这个操作实际上可以用np.amax/np.amin和np.where函数组合使用来实现。下面提供的这几个函数和方法最关键的参数还是axis,和append一样,如果不指定axis,会先对数组做扁平化操作。如果指定了axis则对对应方向的维度进行缩减。
    • np.argmax
    • ndarray.argmax
    • np.argmin
    • ndarray.argmin
  • 转置操作:np.transpose函数和ndarray.transpose方法都有axes参数,用于指定如何转置,默认是将轴的顺序颠倒。例如指定axes=(1,0,2)就是说将axis=0和axis=1的轴进行颠倒。而ndarray对象的T方法则无法使用axes参数,得到的就是将坐标轴顺序颠倒后的新视图。例如a.shape=(2,3,4),那么a.T.shape=(4,3,2)。
    • np.transpose
    • ndarray.transpose
    • ndarray.T:这是一个属性,感觉像是pytho类里面用property修饰器修饰的一个方法?可惜numpy的源码是用C语言写的,我没看懂…
  • 排序操作:核心参数时axis,默认是-1,表示沿着最后一个axis指定的方向对数组进行排序操作,也可以设置为None,这个时候会对数组先做扁平化处理,然后再排序。与np.sort函数返回的副本不同,ndarray.sort方法是直接对ndarray对象进行就地操作,没有返回值。
    • np.sort
    • np.argsort:这个函数拿到的是排序用的索引。
    • ndarray.sort
  • 数组拼接操作:这个函数的关键参数是tup,它是待拼接数组构成的元组,里面的每一个ndarray必须拥有相同的ndim,并且除了再指定方向上允许元素数量不同,其它axis指定的方向上元素数量必须相同。
    • np.vstack:沿着axis=0指定的方向进行拼接操作。
    • np.hstack:沿着axis=1指定的方向上进行拼接操作。
  • 生成单位矩阵/对角线上全是1的矩阵:
    • np.identity:只有一个参数n,用于指定单位矩阵的阶数。
    • np.eye:参数M指定行数,N指定列数,当时,主对角线就是阶单位矩阵的对角线,参数k用于指定哪个对角线上全是1,默认时0,表示主对角线上全是1,如果为正值表示向上偏移k的对角线上全是1,如果时负值表示向下偏移k的对角线上全是1。
  • 生成全0矩阵:
    • np.zeros
  • 生成全1矩阵:
    • np.ones
  • 生成待初始化的矩阵:
    • np.empty:这个矩阵的创建速度是最快的。
  • 生成等差数组:
    • np.arange:和range的行为一致,不过更为强大,它的step(步长/公差)可以不是整数。
    • np.linspace:公差由起点、终点和元素数量决定的等差数组。当endpoint=true时,公差=,否则公差=
  • 生成随机数组:这类数组的生成用的是numpy的random子模块。
    • 设置随机数种子
      • np.random.seed:计算机产生的随机数是伪随机数,再测试程序的时候设置一个固定的种子很有帮助。
    • 均匀分布随机数
      • np.random.rand:用[0,1)区间均匀分布的随机数填充并返回指定形状的数组。
      • np.random.randint:用区间[low,high)中离散均匀分布的随机整数填充并返回指定形状的数组。
  • 从数组中随机抽取元素
    • np.random.choice:关键参数——(1)p可以用来设置数组中每个元素被选中的概率,默认等概率(2)replace用于设置是否是有放回抽样,如果是,这个函数实际上就是遗传算法里面的轮盘赌。(3)size用于设置抽取到的元素构成的数组的最终输出时的形状。
  • 随机打乱数组
    • np.random.shuffle:注意这个函数主要操作的是一维数组,如果输入多维数组,则将第一个axis指定方向的顺序打乱。
  • 数组扁平化操作:flatten方法和ravel方法的区别在于前者是元素组的副本,后者是原数组的副本。
    • np.ndarray.flatten
    • np.ndarray.ravel
  • 数组平铺操作:最关键的是reps参数,用于指定数组如何平铺,如果输入一个整数,就是在数组最后一个axis指定方向平铺对应次数。如果是元组,就依次在对应axis指定方向平铺对应次数。
    • np.tile
  • 数组读写操作:需要注意的是——(1)np.load,np.save,np.savez使用了python的pickle模块的反序列化和序列化实现数组对象的读取与写入,所以存在一定的安全风险,对于不可靠的npy,npz文件不要用这个函数读取;(2)loadtxt和savetxt相对而言安全性更好,而且可以跨编程语言操作这些数组,但是这连个函数不支持保存或读取三维及以上的数组。
    • np.load:从npy后缀或npz后缀的文件中读取ndarray数组
    • np.save:将一个数组保存到npy后缀的文件中,这个文件类型只有python能读取。
    • np.savez:将多个数组分别保存到多个npy文件中,然后将这些npy文件压缩到一个npz后缀的文件中。
    • np.loadtxt:将ndarray从文本文件中加载出来。
    • np.savetxt:将ndarray保存得到文本文件中。

numpy中提供的重要数学函数

  • 求数组的差分、累计和
    • np.diff:最关键的参数是axis,默认在最后一个axis指定方向做差分。
    • np.cumsum:最关键的参数是axis,默认先将数组展平后再做累计和(它是差分的逆运算,但是差分后的数组并不能由这个函数还原,因为不知道初始值)。
  • 向量内积和矩阵乘法:
    • np.dot:这个函数最关键的就是两个参数a,b。当a和b都是0维是做的是标量乘法;当a,b都是一维数组时算的就是两个向量的内积;当a,b都是多维数组时,算的时矩阵乘法;当a是多维数组而b是一维数组时,此时b相当于列向量,做矩阵乘法;当a是一维数组而b是多维数组时,此时a相当于行向量,做矩阵乘法。使用这个函数的时候一定要注意输入数组的ndim和输出结果的ndim,否则会有意料之外的错误。
  • 生成网格点坐标对应的矩阵X,Y,…
    • np.meshgrid:这个函数的输入时一维数组,返回结果时一个ndarray元组,放的时坐标在各个维度对应的矩阵。
  • 求范数
    • np.linalg.norm

关于后续

目前numpy里面我觉得最核心的知识点和函数就是这些,其它的都不怎么用,等用到了再回来补充吧!