Numpy
NumPy 是 Numerical Python的简称,是Python的高性能计算和数据分析的基础核心包。与Python的基本数据类型相比,其具有以下突出优势:
- 提供功能更强大的高维数组(N-dimensional)对象
- 强大的广播功能(broadcasting),便于矢量化数组操作(直接对数组进行数据处理,而不需要编写循环)
- 集成了 C/C++ 以及 Fortran代码编写的工具
- 包含常用的线性代数、傅里叶变换,以及随机数生成
- 提供易用的C API,可以将数据传递到使用低级语言编写的外部库,也可以使外部库返回NumPy数组数据到Python
- 通用的数组算法,例如:sorting,unique和set等操作
-
NumPy提供了两种基本的对象:ndarray(N-dimensional array object)和ufunc(universal function object)。ndarray用来存储单一数据类型的多维数组,ufunc是对数组进行处理的函数。
ndarray
笔者在初次接触NumPy的数组对象时,一直搞不清其与Python的基本数据对象List,array的区别,在此对三者用于数值操作时的差异进行对比:
- list在Python中应用非常广泛,常常被当作数组使用,List的大小可以随意改变,同一个List的元素可以是任意类型的数据,因此List需要保存每个对象的指针。但是这样的特性在数值操作中就成为了很大的弊端,试想List中存储的都是类似于1,2,3这样的数值类型,此时仍要保存这些整数对象的指针,这种存储结构对于数值计算来说显然是极浪费内存和计算时间的。
- 由于List强大的灵活性,Python自带的array模块很少被使用,array数组只能存储同种数据类型,但是它所占的存储空间的要远小于List,类似于c语言中的数组类型。如果是对一致的数据类型进行数值运算,array在性能上要优与List。但是array和List相比,不支持多维数组,也没有丰富的数值运算函数,因此在数值运算上也很鸡肋。
- NumPy数组对象ndarray支持多维数组,同时数组类型必须是同质的,高效的数值操纵,弥补了以上两者的不足。
-
基本属性
ndarray.ndim
数组的最大维度,也叫做数组的轴的个数(从0开始)。In [12]: a = np.arange(20).reshape(1,4,5) In [13]: a Out[13]: array([[[ 0, 1, 2, 3, 4], [ 5, 6, 7, 8, 9], [10, 11, 12, 13, 14], [15, 16, 17, 18, 19]]]) In [14]: a.ndim Out[14]: 3
ndarray.shape
数组的形状,是一个整数元组,显示了每个轴的大小。对于一个n行m列的数组,shape就是元组(n,m) ,元祖的长度也是轴的总数。In [15]: a.shape Out[15]: (1L, 4L, 5L) In [18]: len(a.shape) Out[18]: 3
使用shape改变数组形状
In [19]: a.shape = 2,2,5 #也可以是a.shape = ((2,2,5)) In [20]: a Out[20]: array([[[ 0, 1, 2, 3, 4], [ 5, 6, 7, 8, 9]], [[10, 11, 12, 13, 14], [15, 16, 17, 18, 19]]])
如果数组的某个轴的长度被设置为-1,则该轴的长度将被自动计算出来
In [38]: a.shape = 4,-1 In [39]: a Out[39]: array([[ 0, 1, 2, 3, 4], [ 5, 6, 7, 8, 9], [10, 11, 12, 13, 14], [15, 16, 17, 18, 19]])
使用reshape方法创建一个改变了尺寸的新的数组,原数组的大小不变
In [21]: a.reshape(5,2,2) Out[21]: array([[[ 0, 1], [ 2, 3]], [[ 4, 5], [ 6, 7]], [[ 8, 9], [10, 11]], [[12, 13], [14, 15]], [[16, 17], [18, 19]]]) In [22]: a Out[22]: array([[[ 0, 1, 2, 3, 4], [ 5, 6, 7, 8, 9]], [[10, 11, 12, 13, 14], [15, 16, 17, 18, 19]]])
注意:1、使用reshape创建的数组是原数组的试图,和原数组共享内存,修改任何一个数组的元素值都会同时改变两个数组。
2、使用reshape并不改变数组中元素在内存中的位置,这和矩阵的转置不同,后者将改变元素在内存中的位置。In [45]: b = a.reshape(5,2,2) In [46]: b[2]=10 In [47]: b Out[47]: array([[[ 0, 1], [ 2, 3]], [[ 4, 5], [ 6, 7]], [[10, 10], [10, 10]], [[12, 13], [14, 15]], [[16, 17], [18, 19]]]) In [48]: a Out[48]: array([[ 0, 1, 2, 3, 4], [ 5, 6, 7, 10, 10], [10, 10, 12, 13, 14], [15, 16, 17, 18, 19]])
ndarray.size
数组中总的元素个数,等于元组shape的内积In [49]: a.shape Out[49]: (4L, 5L) In [50]: a.size 20
ndarray.dtype
一个描述数组中元素类型的对象。NumPy提供了 numpy.int32, numpy.int16, 和 numpy.float64 等类型。创建数组的时候可以指定数组内元素的类型,对dtype属性指定元素类型In [54]: a = arange(0,19,2,dtype=float) In [55]: a Out[55]: array([ 0., 2., 4., 6., 8., 10., 12., 14., 16., 18.]) In [56]: a = arange(0,19,2,dtype=complex) In [57]: a Out[57]: array([ 0.+0.j, 2.+0.j, 4.+0.j, 6.+0.j, 8.+0.j, 10.+0.j, 12.+0.j, 14.+0.j, 16.+0.j, 18.+0.j])
ndarray.itemsize
数组中每个元素的大小。
例如 数组元素类型是 float64 的itemsize为 8 (=64/8), complex32 类型的 itemsize是4 (=32/8). 等价于ndarray.dtype.itemsize.>>> a = np.array([[1.3,3.5],[3.6,6.]]) >>> a.dtype dtype('float64') >>> a.itemsize 8
创建数组
NumPy创建数组的方式很多,主要有以下几种:
array创建数组
使用Python的array模块创建数组,传入的参数类似于List创建的多维数组,参数也可以是dtype,用于指定数据的类型
>>> np.array([[1,2,3],[4,5,6]]) array([[1, 2, 3], [4, 5, 6]])
等价于
In [10]: a = [[1,2,3],[4,5,6]] In [11]: np.array(a) Out[11]: array([[1, 2, 3], [4, 5, 6]])
注意:不能缺少最外围的[ ]
>>> np.array([1,2,3],[4,5,6]) Traceback (most recent call last): File "<pyshell#47>", line 1, in <module> np.array([1,2,3],[4,5,6]) TypeError: data type not understood
占位符创建数组
以上几种方式只是对数据创建方式的简单解释,实际应用中,数组中的元素常常是未知的,但是其大小是确定的,因此可以使用占位符来初始化数组的内容,NumPy提供了几种占位符方法。每种方法都可以指定元素的类型
np.zeros
创建一个内容全是0的数组>>> np.zeros((3,4)) array([[ 0., 0., 0., 0.], [ 0., 0., 0., 0.], [ 0., 0., 0., 0.]])
np.ones
创建内容全是1的数组, 可以通过dtype制定参数数据类型>>> np.ones((2,3,4),dtype=np.float64) array([[[ 1., 1., 1., 1.], [ 1., 1., 1., 1.], [ 1., 1., 1., 1.]], [[ 1., 1., 1., 1.], [ 1., 1., 1., 1.], [ 1., 1., 1., 1.]]])
np.empty
不对内容进行初始化,只分配内存空间。内容常常是未初始化的垃圾值指定范围生成数组
np.arange
前两个参数是元素值的范围,最后一个参数是相邻元素之间的跨度(也称为步长)>>> np.arange(0,1,0.1) array([ 0. , 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9])
如果步长为负数,表示起始值大于终止值
In [103]: x = arange(7,2,-2) In [104]: x Out[104]: array([7, 5, 3])
np.linspace
对于浮点类型的数组,给出范围和步长,通常很难预测元素的数量(与浮点数的精度有关),因此对于浮点类型数组,最好指定其元素个数,而不是其步长。
前两个参数依然是元素值范围,最后一个参数是产生的数据个数。>>> np.linspace(1,6,4) array([ 1. , 2.66666667, 4.33333333, 6. ])
np.logspace
logspace函数和linspace函数类似,但是前者创建以10为底的等比数列,如下,创建一个含有10个元素的等比数列,范围在1(10^0)到(10^20)In [58]: a = logspace(0,20,10) In [59]: a Out[59]: array([ 1.00000000e+00, 1.66810054e+02, 2.78255940e+04, 4.64158883e+06, 7.74263683e+08, 1.29154967e+11, 2.15443469e+13, 3.59381366e+15, 5.99484250e+17, 1.00000000e+20])
也可以使用frombuffer, fromstring, fromfile等函数可以从字节序列创建数组
也可以自定义函数,生成数组,使用fromfunction。随机数生成数组
np.random.rand()
[0,1)上的均匀分布产生产生的随机样本,参数是生成的数组的大小In [5]: np.random.rand(2,3) Out[5]: array([[ 0.26865184, 0.62972262, 0.58904232], [ 0.47760496, 0.18785592, 0.85511524]])
np.random.randn()
不指定参数,默认服从标准正态分布(sigma为1,mu为0)
指定参数,服从 N(mu, sigma^2)的正态分布随机产生的样本,可以使用:
sigma * np.random.randn(…) + muIn [6]: np.random.randn(2,3) Out[6]: array([[ 0.88717589, -0.22562155, 1.44601412], [ 1.15406383, 0.75708636, 0.69189156]]) #其中mu是3, sigma是2.5 In [7]: 2.5*np.random.randn(2,3)+3 Out[7]: array([[ 1.22514961, 3.72308164, 2.62028917], [ 3.25345077, 4.35030785, 3.76266858]])
更多关于产生随机数的方法参见NumPy的random模块
索引和切片
一维数组切片
一维数组的切片与python的list切片差不多,主要的不同是,数组切片返回的是原数组的视图,即数据不会被复制,任何对切片后数组的修改都会反映到原数组上,而List的切片返回的是切片后的副本。
多维数组索引
数组的索引灵活而丰富,大部分索引的数组操作都是通过索引进行的。一维数组的索引较为简单,本文不再详述,接下来引入几种常见的多维数组的索引。
整数索引
不同轴的索引对应单个整数,不同轴的索引以逗号隔开In [5]: a Out[5]: array([[ 0.03664104, 0.63714562, 0.1265633 ], [ 0.12740582, 0.03731622, 0.17917975], [ 0.04590225, 0.74800245, 0.74495221], [ 0.99799526, 0.90753861, 0.93806741]]) #只选择第1个轴的索引 In [6]: a[1] Out[6]: array([ 0.12740582, 0.03731622, 0.17917975]) #选择第1个轴和第二个轴的索引 In [8]: a[1,2] Out[8]: 0.17917974571757078
注意:通过整数索引获取的数据是原数组的视图,两者共享内存,改变对应索引的数据会相应地改变原数组的数据。
In [29]: a[1][1]=10 In [30]: a Out[30]: array([[ 0.03664104, 0.63714562, 0.1265633 ], [ 0.12740582, 10. , 0.17917975], [ 0.04590225, 0.74800245, 0.74495221], [ 0.99799526, 0.90753861, 0.93806741]])
切片索引
数组的每一个轴(axis)都有一个索引,可以对一个或者多个轴进行切片,也可以与整数索引混合使用,相邻轴之间的索引参数使用逗号隔开。In [32]: a[1:3,1:3] Out[32]: array([[ 10. , 0.17917975], [ 0.74800245, 0.74495221]]) In [34]: a[1:3,1] Out[34]: array([ 10. , 0.74800245])
使用:(冒号)选择数组某个轴的所有元素
In [36]: a[:,1] Out[36]: array([ 0.63714562, 10. , 0.74800245, 0.90753861])
负数索引,从尾部开始选取数据
In [40]: a[:,-1] Out[40]: array([ 0.1265633 , 0.17917975, 0.74495221, 0.93806741])
负数切片,逆序选择指定间隔的数据,切片的起始值要大小终止值
In [44]: a[:,-3:-1] Out[44]: array([[ 0.03664104, 0.63714562], [ 0.12740582, 10. ], [ 0.04590225, 0.74800245], [ 0.99799526, 0.90753861]])
参数的个数少于轴的个数,缺失参数的轴默认为完整的切片
In [45]: a[1] #等价于a[1,:] Out[45]: array([ 0.12740582, 10. , 0.17917975])
注意:通过切片索引获取的数据也是原数组的视图。
布尔型索引
对数组进行算数运算,将会产生一个布尔型数组,运算符可以是>,<,==,!=,|(或),&(与),-(负)等In [51]: a = np.array(['one','two','three']) In [55]: a == 'two' Out[55]: array([False, True, False], dtype=bool) In [53]: b = np.random.rand(3,4) In [54]: b Out[54]: array([[ 0.23529625, 0.87521492, 0.1038766 , 0.10058617], [ 0.25178891, 0.1172799 , 0.51411217, 0.86013535], [ 0.75510171, 0.81136768, 0.12842083, 0.22549127]]) In [56]: b[a=='two'] Out[56]: array([[ 0.25178891, 0.1172799 , 0.51411217, 0.86013535]])
布尔型索引也可以和整数型索引和切片索引混合使用
In [57]: b[a=='two',:3] Out[57]: array([[ 0.25178891, 0.1172799 , 0.51411217]]) In [58]: b[a=='two',3] Out[58]: array([ 0.86013535])
注意:1、通过布尔索引从原数组中选取的数据,将复制到新创建的数组中。2、布尔数组的长度要与原数组被索引的轴的长度相等。
#布尔数组的长度大于数组第1轴的长度 In [59]: a = np.array(['one','two','three','four']) #选取元素索引在数组对应轴范围内,发出警告 In [60]: a!='four' Out[60]: array([ True, True, True, False], dtype=bool) In [61]: b[a!='four',:3] D:\Python27\Scripts\ipython:1: VisibleDeprecationWarning: boolean index did not match indexed array along dimension 0; dimension is 3 but corresponding boolean dimension is 4 Out[62]: array([[ 0.23529625, 0.87521492, 0.1038766 ], [ 0.25178891, 0.1172799 , 0.51411217], [ 0.75510171, 0.81136768, 0.12842083]]) In [63]: a!='one' Out[63]: array([False, True, True, True], dtype=bool) #选取元素索引不在数组对应轴范围内,发出警告并报错 In [64]: b[a!='one',:3] D:\Python27\Scripts\ipython:1: VisibleDeprecationWarning: boolean index did not match indexed array along dimension 0; dimension is 3 but corresponding boolean dimension is 4 --------------------------------------------------------------------------- IndexError Traceback (most recent call last) <ipython-input-61-0bbfcbfce657> in <module>() ----> 1 b[a!='one',:3] IndexError: index 3 is out of bounds for axis 0 with size 33
花式索引(fancy indexing)
使用整数数组进行索引。
单个整数数组索引。In [66]: a = np.random.rand(4,3,2) In [67]: a Out[67]: array([[[ 0.16476724, 0.76267306], [ 0.73596533, 0.79242585], [ 0.56103429, 0.7122396 ]], [[ 0.35827947, 0.10465505], [ 0.5550632 , 0.0872461 ], [ 0.57150649, 0.07027542]], [[ 0.61577706, 0.81542217], [ 0.24874289, 0.15173563], [ 0.96633552, 0.85607585]], [[ 0.96521609, 0.41418405], [ 0.67424316, 0.04368679], [ 0.37650282, 0.96858812]]]) In [69]: a[[1,3]] Out[69]: array([[[ 0.35827947, 0.10465505], [ 0.5550632 , 0.0872461 ], [ 0.57150649, 0.07027542]], [[ 0.96521609, 0.41418405], [ 0.67424316, 0.04368679], [ 0.37650282, 0.96858812]]])
多个整数数组索引
In [70]: a[[1,3],[0,1]] #等价于选取元素a[1,0]和a[3,1] Out[70]: array([[ 0.35827947, 0.10465505], [ 0.67424316, 0.04368679]]) In [71]: a[[1,3],[0,1],[0,1]] #等价于选取元素a[1,0,0]和a[3,1,1] Out[71]: array([ 0.35827947, 0.04368679])
也可以使用元祖选取数组元素,与多个整数数组选取元素的方法没有差别
In [77]: a[(1,3),(0,1),(0,1)] Out[77]: array([ 0.35827947, 0.04368679])
注意:1、通过整数数组索引从原数组中选取的数据,将复制到新创建的数组中。2、每个轴对应的整数数组的长度必须相同。
In [72]: a[[1,2],[1,2,3]] --------------------------------------------------------------------------- IndexError Traceback (most recent call last) <ipython-input-72-7e69e200063a> in <module>() ----> 1 a[[1,2],[1,2,3]] IndexError: shape mismatch: indexing arrays could not be broadcast together with shapes (2,) (3,)
关于数组切片,NumPy也允许使用多个点代替完整切片
比如:x是有可以5个轴的数组
x[1,2,…]等价于x[1,2,:,:,:],
x[…,3]等价于 x[:,:,:,:,3]
x[4,…,5,:] 等价于x[4,:,:,5,:].想了解更多数组索引方法,参见NumPy的indexing模块
输出和展开
一维数组的输出和展开类似于List,多维数组的输出,都是相对于第一个轴进行输出。
In [78]: a Out[78]: array([[[ 0.16476724, 0.76267306], [ 0.73596533, 0.79242585], [ 0.56103429, 0.7122396 ]], [[ 0.35827947, 0.10465505], [ 0.5550632 , 0.0872461 ], [ 0.57150649, 0.07027542]], [[ 0.61577706, 0.81542217], [ 0.24874289, 0.15173563], [ 0.96633552, 0.85607585]], [[ 0.96521609, 0.41418405], [ 0.67424316, 0.04368679], [ 0.37650282, 0.96858812]]]) In [80]: for row in a: ....: print row ....: print '=====' ....: [[ 0.16476724 0.76267306] [ 0.73596533 0.79242585] [ 0.56103429 0.7122396 ]] ===== [[ 0.35827947 0.10465505] [ 0.5550632 0.0872461 ] [ 0.57150649 0.07027542]] ===== [[ 0.61577706 0.81542217] [ 0.24874289 0.15173563] [ 0.96633552 0.85607585]] ===== [[ 0.96521609 0.41418405] [ 0.67424316 0.04368679] [ 0.37650282 0.96858812]] =====
也可以使用flat方法将数组元素单个输出,,flat生成矩阵或者数据的迭代器 ,输出当个元素
In [81]: for element in a.flat: ....: print element ....: 0.164767240673 0.762673061164 0.735965327746 0.792425846826 0.561034293545 0.712239602363 0.358279466566 0.104655050665 0.555063196968 0.0872460992886 0.571506485379 0.0702754155223 0.615777061656 0.815422174299 0.24874289315 0.151735634542 0.966335515789 0.856075852512 0.965216090091 0.414184052308 0.674243163403 0.043686792205 0.376502823114 0.968588116022
数组的展开一般使用方法flatten,ravel,主要用于将矩阵或者数组降为1维,两者的主要区别是:flatten返回时是原来数组或者矩阵的副本,而ravel返回的是原数组或者矩阵的视图,两者共享内存,任何一个被改变,另外一个也会被改变。
#使用flatten()方法展开数组 In [88]: b = a.flatten() In [89]: b Out[89]: array([ 0.16476724, 0.76267306, 0.73596533, 0.79242585, 0.56103429, 0.7122396 , 0.35827947, 0.10465505, 0.5550632 , 0.0872461 , 0.57150649, 0.07027542, 0.61577706, 0.81542217, 0.24874289, 0.15173563, 0.96633552, 0.85607585, 0.96521609, 0.41418405, 0.67424316, 0.04368679, 0.37650282, 0.96858812]) In [90]: b[2]=10 In [91]: b Out[91]: array([ 0.16476724, 0.76267306, 10. , 0.79242585, 0.56103429, 0.7122396 , 0.35827947, 0.10465505, 0.5550632 , 0.0872461 , 0.57150649, 0.07027542, 0.61577706, 0.81542217, 0.24874289, 0.15173563, 0.96633552, 0.85607585, 0.96521609, 0.41418405, 0.67424316, 0.04368679, 0.37650282, 0.96858812]) In [92]: a Out[92]: array([[[ 0.16476724, 0.76267306], [ 0.73596533, 0.79242585], [ 0.56103429, 0.7122396 ]], [[ 0.35827947, 0.10465505], [ 0.5550632 , 0.0872461 ], [ 0.57150649, 0.07027542]], [[ 0.61577706, 0.81542217], [ 0.24874289, 0.15173563], [ 0.96633552, 0.85607585]], [[ 0.96521609, 0.41418405], [ 0.67424316, 0.04368679], [ 0.37650282, 0.96858812]]]) #使用ravel()方法展开数组 In [93]: b = a.ravel() In [94]: b Out[94]: array([ 0.16476724, 0.76267306, 0.73596533, 0.79242585, 0.56103429, 0.7122396 , 0.35827947, 0.10465505, 0.5550632 , 0.0872461 , 0.57150649, 0.07027542, 0.61577706, 0.81542217, 0.24874289, 0.15173563, 0.96633552, 0.85607585, 0.96521609, 0.41418405, 0.67424316, 0.04368679, 0.37650282, 0.96858812]) In [95]: b[2]=20 In [96]: b Out[96]: array([ 0.16476724, 0.76267306, 20. , 0.79242585, 0.56103429, 0.7122396 , 0.35827947, 0.10465505, 0.5550632 , 0.0872461 , 0.57150649, 0.07027542, 0.61577706, 0.81542217, 0.24874289, 0.15173563, 0.96633552, 0.85607585, 0.96521609, 0.41418405, 0.67424316, 0.04368679, 0.37650282, 0.96858812]) In [98]: a Out[98]: array([[[ 0.16476724, 0.76267306], [ 20. , 0.79242585], [ 0.56103429, 0.7122396 ]], [[ 0.35827947, 0.10465505], [ 0.5550632 , 0.0872461 ], [ 0.57150649, 0.07027542]], [[ 0.61577706, 0.81542217], [ 0.24874289, 0.15173563], [ 0.96633552, 0.85607585]], [[ 0.96521609, 0.41418405], [ 0.67424316, 0.04368679], [ 0.37650282, 0.96858812]]])
注:文中所有的模块可以使用help(np.模块名字)的方式查看该模块的详细说明。使用dir(**)查看模块的内置函数,使用ipython的童鞋可以使用?(内省)的方式查看不同模块的详细使用方法
reference
1、https://docs.scipy.org/doc/numpy-dev/user/quickstart.html
2、python for data analysis
3、用Python做科学计算
4、http://docs.scipy.org/doc/numpy/reference/
5、http://cs231n.github.io/python-numpy-tutorial/#numpy-datatypes