NumPy是python的第三方科学计算包,全名称为Numerical Python extensions。NumPy包含以下几个功能组件:强大的N维数组对象(可以操控多为数组),优美巧妙的功能(广播)函数,对于线性代数,傅里叶变换,随机数的生成有着很好的支持。现在广泛用于机器学习与深度学习之中。
基础类型
NumPy中最基本的对象就是均匀多维数组,包含一系列相同类型的变量(一般数字),类似于数组,可以通过正整数来进行访问。注意,在Numpy中维数(dimensions)称之为axes。axes的数量就是rank(秩)。
举个例子,在一个三维空间中的一个点的坐标为[1, 3, 2]
,这是一个秩为1的数组,因为它只含有一个轴(axis),轴的长度为3。
接下来的这个例子的rank为2,因为它有两维。
(注意这里的维数于向量中的维数不同,这里的维数相当于C语言中数组的维数,又称轴数。另外这里的rank不等同于矩阵中的秩,在python的语言中,维数的数量称之为rank)
[[1, 0, 2],
[0, 2, 1]]
第一维(轴)的数量为2([1, 0, 2],[0 ,2, 1]),第二维的数量为3(如第一个数组中的1,0,2)。
再来区别一下python中rank与矩阵中rank(秩)的区别,numpy中多维数组其实就是矩阵,拥有和矩阵一样的性质。以下讲解中数组一般为多维数组。
>>> a =np.array([[1,2,3],[2,3,4],[3,4,5]]) #这里定义了一个三行三列的多维数组(可以理解为矩阵)
>>> a
array([[1, 2, 3],
[2, 3, 4],
[3, 4, 5]])
>>> a.ndim #在numpy中ndarry.ndim返回这个数组的rank,要注意,这里的rank是2,但是在矩阵中rank应该为3
2
注意NumPy中的数组类称之为ndarray
,一般我们叫它的别名array
,其这个与python标准库中的array.array
不同,python中的只可以处理一维数组,功能上差很多。
基本定义方法
>>> import numpy as np
>>> a = np.array([1,2,3]) #定义一个一维数组
>>> a.size #输出a的容量
3
>>> a.shape #a的形状,对于单个一维数组来说 维数就是元素长度
(3,)
>>> b = np.array([[1,2,3],[2,3,4],[3,4,5]]) #定义一个二维数组
>>> b.size #b的容量为元素的数量
9
>>> b.shape #b的形状,这里b是一个拥有三个数组,每个数组有三个元素
(3, 3)
>>> b.shape[0] #b中第一维的数量
3
>>> b.shape[1] #b重第二维的数量(即元素长度)
3
>>> c = np.arange(15).reshape(3, 5) #定义一个范围从数量为15,第一维为3,第二维为5的数组
>>> c
array([[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14]])
>>> c.shape #同上
(3, 5)
>>> c.ndim #数组的秩
2
>>> c.dtype.name #数组中元素的类型
'int64'
>>> c.itemsize #数组中元素字节数(大小) 这里的8为(64/8)
8
>>> c.size #C中元素数量
15
>>> type(c) #类型
<type 'numpy.ndarray'>
>>> d = np.array([6, 7, 8])
>>> d
array([6, 7, 8])
>>> type(d)
<type 'numpy.ndarray'>
在平常的使用中,我们通常使用np做numpy的别名,这样可以使程序更加简洁便于阅读。
下面例子也中展示了array的基本构造,注意,这样初始化是错误的!
>>> a = np.array(1,2,3) # 错的!
>>> a = np.array([1,2,3]) # 应该这样
另外,还可以这样构造
>>> a = np.array([[1,2],[3,4]], dtype=complex) #在后面通过函数参数指定类型
>>> a
array([[ 1.+0.j, 2.+0.j],
[ 3.+0.j, 4.+0.j]])
>>> b = np.zeros((3,3)) #构造全为0,(3,3)的数组
>>> b
array([[ 0., 0., 0.],
[ 0., 0., 0.],
[ 0., 0., 0.]])
>>> d = np.zeros((2,3,4), dtype=np.int16) #指定构造维数和元素类型
>>> d
array([[[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]],
[[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]]], dtype=int16)
>>> a = np.ones((3,4)) #单位数组
>>> a
array([[ 1., 1., 1., 1.],
[ 1., 1., 1., 1.],
[ 1., 1., 1., 1.]])
>>> e = np.arange(1, 10 ,2) #定义范围为1-10,间隔为2的数组
>>> e
array([1, 3, 5, 7, 9])
>>> f = np.arange(0, 2, 0.1) #也支持浮点型
>>> f
array([ 0. , 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1. ,
1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9])
>>> g = np.linspace(0,10,11) #定义0-10中,均匀取11个点的数组
>>> g
array([ 0., 1., 2., 3., 4., 5., 6., 7., 8., 9., 10.])
基本操作
算术算法操作同样适合数组,numpy可以方便地在数组中进行操作。
>>> import numpy as np
>>> a = np.array([1,2,3])
>>> b = np.arange(3)
>>> b
array([0, 1, 2])
>>> c = a - b #对应元素相减
>>> c
array([1, 1, 1])
>>> b**2 #b中所有元素平方
array([0, 1, 4], dtype=int32)
>>> 10*np.sin(a) #a中所有元素通过sin函数乘于10
array([ 8.41470985, 9.09297427, 1.41120008])
>>> a < 2 #判断a中的元素,返回bool类型
array([ True, False, False], dtype=bool)
注意,numpy中的乘法符号*对数组中的每个元素也是同样起作用的,这个与矩阵中的乘法不同(dot)
>>> A = np.array( [[1,1],
... [0,1]] )
>>> B = np.array( [[2,0],
... [3,4]] )
>>> A*B # 对应元素乘积
array([[2, 0],
[0, 4]])
>>> A.dot(B) # 矩阵乘法
array([[5, 4],
[3, 4]])
>>> np.dot(A, B) # 矩阵乘法
array([[5, 4],
[3, 4]])
numpy中也支持+=
和-+
运算,在原先存在的array中进行操作(不是新创建一个数组)。
>>> import numpy as np
>>> a = np.array([[1,2,3],[2,3,4]])
>>> a
array([[1, 2, 3],
[2, 3, 4]])
>>> b = np.ones((2,3)) #默认类型为浮点型float64
>>> b
array([[ 1., 1., 1.],
[ 1., 1., 1.]])
>>> b += a #这里整型int32被转化为float64
>>> b
array([[ 2., 3., 4.],
[ 3., 4., 5.]])
>>> a += b
Traceback (most recent call last):
File "<input>", line 1, in <module>
TypeError: Cannot cast ufunc add output from dtype('float64') to dtype('int32') with casting rule 'same_kind'
上面的例子a+=b
出现错误,这是因为numpy在操作不同类型的数据时,会朝着更精确的数据类型进行转化(类似于C语言中的向上转型)。
>>> from numpy import pi #pi即PI,圆周率
>>> a = np.ones(3, dtype=np.int32)
>>> b = np.linspace(0,pi,3)
>>> b.dtype.name
'float64'
>>> c = a+b
>>> c
array([ 1. , 2.57079633, 4.14159265]) #转型
>>> c.dtype.name
'float64'
>>> d = np.exp(c*1j)
>>> d
array([ 0.54030231+0.84147098j, -0.84147098+0.54030231j,
-0.54030231-0.84147098j])
>>> d.dtype.name
'complex128' #复数型
numpy中可以使用很多“非数组”操作符来对数组进行操作,如求和sum
,取平均mean
。也可以通过设定axis
参数来对特定的轴进行操作。
>>> a = np.array([[1,2,3],[2,3,4]])
>>> a
array([[1, 2, 3],
[2, 3, 4]])
>>> a.sum() #求和
15
>>> a.min() #求最小
1
>>> a.max() #求最大
4
>>> a.sum(axis=0) #求每列的和
array([3, 5, 7])
>>> a.sum(axis=1) #求每行的和
array([6, 9])
>>> a.min(axis=1) #求每行的最小值
array([1, 2])
>>> a.cumsum(axis=1) #求每行的累积和
array([[1, 3, 6],
[2, 5, 9]], dtype=int32)
通用操作
numpy中也有我们常见的数学函数sin
、exp
等,但在numpy中这些函数是对数组中所有元素进行全局操作,与以往的函数不同,这里输入输出都为数组。
>>> a = np.arange(3)
>>> a
array([0, 1, 2])
>>> np.exp(a) #对其中每个元素进行exp操作
array([ 1. , 2.71828183, 7.3890561 ])
>>> np.sqrt(a) #对其中每个元素进行sqrt操作(平方根)
array([ 0. , 1. , 1.41421356])
>>> b = np.array([2., -1., 4.])
>>> np.add(a, b) #两个数组相加
array([ 2., 0., 6.])
numpy中的一维数组也可以进行索引、切片和迭代,这个和python中的lists有着相似的功能。
>>> a = np.arange(10)**3 #数组中0-9每个元素对其3次方
>>> a
array([ 0, 1, 8, 27, 64, 125, 216, 343, 512, 729])
>>> a[2] #取第2+1个元素
8
>>> a[2:5] #取第2+1到第5+1个元素
array([ 8, 27, 64])
>>> a[:6:2] = -1000 # 与 a[0:6:2] = -1000相同; 从开始到第6+1个位置, 每两个元素设为-1000
>>> a
array([-1000, 1, -1000, 27, -1000, 125, 216, 343, 512, 729])
>>> a[ : :-1] # 翻转 a
array([ 729, 512, 343, 216, 125, -1000, 27, -1000, 1, -1000])
>>> for i in a: #打印a中的元素(每个元素+1打印)
... print(i+1)
730
513
344
217
126
-999
28
-999
2
-999
对于多维的数组,可以对其特定“轴”(axis)进行操作:
import numpy as np
>>> def f(x,y):
... return x+y
>>> a = np.fromfunction(f,(5,4),dtype=int) #通过函数构造二维数组,函数参数为数组的坐标
>>> a
array([[0, 1, 2, 3],
[1, 2, 3, 4],
[2, 3, 4, 5],
[3, 4, 5, 6],
[4, 5, 6, 7]])
>>> a[2,3] #取第2+1行第3+1个元素
5
>>> a[1,:] #取第1+1行的元素
array([1, 2, 3, 4])
>>> a[:,2] #取第2+1列的元素
array([2, 3, 4, 5, 6])
>>> a[0:2,:] #取第1-3行(不包括3)的元素
array([[0, 1, 2, 3],
[1, 2, 3, 4]])
>>> a[-1] #取最后一行的元素
array([4, 5, 6, 7])
numpy中也有一种用法可以在多维数组简洁表示,使用dots(...
)来表示同样数量的:
,比如:
x为秩为5的数组(有5个axis) x[1,2,...]
相当于x[1,2,:,:,:]
x[...,3]
相当于x[:,:,:,:,3]
x[4,...,5,:]
相当于x[4,:,:,5,:]
>>> c = np.array( [[[ 0, 1, 2], # 三维数组(包括两个二维数组)
... [ 10, 12, 13]],
... [[100,101,102],
... [110,112,113]]])
>>> c.shape
(2, 2, 3)
>>> c[1,...] # 与 c[1,:,:] 或 c[1] 相同
array([[100, 101, 102],
[110, 112, 113]])
>>> c[...,2] # 等同于 c[:,:,2]
array([[ 2, 13],
[102, 113]])
在对多维数组进行迭代时,默认对第一维进行迭代:
>>> a = np.array([[0, 1, 2, 3],
[1, 2, 3, 4],
[2, 3, 4, 5],
[3, 4, 5, 6],
[4, 5, 6, 7]])
>>>for row in a:
... print(row)
[0 1 2 3]
[1 2 3 4]
[2 3 4 5]
[3 4 5 6]
[4 5 6 7]
可以使用flat来对数组中所有单个元素进行迭代:
>>>for element in a.flat:
... print(element)
0
1
2
3
1
2
3
4
2
3
4
5
3
4
5
6
4
5
6
7
形状(shape)操作
每个数组对象都有一个对应元素数量的形状
>>> a = np.floor(10*np.random.random((3,4)))
>>> a
array([[ 2., 8., 0., 6.],
[ 4., 5., 1., 1.],
[ 8., 9., 3., 6.]])
>>> a.shape #这里的a的形状为(3,4)也就是三行四列
(3, 4)
数组的形状是可以通过不同的命令进行改变的
>>> a.ravel() #将数组进行平铺
array([ 2., 8., 0., 6., 4., 5., 1., 1., 8., 9., 3., 6.])
>>> a.reshape(6,2) # 返回一个改变形状的数组
array([[ 2., 8.],
[ 0., 6.],
[ 4., 5.],
[ 1., 1.],
[ 8., 9.],
[ 3., 6.]])
>>> a.T # 返回一个a的转置
array([[ 2., 4., 8.],
[ 8., 5., 9.],
[ 0., 1., 3.],
[ 6., 1., 6.]])
>>> a.T.shape #a转置的形状
(4, 3)
>>> a.shape #a的形状
(3, 4)
reshape
命令与resize
命令功能相同,只是resize
命令在自身上面进行改动
>>> a
array([[ 2., 8., 0., 6.],
[ 4., 5., 1., 1.],
[ 8., 9., 3., 6.]])
>>> a.resize((2,6))
>>> a #注意:a发生了变化
array([[ 2., 8., 0., 6., 4., 5.],
[ 1., 1., 8., 9., 3., 6.]])
如果reshape
中某个参数赋予-1时,则这个参数会根据另一个参数进行自动填充
>>> a.reshape(3,-1) #之前a.shape为(2,6),reshape(3,-1)相当于(3,4)
array([[ 2., 8., 0., 6.],
[ 4., 5., 1., 1.],
[ 8., 9., 3., 6.]])
数组合并操作
数组之间可以相互进行合并,通过合并轴(axis)来实现
>>> import numpy as np
>>> a = np.floor(10*np.random.random((2,2))) #产生2×2的数组,元素通过floor函数去掉后面的小数点
>>> a
array([[ 1., 0.],
[ 3., 2.]])
>>> b = np.floor(10*np.random.random((2,2)))
>>> b
array([[ 1., 3.],
[ 1., 8.]])
>>> np.vstack((a,b)) #垂直叠加数组
array([[ 1., 0.],
[ 3., 2.],
[ 1., 3.],
[ 1., 8.]])
>>> np.hstack((a,b)) #水平叠加数组
array([[ 1., 0., 1., 3.],
[ 3., 2., 1., 8.]])
注意,在多于二维数组的操作中,vstack
对数组中的第二轴进行堆叠,而hstack
则对第一轴进行堆叠。
对多维数组进行切片
使用hspit
函数可以对数组在水平轴上进行切割,可以指定切割后有多少相似数组数量,和切割后有多少列来进行切割。
>>> a = np.floor(10*np.random.random((2,12)))
>>> a
array([[ 9., 5., 6., 3., 6., 8., 0., 7., 9., 7., 2., 7.],
[ 1., 4., 9., 2., 2., 1., 0., 6., 2., 2., 4., 0.]])
>>> np.hsplit(a,3) # 切割成3个相似数组
[array([[ 9., 5., 6., 3.],
[ 1., 4., 9., 2.]]), array([[ 6., 8., 0., 7.],
[ 2., 1., 0., 6.]]), array([[ 9., 7., 2., 7.],
[ 2., 2., 4., 0.]])]
>>> np.hsplit(a,(3,4)) # 在第三列和第四列分别开刀
[array([[ 9., 5., 6.],
[ 1., 4., 9.]]), array([[ 3.],
[ 2.]]), array([[ 6., 8., 0., 7., 9., 7., 2., 7.],
[ 2., 1., 0., 6., 2., 2., 4., 0.]])]
同样的vsplit
可以对数组进行垂直切割。
复制和赋值
当在操作数组的时候,赋值和复制操作是不同的,这里需要注意。
>>> a = np.arange(12)
>>> b = a # 创造一个新的对象b,
>>> b is a # a和b指向同一个数组,只是名称不同
True
>>> b.shape = 3,4 # 改变b的形状,a的形状也随之改变
>>> a.shape
(3, 4)
为什么会这样,因为python在传递时传递的是引用,类似于C++中的引用,上面的a和b其实是完全相同的,因为它们指向一个相同的对象,只是操作时的名称不同。如果想复制一个新的对象而不是引用,则需要使用其他操作符。
>>> c = a.view()
>>> c is a
False
>>> c.base is a # c是拥有a里面元素的一个模子
True
>>> c.flags.owndata
False
>>>
>>> c.shape = 2,6 # a的形状并没有改变
>>> a.shape
(3, 4)
>>> c[0,4] = 1234 # a的数据发生了改变
>>> a
array([[ 0, 1, 2, 3],
[1234, 5, 6, 7],
[ 8, 9, 10, 11]])
这里view
只是模子,但其实数据还是公用的,接下来的copy
操作则实现了完全的复制
>>> d = a.copy() # d是一个新的对象,内容是a的复制品
>>> d is a
False
>>> d.base is a # d与a并不共享数据
False
>>> d[0,0] = 9999
>>> a # b改变,a不改变
array([[ 0, 10, 10, 3],
[1234, 10, 10, 7],
[ 8, 10, 10, 11]])
广播
广播是numpy一个非常强大的功能。你可以在一个数组中对其所有元素同时进行相同的操作,也可以同时操作两个及以上相同形状的多维数组,对其中的元素进行操作:
>>> a = np.array([1.0, 2.0, 3.0])
>>> b = np.array([2.0, 2.0, 2.0])
>>> a * b #注意,这里是a,b相对应的每个元素进行了相乘
array([ 2., 4., 6.])
>>> a = np.array([1.0, 2.0, 3.0])
>>> b = 2.0 #同上
>>> a * b
array([ 2., 4., 6.])
关于广播功能还有很多,这里只进行简单的介绍。
更多请看:https://docs.scipy.org/doc/numpy/user/quickstart.html
高级索引功能
numpy中除了通过特定的整数和切片进行访问,还有很多高级的索引访问功能:
>>> a = np.arange(12)**2 # 对前12个元素进行平方
>>> i = np.array( [ 1,1,3,8,5 ] ) # 创建一个“索引”数组
>>> a[i] # 取出相应索引位置的元素
array([ 1, 1, 9, 64, 25])
>>>
>>> j = np.array( [ [ 3, 4], [ 9, 7 ] ] ) # 二维“索引”数组
>>> a[j] # 取出的元素数组与j形状相同
array([[ 9, 16],
[81, 49]])
再举一个例子,下面这段程序模拟了从一个“着色板”里面取出颜色汇成一个色彩图像。
>>> palette = np.array( [ [0,0,0], # 黑
... [255,0,0], # 红
... [0,255,0], # 绿
... [0,0,255], # 蓝
... [255,255,255] ] ) # 白
>>> image = np.array( [ [ 0, 1, 2, 0 ], # 每个值和颜色数组对应
... [ 0, 3, 4, 0 ] ] )
>>> palette[image] # 彩色图
array([[[ 0, 0, 0],
[255, 0, 0],
[ 0, 255, 0],
[ 0, 0, 0]],
[[ 0, 0, 0],
[ 0, 0, 255],
[255, 255, 255],
[ 0, 0, 0]]])
当然也可以对多维进行操作:
>>> a = np.arange(12).reshape(3,4)
>>> a
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
>>> i = np.array( [ [0,1],
... [1,2] ] )
>>> j = np.array( [ [2,1],
... [3,3] ] )
>>>
>>> a[i,j]
array([[ 2, 5],
[ 7, 11]])
>>>
>>> a[i,2]
array([[ 2, 6],
[ 6, 10]])
>>>
>>> a[:,j]
array([[[ 2, 1],
[ 3, 3]],
[[ 6, 5],
[ 7, 7]],
[[10, 9],
[11, 11]]])
为了程序的美观和效率,一般这样使用:
>>> l = [i,j]
>>> a[l] # 相当于a[i,j]
array([[ 2, 5],
[ 7, 11]])
深度学习常用的几个numpy函数
numpy.prod:返回数组内元素的乘积
>>> np.prod([1.,2.]) # 返回一维数组内元素的乘积
2.0
>>> np.prod([[1.,2.],[3.,4.]]) # 返回二维数组里面所有元素的乘积
24.0
>>> np.prod([[1.,2.],[3.,4.]], axis=1) # 将每列的元素进行乘积
array([ 2., 12.])
>>> x = np.array([1, 2, 3], dtype=np.uint8) #还可以指定元素类型
>>> np.prod(x).dtype == np.uint
True
>>> x = np.array([1, 2, 3], dtype=np.int8)
>>> np.prod(x).dtype == np.int
True
numpy.pad:将数组(矩阵)周围进行填充
>>> a = np.array([[1,2,3],[2,3,4],[3,4,5]])
>>>a
Out[4]:
array([[1, 2, 3],
[2, 3, 4],
[3, 4, 5]])
>>> np.lib.pad(a,1,'constant',constant_values=0) #将a数组周围填充宽度为1的常数0
Out[5]:
array([[0, 0, 0, 0, 0],
[0, 1, 2, 3, 0],
[0, 2, 3, 4, 0],
[0, 3, 4, 5, 0],
[0, 0, 0, 0, 0]])