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