Numpy学习笔记
python是好文明!——虽然图书馆总是不肯买python数据分析的书,一大把matlab教程锃光瓦亮的摆在那里。
问题总是层出不穷的,要学会搜索,Numpy 手册搜索页面传送门 。
数据结构:array 与 matrix
Numpy 中有两种数组:array
和 matrix
,后者是前者的子类,继承了大部分操作。在用户手册上建议大家使用 array ,不过 matrix 在线性代数方面更为方便。
在这里对二者进行一个简单(包罗万象)的对比:
运算操作
array | matrix | |
---|---|---|
维度 | 1~,根据括号层数,可能下降 ① | 2 |
矩阵乘法 | np.matmul(A,B) 或A.dot(B) A @ B ② |
A*B |
按位乘法 | A*B ,遵循广播法则 ③ |
np.multiply(A,B) |
矩阵乘方 | np.linalg.matrix_power(A,k) |
A**k |
按位乘方 | A**B ,B 不能包含负整数 ④ |
np.power(A,B) |
其他按位运算 | 遵循广播法则 | 遵循广播法则 |
逆矩阵 | np.linalg.inv(A) |
A**-1 或A.I |
- 例如对于一个二维 array ,
mean(A,1)
的结果是一维的 对于多个矩阵连乘,优雅的写法是PEP465更新,将np.linalg.multi_dot([A,B,C])
,我感觉写成A.dot(B).dot(C)
也挺明白的@
作为矩阵相乘的符号- 广播法则:简而言之,就是把两个数组中低维、较小的升维(加入长度为1的维度)、重复,使其形状与另一个数组匹配。如果可以做到,就能成功进行运算。
- 可以使用
A**-2.0
或者(1/A)**2
这种 hack 手段
创建特殊数组
array 和 matrix 分别使用两套创建方法,matrix 所用的函数位于 np.matlib
中。要注意两点:
- matrix 的方法中,无论怎么给参数,输出都是二维的(要么报错),array 系列则不然。
- array 系列的
empty,zeros,ones,full
均可改用empty_like(A)
之类的函数来创建与已知矩阵 A 同形态的矩阵。 np.random.rand()
的参数与众不同。
以下假设from numpy import *
array | matrix | |
---|---|---|
[NxN] 单位矩阵 |
eye(N) |
matlib.eye(N) |
[MxN] 空矩阵 |
empty([M,N]) |
matlib.empty([M,N]) |
[MxN] 零矩阵 |
zeros([M,N]) |
matlib.zeros([M,N]) |
[MxN] 全1矩阵 |
ones([M,N]) |
matlib.ones([M,N]) |
[MxN] 全a矩阵 |
full([M,N],a) |
无 |
[MxN] 随机矩阵 |
random.rand(M,N) |
matlib.rand(M,N) 或matlib.rand([M,N]) |
[MxN] 正态随机矩阵 |
random.randn(M,N) |
matlib.randn(M,N) 或matlib.randn([M,N]) |
[MxN] 随机整数矩阵 |
random.rand(low,hight,[M,N]) |
无 |
结论
鉴于 array 的泛用性以及 matrix 按位运算的坑爹性,我打算用 array了。 既然已经可以用 A.dot(B)
的写法虽不舒服,把.dot()
当作一个算符还能接受,而np.multiply(A,B)
的写法实在是太不数学了。A @ B
表示矩阵相乘,还有什么理由不用 array 呢?
因为 array 是支持各种维度数据的,和数学上常用的矩阵并不一样,例如:[N]
个数据的向量和[1xN]
的矩阵并不是一回事,对向量进行转置操作没有效果。而对于维度不等的数组,在运算时会用到广播法则,能得到想要的结果——但是总是觉得不放心。为了符合数学习惯,在声明变量的时候可以把向量都写成 A=np.array([[a,b,c]])
的形式,这样就不必再考虑相乘的时候用什么函数(.dot()? .inner()? .outer()?
(/TДT)/),只要按数学表达式转置相乘即可。不过要注意,两个向量用矩阵乘法或类似乘法的函数,返回值是标量;两个矩阵用矩阵乘法,返回值是[1x1]
的矩阵。
各种乘法函数
如上所述,其实各种乘法函数都可以用“转置+矩阵乘法”代替,不过还是在这里列一下,假设m,n
是两个[1xN]
矩阵:
函数 | 等价于 | 注释 |
---|---|---|
np.matmul(A,B)->ndarray |
A @ B |
矩阵乘法,参数不能是标量 |
A.dot(B),np.dot(A,B)->ndarray |
A @ B |
参数中的一个可以是标量 |
np.inner(m,n)->num |
m @ n.T |
可用于高维,但数学意义不明 |
np.outer(m,n)->ndarray |
m.T @ n |
|
np.vdot(m,n)->num |
m.conjugate() @ n.T |
复共轭内积,不应用于高维 |
矩阵的连接
r_[] 与 c_[]
两个很方便的语法糖,但是具体用法……很那理解,可以说是只可意会不可言传了。
“叠加”方式
r_[]
的作用是“连接”,可以带一个“拟似参数”指定连接的轴,例如:
1
2
3
4
5
6
71,2,3],[4,5,6]] # shape(3,),shape(3,) r_[[
array([1,2,3,4,5,6]) # shape(6,)
'0',[[1,2,3]],[[4,5,6]]] # shape(1,3),shape(1,3) r_[
array([[1,2,3],
[4,5,6]]) # shape(2,3)
'1',[[1,2,3]],[[4,5,6]]] # shape(1,3),shape(1,3) r_[
array([[1,2,3,4,5,6]]) # shape(1,6)
从shape
值的角度看,玄乎的“连接”就对应着 shape
的变化,记shape
为向量\(S\),则沿 0 轴连接,就是 \(S[0]=s_a[0]+s_b[0]\),同时其他分量不变 \(S[i]=s_a[i]=s_b[i]\)
- 这样引出第二个“拟似参数”:最小维度。例如(数不够,字母凑,就当
a,b,c
是普通数字):
1
2
3
4
5
6
7
8
9
10'0,2',[1,2,3],[4,5,6]] # shape(3,),shape(3,) r_[
array([[1,2,3],
[4,5,6]]) # shape(2,3)
'1,3', r_[
1,2,3],[4,5,6]], [[
7,8,9],[a,b,c]]] # shape(2,3),shape(2,3) [[
array([[[1,2,3],
[4,5,6],
[7,8,9],
[a,b,c]]]) # shape(1,4,3)
维度,也就是shape
值的分量数。当前维度比要求的最小维度低,就要升维,对应着shape
增加分量。默认的升维方式是在高位加一维,其长度为 1,即 \(S_a=(1,s_a)\)。之后再进行连接,显然第二个例子的历程是这样的: \[ \begin{matrix}s_a=(2,3)\\ s_b=(2,3)\end{matrix}\xrightarrow{升维}\begin{matrix}S_a=(1,2,3)\\ S_b=(1,2,3)\end{matrix}\xrightarrow{连接} S=(1,2+2,3)=(1,4,3)\]
- 那么升维只有这一种方法吗?No!请看第三个“拟似参数”:原
shape
在新shape
中的坐标,这个最难理解。然而我和其他博主不一样,我会画图。简单的假设原来\(s_a=(h,k,l)\),用“1”填充升维之后\(S_a\)为
1
2
3( 1 1 . . . 1 h k l 1 . . . 1 1 )
^ ^
坐标 m 坐标 -n
我们很自然的采用 python 中列表索引的方式来表示这个坐标。这个坐标的意义是:确定好了原来shape
的位置之后,其他的1~m-1
和-n+1~-1
的位置会被“1”填充。第三个“拟似参数”若为非负数,则表示 m
;若为负数,则表示-n
。下面是一个例子: 1
2
3
4
5
6'2,5,1', r_[
1,2],[3,4],[5,6]], [[
7,8],[9,a],[b,c]]] # shape(3,2),shape(3,2) [[
array([[[[[1]],[[2]],[[7]],[[8]]],
[[[3]],[[4]],[[9]],[[a]]],
[[[5]],[[6]],[[b]],[[c]]]]]) # shape(1,3,4,1,1)
所以说,其实默认的
r_[A,B]
就是r_['0,1,-1',A,B]
罢了而
c_[A,B]
就是r_['-1,2,0',A,B]
,也就是说:沿最后一轴连接,维度最小为2,倒序升维。
骚操作切片
r_[]
和c_[]
之所以不设计成函数,就是为了方便切片,其中用于连接的除了普通数组外,也可以是以下表达式:
起始:终止:步长
的形式,结果就像np.arange()
一样,包含起始
但不包含终止
,python 一贯风格。- 如果步长是个虚数,即
起始:终止:步长j
的形式,那么步长
表示的是总的点数,结果如同np.linspace()
,然而这时候就是包含终止
的了。
那么可以这样快速产生等距矩阵:
1 | x=r_['0,2',0:10:100j] |
一般用途
说了那么多玄的,其实r_[]
和c_[]
一般还是用在二维的矩阵上,这时候就很简单了:
r_[A,B]
:保持列数不变,按行连接c_[A,B]
:保持行数不变,按列连接
1 | 1,2,3],[4,5,6]]) A=array([[ |
而作用在一维向量上,二者效果就没那么直观,不如先转换成矩阵再来操作。