Numpy 是广泛应用于学术,工业的python数值计算库,其底层源码由C,Fortran写成。
由于python语言灵活,简洁,而且在数值计算中有numpy,scipy,sympy这类数值计算库的依托,使得快速开发
数值算法十分简便。所以python在数值计算中应用十分广泛,而numpy更是python数值计算中的基石。
本文主要介绍array操作和Numpy常用函数,这是数值计算的基础。
一·基础的基础 numpy arrays
python数值计算领域,numpy是其他数值计算库的基础。
安装其他函数库的时候,经常看到安装条件是已安装numpy。
而在numpy中,ndarray正是numpy的基础。
In [1]: import numpy as np
In [2]: a = np.zeros(3) #生成一个三维零向量
In [3]: a
Out[3]: array([ 0., 0., 0.])
In [4]: type(a)
Out[4]: numpy.ndarray
numpy的arrays有点像python的list,但是又有一些不同: 1.array中所有元素必须为同一类型
2.而这数据类型必须是Numpy提供的数据类型(dtypes)之一
numpy提供的最重要数据类型有:
1.float64 64位浮点数
2.float32 32位浮点数
3.int64 64位整数
4.int32 32位整数
5.bool 8位 True 或者 False
还有其他一些数据类型包括:无符号整数,复数
可以测试一下自己的numpy默认的数据类型是什么
In [7]: a = np.zeros(3)
In [8]: type(a[0])
Out[8]: numpy.float64 #64位浮点数
当然,如果你要想要整数值组成的零向量也可以, 这时候需要改变默认参数的dtype=int
In [9]: a = np.zeros(3, dtype=int)
In [10]: type(a[0])
Out[10]: numpy.int32
二·数组维度
进行如下命令:
In [11]: z = np.zeros(10)
这时候z既不是行向量也不是列向量。可以叫它没有维度的‘flat’array(with no dimension ) z的维度(dimension )被记录在shape属性中,也就是z.shape ,它是一个tuple
In [12]: z.shape
Out[12]: (10,) # 元组中只有一个元素的时候要加逗号。
这时候,z.shape中只有一个元素,仅仅表示z的长度而已,你可千万别多想。 如果使z具有维度的话,那么就先从改变z.shape做起吧。
In [13]: z.shape = (10, 1) #10代表行数,1代表列数。只有1列,当然,这是一个列向量 In [14]: z Out[14]: array([[ 0.], [ 0.], [ 0.], [ 0.], [ 0.], [ 0.], [ 0.], [ 0.], [ 0.], [ 0.]]) In [15]: z = np.zeros(4) In [16]: z.shape = (2, 2) #2行2列的零矩阵 In [17]: z Out[17]: array([[ 0., 0.], [ 0., 0.]])要创建两行两列的零阵,这样做也太麻烦了,直接穿递元组(2,2)到np.zeros()就可以了。
z = np.zeros((2, 2))
三·创建数组
我们知道用np.zeros()就可以创建零元素组成的数组。同样,我们可以创建全部由1组成的数组。
z = np.ones((2, 2))#2行2列数组,数组元素都为1
我们还可以创建空数组 np.empty(),以便以后传入参数进去。
In [18]: z = np.empty(3)
In [19]: z
Out[19]: array([ 8.90030222e-307, 4.94944794e+173, 4.04144187e-262])
np.empty(3)在内存中连续开辟出可以存放3个float64元素的地址。得到的z,其中的元素都是随机的,没有意义。 要创建某个区间的连续多个数值组成的数组,可以用np.linspace(a,b,num)
代表在区间[a,b]上去num个均匀分隔这个区间的点。
In [20]: z = np.linspace(2, 4, 5) # From 2 to 4, with 5 elements
array([ 2. , 2.5, 3. , 3.5, 4. ])
创建单位矩阵可以使用np.identity(),np.eye()
In [21]: z = np.identity(2)
In [22]: z
Out[22]:
array([[ 1., 0.],
[ 0., 1.]])
另外,array可以利用np.array()直接通过python的list直接创建。
In [23]: z = np.array([10, 20]) # ndarray from Python list
In [24]: z
Out[24]: array([10, 20])
In [25]: type(z)
Out[25]: numpy.ndarray
In [26]: z = np.array((10, 20), dtype=float) # Here 'float' is equivalent to 'np.float64'
In [27]: z
Out[27]: array([ 10., 20.])
In [28]: z = np.array([[1, 2], [3, 4]]) # 2D array from a list of lists
In [29]: z
Out[29]:
array([[1, 2],
[3, 4]])
还有一个类似的函数np.asarray().它与 np.array()有一些不同,np.asarray()并不是复制原有对象,而是引用原有对象。
而np.array()是创建一个新的np.narray对象。
In [11]: na = np.linspace(10, 20, 2) In [12]: na is np.asarray(na) # Does not copy NumPy arrays Out[12]: True In [13]: na is np.array(na) # Does make a new copy --- perhaps unnecessarily Out[13]: False
四·载入和写入txt文件
np.loadtxt() 文件的每行数据必须有相同数目的数值
>>> from StringIO import StringIO # StringIO behaves like a file object
>>> c = StringIO("0 1\n2 3")
>>> np.loadtxt(c)
array([[ 0., 1.],
[ 2., 3.]])
同样,还可以载入字符串数据 >>> d = StringIO("M 21 72\nF 35 58")
>>> np.loadtxt(d, dtype={'names': ('gender', 'age', 'weight'),
... 'formats': ('S1', 'i4', 'f4')}) #'name' 表示数据各列的名称。'format'表式格式。
array([('M', 21, 72.0), ('F', 35, 58.0)],
dtype=[('gender', '|S1'), ('age', '<i4'), ('weight', '<f4')])
更加高级的使用形式,如载入csv文件:
>>> c = StringIO("1,0,2\n3,0,4")
>>> x, y = np.loadtxt(c, delimiter=',', usecols=(0, 2), unpack=True)
>>> x
array([ 1., 3.])
>>> y
array([ 2., 4.])
delimiter 表示分隔符为逗号,usecols表示载入文件中的哪列数据。本文中是第0,2列。unpack参数 表示的是分裂,用x引用第0列,用y引用第2列。unpack的默认值为False,如果要使用x,y = np.loadtxt(something)
一定不要忘记修改unpack的默认值。
使用更高级的np.genfromtxt()载入文本数据。genfromtxt可以处理缺失值。
>>> s = StringIO("1,1.3,abcde")
>>> data = np.genfromtxt(s, dtype=[('myint','i8'),('myfloat','f8'),
... ('mystring','S5')], delimiter=",")
>>> data
array((1, 1.3, 'abcde'),
dtype=[('myint', '<i8'), ('myfloat', '<f8'), ('mystring', '|S5')])
>>> s.seek(0) # needed for StringIO example only
>>> data = np.genfromtxt(s, dtype=None,
... names = ['myint','myfloat','mystring'], delimiter=",")
>>> data
array((1, 1.3, 'abcde'),
dtype=[('myint', '<i8'), ('myfloat', '<f8'), ('mystring', '|S5')])
>>> s.seek(0)
>>> data = np.genfromtxt(s, dtype="i8,f8,S5",
... names=['myint','myfloat','mystring'], delimiter=",")
>>> data
array((1, 1.3, 'abcde'),
dtype=[('myint', '<i8'), ('myfloat', '<f8'), ('mystring', '|S5')])
使用固定宽度分割每行数据,delimiter=[1,3,5]各列固定宽度分别为1,3,5
>>> s = StringIO("11.3abcde")
>>> data = np.genfromtxt(s, dtype=None, names=['intvar','fltvar','strvar'],
... delimiter=[1,3,5])
>>> data
array((1, 1.3, 'abcde'),
dtype=[('intvar', '<i8'), ('fltvar', '<f8'), ('strvar', '|S5')])
五·数组索引
对于1维数组来讲,索引与python中序列的索引没有区别。
In [30]: z = np.linspace(1, 2, 5)
In [31]: z
Out[31]: array([ 1. , 1.25, 1.5 , 1.75, 2. ])
In [32]: z[0]
Out[32]: 1.0
In [33]: z[0:2] # Slice numbering is left closed, right open
Out[33]: array([ 1. , 1.25])
In [34]: z[-1]
Out[34]: 2.0
对于二维数组来讲,索引与数学上的矩阵索引没有区别。唯一不同在于,在此矩阵索引下标从0开始。
In [35]: z = np.array([[1, 2], [3, 4]])
In [36]: z
Out[36]:
array([[1, 2],
[3, 4]])
In [37]: z[0, 0]
Out[37]: 1
In [38]: z[0, 1]
Out[38]: 2
切片功能则是矩阵索引与python切片的结合。
In [39]: z[0,:] #第0行
Out[39]: array([1, 2])
In [40]: z[:,1] #第1列
Out[40]: array([2, 4])
用numpy数组索引:
In [41]: z = np.linspace(2, 4, 5)
In [42]: z
Out[42]: array([ 2. , 2.5, 3. , 3.5, 4. ])
In [43]: indices = np.array((0, 2, 3))
In [44]: z[indices]
Out[44]: array([ 2. , 3. , 3.5])
这仅仅是第一种形式,indices包含的仅仅是所需元素的位置。 indices还可以为仅仅指明每个元素是否可留,留下来为True,不需要的为False,这是另一种方法:
In [45]: z
Out[45]: array([ 2. , 2.5, 3. , 3.5, 4. ])
In [46]: d = np.array([0, 1, 1, 0, 0], dtype=bool) #注意datype是bool,1代表True,0代表False
In [47]: d
Out[47]: array([False, True, True, False, False], dtype=bool)
In [48]: z[d]
Out[48]: array([ 2.5, 3. ])
索引全部使用z[:]
In [49]: z = np.empty(3)
In [50]: z
Out[50]: array([ -1.25236750e-041, 0.00000000e+000, 5.45693855e-313])
In [51]: z[:] = 42
In [52]: z
Out[52]: array([ 42., 42., 42.])
六·数组方法
以下所有方法都被高度优化。
In [53]: A = np.array((4, 3, 2, 1))
In [54]: A
Out[54]: array([4, 3, 2, 1])
In [55]: A.sort() # 升序排序,无返回值
In [56]: A
Out[56]: array([1, 2, 3, 4])
In [57]: A.sum() # 求和
Out[57]: 10
In [58]: A.mean() # 平均值
Out[58]: 2.5
In [59]: A.max() # 最大值
Out[59]: 4
In [60]: A.argmax() # 最大值所在位置
Out[60]: 3
In [61]: A.cumsum() # 累积和
Out[61]: array([ 1, 3, 6, 10])
In [62]: A.cumprod() # 累积积
Out[62]: array([ 1, 2, 6, 24])
In [63]: A.var() # 方差
Out[63]: 1.25
In [64]: A.std() # 标准差
Out[64]: 1.1180339887498949
In [65]: A.shape = (2, 2)
In [66]: A.T # 转置 与 A.transpose() 作用相同
Out[66]:
array([[1, 3],
[2, 4]])
z.searchsorted(a) 首先要求z是非降序数组,其作用是返回z中第一个出现的比a大的元素的索引值。
In [67]: z = np.linspace(2, 4, 5)
In [68]: z
Out[68]: array([ 2. , 2.5, 3. , 3.5, 4. ])
In [69]: z.searchsorted(2.2)
Out[69]: 1
In [70]: z.searchsorted(2.5)
Out[70]: 1
In [71]: z.searchsorted(2.6)
Out[71]: 2
当然,上文所介绍的array方法,还可以直接作用于python的基本序列结构:
In [72]: a = np.array((4, 3, 2, 1))
In [73]: np.sum(a)
Out[73]: 10
In [74]: np.mean(a)
Out[74]: 2.5
七·数组运算
代数运算:+,-,*,/,**
这些运算的对象都是array中的每个元素,与向量运算一样
In [75]: a = np.array([1, 2, 3, 4])
In [76]: b = np.array([5, 6, 7, 8])
In [77]: a + b #每个元素相加
Out[77]: array([ 6, 8, 10, 12])
In [78]: a * b #每个元素相乘,这可不是向量的内积运算!
Out[78]: array([ 5, 12, 21, 32])
array与标量运算。 In [79]: a + 10
Out[79]: array([11, 12, 13, 14])
In [81]: a = np.array([1, 2, 3, 4])
In [82]: a * 10
Out[82]: array([10, 20, 30, 40])
多维数组也是如此
In [86]: A = np.ones((2, 2))
In [87]: B = np.ones((2, 2))
In [88]: A + B
Out[88]:
array([[ 2., 2.],
[ 2., 2.]])
In [89]: A + 10
Out[89]:
array([[ 11., 11.],
[ 11., 11.]])
In [90]: A * B
Out[90]:
array([[ 1., 1.],
[ 1., 1.]])
向量与矩阵乘法 numpy中向量与矩阵的乘法由dot()函数完成。
In [91]: A = np.array([1, 2])
In [92]: B = np.array([10, 20])
In [93]: np.dot(A, B) # Returns a scalar in this case
Out[93]: 50
In [137]: A = np.ones((2, 2))
In [138]: B = np.ones((2, 2))
In [139]: np.dot(A, B)
Out[139]:
array([[ 2., 2.],
[ 2., 2.]])
实际上,我们可以直接利用np.dot()对python序列进行计算。当然,这里的序列可不是字符串
In [94]: A = np.empty((2, 2))
In [95]: A
Out[95]:
array([[ 3.48091887e-262, 1.14802984e-263],
[ 3.61513512e-313, -1.25232371e-041]])
In [96]: np.dot(A, (0, 1))
Out[96]: array([ 1.14802984e-263, -1.25232371e-041])
这里(0,1)被认为是一个列向量 八·比较操作
比较操作,比较的是两个array上相对应位置的元素的值,返回的是元素类型为bool的array
In [97]: z = np.array([2, 3])
In [98]: y = np.array([2, 3])
In [99]: z == y
Out[99]: array([ True, True], dtype=bool)
In [100]: y[0] = 5
In [101]: z == y
Out[101]: array([False, True], dtype=bool)
In [102]: z != y
Out[102]: array([ True, False], dtype=bool)
比较符号依然是,>,<,==,!=,>=,<= array可以直接与标量比较
In [103]: z = np.linspace(0, 10, 5)
In [104]: z
Out[104]: array([ 0. , 2.5, 5. , 7.5, 10. ])
In [105]: z > 3
Out[105]: array([False, False, True, True, True], dtype=bool)
比较操作经常用于条件索引:
In [106]: b = z > 3
In [107]: b
Out[107]: array([False, False, True, True, True], dtype=bool)
In [108]: <span style="color:#ff0000;">z[b]</span>
Out[108]: array([ 5. , 7.5, 10. ])
更加直接的比较索引如下:
In [109]:<span style="color:#ff0000;"> z[z > 3]</span>
Out[109]: array([ 5. , 7.5, 10. ])
九·函数的向量化
numpy中还有一些其他函数可以直接处理array数据。如np.sin(),np.cos(),np.exp(),np.log()
In [110]: z = np.array([1, 2, 3])
In [111]: np.sin(z)
Out[111]: array([ 0.84147098, 0.90929743, 0.14112001])
In [112]: z
Out[112]: array([1, 2, 3])
In [113]: (1 / np.sqrt(2 * np.pi)) * np.exp(- 0.5 * z**2)
Out[113]: array([ 0.24197072, 0.05399097, 0.00443185])
In [114]: import numpy as np In [115]: x = np.random.randn(4) In [116]: x Out[116]: array([-0.25521782, 0.38285891, -0.98037787, -0.083662 ]) In [117]: np.where(x > 0, 1, 0) # Insert 1 if x > 0 true, otherwise 0 Out[117]: array([0, 1, 0, 0])np.where(x>0,1,0) 十分像C语言中的唯一三元运算符 a>b? 1:0
一个对标量数据进行处理的函数,如何才能用于处理向量呢?
当然可以直接重新写一个,但是比较费时费力,numpy给我们提供了另一个思路:
在原有标量函数的基础上,将标量函数向量化,得到的新函数可以直接处理向量数据:
In [118]: def f(x): return 1 if x > 0 else 0
In [119]: f = np.vectorize(f)
In [120]: f(x) # Passing same vector x as previous example
Out[120]: array([0, 1, 0, 0])
十·其他函数
In [131]: A = np.array([[1, 2], [3, 4]])
In [132]: np.linalg.det(A) # 行列式
Out[132]: -2.0000000000000004
In [133]: np.linalg.inv(A) # 求逆矩阵
Out[133]:
array([[-2. , 1. ],
[ 1.5, -0.5]])
In [134]: Z = np.random.randn(10000) # 生成正态分布
In [135]: y = np.random.binomial(10, 0.5, size=1000) # 生成伯努利分布
In [136]: y.mean()
Out[136]: 5.0369999999999999