NumPy 部分功能如下:
- ndarray,一个具有矢量运算符和复杂广播能力的快速节省空间的多维数组
- 用于对数组数据进行快速运算的标准数学函数
- 用于读写磁盘数据的工具以及用于操作内存映射文件的工具
- 线性代数丶随机数生成以及傅里叶变换功能
- 用于继承由C丶C++ 丶Fortran等语言编写的代码的工具
NumPy 最重要的一个特点就是其N维数组对象(ndarray),该对象是一个快速而灵活的大数据集容器 。可以利用这个数组对象对整块数据进行一些数学运算,其语法跟标量之间的运算一样。
统一进入方式:ipython --pylab
> ipython --pylab Python 3.6.5 (v3.6.5:f59c0932b4, Mar 28 2018, 17:00:18) [MSC v.1900 64 bit (AMD64)] Type 'copyright', 'credits' or 'license' for more information IPython 6.4.0 -- An enhanced Interactive Python. Type '?' for help. Using matplotlib backend: TkAgg
In [5]: import numpy as np In [6]: data=np.array([[0.9526,-0.246,-0.8856],[0.5639,0.2379,0.9104]]) In [7]: data Out[7]: array([[ 0.9526, -0.246 , -0.8856], [ 0.5639, 0.2379, 0.9104]]) In [8]: data * 10 Out[8]: array([[ 9.526, -2.46 , -8.856], [ 5.639, 2.379, 9.104]]) In [9]: data + data Out[9]: array([[ 1.9052, -0.492 , -1.7712], [ 1.1278, 0.4758, 1.8208]])
ndarray是一个通用的同构数据多维容器,也就是说,其中的所有元素必须是相同类型的。每个数组都有一个shape(一个表示个维度大小的元组)和一个dtype(一个用于说明数组数据类型的对象)
In [29]: test_data=np.array([[1,2,3],[4,5,6],[7,8,9]]) In [30]: data Out[30]: array([[ 0.9526, -0.246 , -0.8856], [ 0.5639, 0.2379, 0.9104]]) In [31]: test_data Out[31]: array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) In [32]: data.shape Out[32]: (2, 3) #表示2行3列 In [33]: test_data.shape Out[33]: (3, 3) #表示3行3列
In [34]: data.dtype Out[34]: dtype('float64') In [35]: test_data.dtype Out[35]: dtype('int32')
创建ndarry
创建数组最简单的办法就是使用array函数。它接受一切序列型的对象(包括其他数组),然后产生一个新的传入数据的NumPy数组。
In [1]: import numpy as np In [2]: data1=[6,7.5,8,0,1] In [3]: arr1=np.array(data1) In [4]: arr1 Out[4]: array([6. , 7.5, 8. , 0. , 1. ])
因为里面有7.5为浮点数,所以:
In [5]: arr1.dtype Out[5]: dtype('float64')
查询出的数据类型为float64
除非显示说明,np.array会尝试为新建的这个数组推断出一个较为合适的数据类型。数据类型保存在一个特殊的dtype对象中。
嵌套序列
比如一组等长的列表组成的列表,将会转换成一个多维数组 :
In [6]: data2=[[1,2,3,4],[3,4,5,6],[5,6,7,8]] In [7]: arr2=np.array(data2) In [8]: arr2 Out[8]: array([[1, 2, 3, 4], [3, 4, 5, 6], [5, 6, 7, 8]]) In [9]: arr2.ndim Out[9]: 2 In [10]: arr2.shape Out[10]: (3, 4) In [11]: arr2.dtype Out[11]: dtype('int32')
其中ndim和shape介绍:
ndim:数组的维数。如二维可以通过array[x][y]获取,三维是通过array[x][y][z]获取
shape:返回值是一个元组,里面每个数字表示每一维的长度。
In [13]: arr3=np.ones([2,3,4]) In [14]: arr3 Out[14]: 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.]]]) In [15]: arr3.ndim Out[15]: 3 In [16]: arr3.shape Out[16]: (2, 3, 4)
除了np.array之外,还有一些函数可以新建数组。比如zeros和ones分别可以创建制定长度或形状的全0或全1数组。emtpy可以创建一个没有任何具体值的数组。
In [17]: np.zeros(10) Out[17]: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]) In [19]: np.zeros((3,6)) Out[19]: array([[0., 0., 0., 0., 0., 0.], [0., 0., 0., 0., 0., 0.], [0., 0., 0., 0., 0., 0.]]) In [20]: np.zeros((3,6,7)) Out[20]: 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., 0., 0., 0., 0.], [0., 0., 0., 0., 0., 0., 0.], [0., 0., 0., 0., 0., 0., 0.]], [[0., 0., 0., 0., 0., 0., 0.], [0., 0., 0., 0., 0., 0., 0.], [0., 0., 0., 0., 0., 0., 0.], [0., 0., 0., 0., 0., 0., 0.], [0., 0., 0., 0., 0., 0., 0.], [0., 0., 0., 0., 0., 0., 0.]], [[0., 0., 0., 0., 0., 0., 0.], [0., 0., 0., 0., 0., 0., 0.], [0., 0., 0., 0., 0., 0., 0.], [0., 0., 0., 0., 0., 0., 0.], [0., 0., 0., 0., 0., 0., 0.], [0., 0., 0., 0., 0., 0., 0.]]]) In [21]: np.ones((2,3)) Out[21]: array([[1., 1., 1.], [1., 1., 1.]]) In [22]: np.ones(5) Out[22]: array([1., 1., 1., 1., 1.]) In [27]: np.empty((2,3,1)) Out[27]: array([[[1.], [1.], [1.]], [[1.], [1.], [1.]]]) In [28]: np.empty((2,3,2)) Out[28]: array([[[0., 0.], [0., 0.], [0., 0.]], [[0., 0.], [0., 0.], [0., 0.]]])
注意:np.empty返回的是一些未初始化的垃圾值,并不一定返回全0数组 。
arange是python内置函数range的数组版:
In [31]: np.arange(10)
Out[31]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
数组函数创建:
array: 将输入数据(列表,元组,数组或其他序列类型)转换为ndarry。 要么推断出dtype,要么显示制定dtype。默认直接复制输入数据
asarray:将输入转换成ndarry,如果本身就是一个ndarry就不进行复制。
arange:类似内置的range,但返回的是一个ndarry而不是列表。
ones 丶ones_like:根据指定的形状和dtype创建一个全1数组。ones_like 以另一个数组为参数,并根据其形状和dtype创建一个全1数组 。
zeros丶zeros_like:类似ones 丶ones_like,不过创建的是全0数组。
empty丶empty_like :创建新数组,只分配内存空间但不填充任何值
eye丶identity :创建一个N * N 单位矩阵(对角线为1,其余为0)
In [42]: np.eye(5) Out[42]: array([[1., 0., 0., 0., 0.], [0., 1., 0., 0., 0.], [0., 0., 1., 0., 0.], [0., 0., 0., 1., 0.], [0., 0., 0., 0., 1.]]) In [43]: np.identity(5) Out[43]: array([[1., 0., 0., 0., 0.], [0., 1., 0., 0., 0.], [0., 0., 1., 0., 0.], [0., 0., 0., 1., 0.], [0., 0., 0., 0., 1.]])
ndarry的数据类型
dtype(数据类型)是一个特殊的对象,它包含有ndarry将一块内存解释为特定数据类型所需的信息:
In [45]: arr1=np.array([1,2,3],dtype=np.float64) In [46]: arr1 Out[46]: array([1., 2., 3.]) In [47]: arr2=np.array([1,2,3],dtype=np.int32) In [48]: arr1.dtype Out[48]: dtype('float64') In [49]: arr2.dtype Out[49]: dtype('int32')
一个数组创建后,系统会自动地将python类型映射到等价的dtype上 。
可以通过ndarry的astype方法显式的转换dtype:( astype --> as type 修改类型; dtype--> 的type,查询出当前类型 )
In [51]: arr=np.array([1,2,3,4,5]) In [52]: arr.dtype Out[52]: dtype('int32') In [53]: float_arr=arr.astype(np.float64) In [54]: float_arr.dtype Out[54]: dtype('float64')
上面将整数转换成了浮点数,如果将浮点数转换成整数,那么小数部分将会截断 :
In [56]: arr=np.array([1.2,3.6,5.3,4.8,7.0,5.11,8.12]) In [57]: arr Out[57]: array([1.2 , 3.6 , 5.3 , 4.8 , 7. , 5.11, 8.12]) In [58]: arr.astype(np.int32) Out[58]: array([1, 3, 5, 4, 7, 5, 8])
还有另外一个用法:
In [65]: float_array Out[65]: array([1.2 , 5.8 , 2.89, 7.53]) In [66]: int_array Out[66]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) In [67]: float_array.dtype Out[67]: dtype('float64') In [68]: int_array.dtype Out[68]: dtype('int32') In [69]: int_array.astype(float_array.dtype) Out[69]: array([0., 1., 2., 3., 4., 5., 6., 7., 8., 9.]) In [70]: int_array.dtype Out[70]: dtype('int32') In [71]: int_arr=int_array.astype(float_array.dtype) In [72]: int_arr.dtype Out[72]: dtype('float64')
从上面可以看到int_array原始数据类型为int32,float_array为float64,执行int_array.astype(float_array.dtype)后是将int_array数据类型设置为float_array的一样,但是70行再次查询时发现int_array数据类型仍然是int32,只有定义一个新数组来接受时,新数组类型才是float64
总结:astype使用方法ndarry.astype(np.dtype) 返回一个新的,dtype为传入的新的dtype的新数组,原始数组数据类型保持不变 。
数组和标量之间的运算
大小相等的数组之间的任何算术运算都会将运算应用到元素级:
In [89]: arr1 Out[89]: array([[1, 2, 3], [4, 5, 6]]) In [90]: arr2 Out[90]: array([[1, 2, 3], [4, 5, 2]]) In [91]: arr1 * arr2 Out[91]: array([[ 1, 4, 9], [16, 25, 12]]) In [92]: arr1 + arr2 Out[92]: array([[ 2, 4, 6], [ 8, 10, 8]]) In [93]: arr1 ** 2 Out[93]: array([[ 1, 4, 9], [16, 25, 36]], dtype=int32) In [94]: 1 / arr1 Out[94]: array([[1. , 0.5 , 0.33333333], [0.25 , 0.2 , 0.16666667]])
不同大小的数组之间的运算叫做广播。
基本的索引和切片
In [1]: arr=np.arange(10) In [2]: arr Out[2]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) In [3]: arr[5:8] Out[3]: array([5, 6, 7]) In [4]: arr[5:8]=12 In [5]: arr Out[5]: array([ 0, 1, 2, 3, 4, 12, 12, 12, 8, 9]) In [6]: arr_slice=arr[5:8] In [7]: arr_slice Out[7]: array([12, 12, 12]) In [8]: arr_slice[1] Out[8]: 12 In [9]: arr_slice[1]=12345 In [10]: arr Out[10]: array([ 0, 1, 2, 3, 4, 12, 12345, 12, 8, 9]) In [11]: arr_slice[:]=64 In [12]: arr Out[12]: array([ 0, 1, 2, 3, 4, 64, 64, 64, 8, 9])
可以发现,当你将一个标量值赋值给一个切片时(如arr[5:8]=12) ,该值会自动传播到整个选区。跟列表最重要的区别在于,数组切片是原始数组的视图。这意味着数据不会被复制,视图上的任何修改都会直接反映到源数组上。
对于高维度数组,能做的事情更多。在一个二维数组中,各索引位置上的元素不再是标量而是一维数组。
In [13]: arr2d=np.array([[1,2,3],[4,5,6],[7,8,9]]) In [14]: arr2d[2] Out[14]: array([7, 8, 9]) #第三个数组 In [15]: arr2d[0][2] #第一个数组的第三个 Out[15]: 3
在多维数组中,如果省略了后面的索引,则返回对象是一个维度低一点的ndarry 。
比如,在下面的一个2 * 2 * 3的数组arr3d中:
In [16]: arr3d=np.array([[[1,2,3],[4,5,6]],[[7,8,9],[10,11,12]]]) In [17]: arr3d Out[17]: array([[[ 1, 2, 3], [ 4, 5, 6]], [[ 7, 8, 9], [10, 11, 12]]]) In [18]: arr3d[0] #先去掉最外面的[],然后取第一个列表 Out[18]: array([[1, 2, 3], [4, 5, 6]]) In [19]: arr3d[0][1] #从最外面的[]看起,先取第一个列表,再从里面取第二个 Out[19]: array([4, 5, 6])
标量和数组都可以赋值给arr3d[0]
In [20]: old_values=arr3d[0].copy() #先将arr3d[0]的值赋给old_values Out[22]: array([[1, 2, 3], [4, 5, 6]]) In [23]: arr3d[0]=42 #修改值后 In [24]: arr3d Out[24]: array([[[42, 42, 42], [42, 42, 42]], [[ 7, 8, 9], [10, 11, 12]]]) In [25]: arr3d[0]=old_values #重新赋值 In [26]: arr3d Out[26]: array([[[ 1, 2, 3], [ 4, 5, 6]], [[ 7, 8, 9], [10, 11, 12]]])
切片索引
ndarry的切片语法跟python列表这样的一维对象差不多:
In [1]: arr=np.array([0,1,2,3,4,64,64,64,8,9]) In [2]: arr[1:6] Out[2]: array([ 1, 2, 3, 4, 64])
高维度对象的花样更多,可以在一个或多个轴上进行切片,也可以跟整数索引混合使用。
In [4]: arr2d Out[4]: array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) In [5: arr2d[:2] #取第一个(下标为0)和第二个(下标为1) Out[5]: array([[1, 2, 3], [4, 5, 6]])
可以一次传入多个切片,就像传入多个索引一样:
In [8]: arr2d[:2,:1] Out[8]: array([[1], [4]])
通过整数索引和切片混合,可以得到低维度的切片:
In [9]: arr2d[1,:2] Out[9]: array([4, 5]) #先取下标1的,即第二个列表,然后切片[:2]取前2个 In [10]: arr2d Out[10]: array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
切片跟切片混合的,只能得到相同维数的数组视图。但是数组索引跟切片混合可以得到低维度的数组视图。数组索引跟数组索引混合,得到的是低维度的视图或数值:
下面介绍数组索引和数组索引混合:
In [19]: arr3d #原始数组 Out[19]: array([[[ 1, 2, 3], [ 4, 5, 6]], [[ 7, 8, 9], [10, 11, 12]]]) In [21]: arr3d[1] #取第二个数组 Out[21]: #得到一个二维数组 array([[ 7, 8, 9], [10, 11, 12]]) In [22]: arr3d[1,1] #取第二个数组,然后再取第二个 Out[22]: array([10, 11, 12]) #得到一个一维数组 In [24]: arr3d[1,1,1] #取第二个数组,然后再取第二个,然后再取第二个 Out[24]: 11 #得到一个数值