numPy的一些知识点

Posted by kevin on February 13, 2020

preface

numpy 早就用过了,但是长时间不用的话对其中的一些知识点又会忘记,又要去网上翻看各种博客,干脆自己把常用的一些东西记下来好了,以后忘了的话直接看自己写的笔记就行了

numpy 基础

numpy 是 python 的矩阵运算库,底层由 C++ 编写,因此速度相比 python 自身快得多,经常用于数据科学领域中,语法和 Matlab 有些相似。

numpy(下面简称 np)的基本类型是 ndarray(n dimensions array),又用 np.array 称呼它,它有很多属性:np.ndim 表示数组的维度,np.size 表示数组中元素的个数,np.shape 表示数组各个维度的大小,例如一个三行四列矩阵的 shape 就是(3, 4),np.dtype 表示数组的数据类型,np 里面有很多的数据类型,如 np.int32,np.int16,np.float64,np.complex 等等

创建 array

可以用特定的数据来创建一个 array 矩阵,只需要在 np.array() 的括号中传入一个列表作为参数就行了,多维的 array 传入多重列表就行,并且还可以顺便加上 dtype 参数指定 array 的数据类型

不过这样子创造 array 太累了,如果我只是想捏造一些数据来做实验而已的话完全可以用 np 内置的函数来快速生成一个 array。np.zeros((size), dtype) 生成零矩阵,np.ones((size), dtype) 生成单位阵,np.empty((size), dtype) 生成的矩阵的值是随机初始化的,和内存的状态有关,默认情况下,如果不显示指明 dtype,用这些函数生成的 array 都是 np.float64 类型的

还有一种方法是用 np.arange(begin, end, step) 通过一个数字序列来生成 array ,很像 python 中创建列表的 range 方法,同样也是 “包头不包尾” ,还有np.linspace(begin, end, count) 用来创建从 begin 到 end-1 范围内,拥有 count 个元素的 array。

当然也还有创建随机矩阵,这个是挺常用的,一般用 np.random.random() 来创建,里面接受一个 tuple 型的参数,代表 array 各个维度的大小,还有一种 np.random.randn() 也是常用的,里面接受任意个参数来代表各个维度的大小,如果是三维的 array 就传入三个参数,用 randn 方法生成的数据是基于标准差为 1 ,u = 0 的正态分布数据。

基本运算

四则运算中,加法和减法在 np 中还是通用的,因为 np 主要操作对象是矩阵,所以乘法除法另说,* 在 np 中指的是对每一个元素进行的乘法(elementwise),矩阵相乘在 np 中用 @ 或者 np.dot 来操作,没有除法,只有用 np.linalg.inv 对矩阵进行求逆矩阵操作

除此之外,np 也可以对 array 的每一列每一行都进行操作,比如求每一行或每一列的最大最小值,ndarray 对象提供了 sum,min,max(axis=0/1) 等统计方法,axis = 0 时对象为每一列,axis = 1 的对象为每一行

以及还有很多通用函数,如 np.sqrt,np.sin,np.exp 等等等等说不完,只要看到函数名字应该就能够知道他们是干什么的了,使用时看看手册便 OK

slice & index & iteration

np 的切片和索引和 python 是差不多的,甚至有些地方比 python 的还要更加高级一些,np 的多维矩阵的每个维度都可以运用切片,不同维度之间用逗号隔开,... 代表其他维度都按默认全部切片

x[1,2,…] is equivalent to x[1,2,:,:,:], x[…,3] to x[:,:,:,:,3] and x[4,…,5,:] to x[4,:,:,5,:]

迭代默认是按照第一个维度进行迭代,也就是一行一行输出,如果要想将 array 中的元素全部输出用 for element in array.flat 进行迭代。

np 的高级索引,不像 python 只能用字符和数字做索引,np 还可以用整形矩阵和布尔型矩阵来进行索引,虽然平时可能用的并不多。还可以用多维矩阵做索引,有点类似函数的感觉,看个例子就知道了

改变 shape

前面就已经给出了几个改变 array 的 shape 的方法了,在 np 中有 ndarray.reshape,ndarray.T,ndarray.resize,ndarray.ravel 等几个常用的进行变形的方法,其中 reshape 和 resize 的效果是一样的,不过 reshape 会返回一个新的 array ,而 resize 是在原来的 array 上就地修改,并且,为了方便,reshape 可以在确定了其中一个维度的大小后将另一个维度用 -1 表示,让计算机自己去计算,但是用 resize 的话一定要将所有的维度都正确填写,不能偷懒。ravel 是将 array 平摊成一行展开变成一个一行的矩阵

堆叠和拆分

这部分用得比较少吧?但是还是记一下,堆叠也就是将两个矩阵变成一个矩阵,有点类似增广矩阵的意思,拆分就是把一个矩阵拆成好多个小矩阵,np 中用 stack 和 split 关键字来处理。

堆叠有水平堆叠 np.hstack 和垂直堆叠 np.vstack,两个函数都接受一个 tuple 参数,tuple 中是要进行合并的两个矩阵,既然要合并的话,两个矩阵在合并方向上的维度大小一定要一致才行:例如用 vstack 进行垂直合并的话,就得保证合并的两个矩阵的列数要一致

拆分道理差不多,用的比较少就不说了,需要用的时候再去看手册好啦

深浅拷贝

这个概念在很多编程语言里面都有,一旦理解的话可以运用到其他的地方,底层来说的话,浅拷贝相当于拷贝前后的两个变量公用一块内存,改变了其中一个的话,另一个也会跟着改变,深拷贝则是开辟了另一块内存进行拷贝,使拷贝前后二者没有任何关联,仅仅是值相等,改变其中一个的值另一个并不会跟着改变。

np 中有点不同,如果直接将矩阵赋值给另一个矩阵,相当于没有拷贝,只是给矩阵换了个名字而已,因此如果有 a = b,b 改变的同时 a 也会改变。

np 的浅拷贝用 np.view() 来实现,view 只分享数据给另一个矩阵,b = a.view(),此时 b 就是 a 浅拷贝后的矩阵,a 改变 shape 后 b 的 shape 不改变,但是 a 改变数据的值后 b 的值也会改变。(值得注意的是 np 的切片返回的是原矩阵的浅拷贝,见下图中的例子)

深拷贝用 np.copy() 实现,深拷贝并不会和原矩阵共享任何东西,原矩阵有任何变化都不会影响深拷贝得到的矩阵

线性代数

np 是为矩阵操作而设计的,所以里面也有很多的线性代数方面的接口,比如求逆 np.linalg.inv,求转置 np.transpose,创建单位阵 np.eye ,求矩阵的迹 np.trace,其实跟 matlab 里面的函数是差不多的,所以还是比较好掌握的,主要是理解线性代数中矩阵各种性质的原理。