NumPy库
NumPy是用Python进行科学计算,尤其是数据分析时,所用到的一个基础库。它是大量Python数学和科学计算包的基础。
1 NumPy简史
Python语言诞生不久,开发人员就产生了数值计算的需求,更为重要的是,科学社区开始考虑用它进行科学计算。
1995年,Jim Hugunin开发了Numeric,这是第一次尝试用Python进行科学计算。随后又诞生了Numarray包。这两个包都是专门用于数组计算的,但各有各的优势,开发人员只好根据不同的使用场景,从中选择效率更高的包。由于两者之间的区别并不那么明确,开发人员产生了把它们整合为一个包的想法。Travis Oliphant遂着手开发NumPy库,并于2006年发布了它的第一个版本。
从此之后,NumPy成为Python科学计算的扩展包。如今,在计算多维数组和大型数组方面,它是使用最广的。此外,它还提供多个函数,操作起数组来效率很高,还可用来实现高级数学运算。
当前,NumPy是开源项目,使用BSD许可证。在众多开发者的支持下,这个库的潜力得到了进一步挖掘。
2 NumPy安装
大多数Python发行版都把NumPy作为一个基础包。如果NumPy不是基础包的话,可以自行安装。
Linux系统(Ubuntu和Debian):
sudo apt-get install python-numpy
使用Anaconda发行版的Windows系统:
conda install numpy
输入以下代码导入它NumPy模块:
import numpy as np
3 ndarray
整个NumPy库的基础是ndarray(N-dimensional array,N维数组)对象。它是一种由同质元素组成的多维数组,元素数量是事先指定好的。同质指的是几乎所有元素的类型和大小都相同。事实上,数据类型由另外一个叫作dtype(data-type,数据类型)的NumPy对象来指定;每个ndarray只有一种dtype类型。
数组的维数和元素数量由数组的型(shape)来确定,数组的型由N个正整数组成的元组来指定,元组的每个元素对应每一维的大小。数组的维统称为轴(axes),轴的数量被称作秩(rank)。
NumPy数组的另一个特点是大小固定,也就是说,创建数组时一旦指定好大小,就不会再发生改变。这与Python的列表有所不同,列表的大小是可以改变的。
定义ndarray最简单的方式是使用array()函数,以Python列表作为参数,列表的元素即是ndarray的元素。
>>> a = np.array([1, 2, 3])
>>> a
array([1, 2, 3])
检测新创建的对象是否是ndarray很简单,只需要把新声明的变量传递给type()函数即可。
>>> type(a)
<type 'numpy.ndarray'>
调用变量的dtype属性,即可获知新建的ndarray属于哪种数据类型。
>>> a.dtype
dtype('int32')
刚建的这个数组只有一个轴,秩的数量为1,它的型为(3,1)。这些值的获取方法如下:轴数量需要使用ndim属性,数组长度使用size属性,而数组的型要用shape属性。
>>> a.ndim
1
>>> a.size
3
>>> a.shape
(3L,)
这个数组再简单不过,只有一维。但是数组很容易就能扩展成多维。例如,可以定义一个2×2的二维数组:
>>> b = np.array([[1.3, 2.4],[0.3, 4.1]])
>>> b.dtype
dtype('float64')
>>> b.ndim
2
>>> b.size
4
>>> b.shape
(2L, 2L)
这个数组有两条轴,所以秩为2,每条轴的长度为2。
ndarray对象拥有另外一个叫作itemsize的重要属性。它定义了数组中每个元素的长度为几个字节。data属性表示的是包含数组实际元素的缓冲区。该属性至今用得并不多,因为要获取数组中的元素,使用索引方法即可。
>>> b.itemsize
8
>>> b.data
<read-write buffer for 0x0000000002D34DF0, size 32, offset 0 at 0x0000000002D5FEA0>
3.1 创建数组
数组的创建方法有几种,使用array()函数,参数为单层或嵌套列表。
>>> c = np.array([[1, 2, 3],[4, 5, 6]])
>>> c
array([[1, 2, 3],
[4, 5, 6]])
除了列表,array()函数还可以接收嵌套元组或元组列表作为参数。
>>> d = np.array(((1, 2, 3),(4, 5, 6)))
>>> d
array([[1, 2, 3],
[4, 5, 6]])
此外,参数可以是由元组或列表组成的列表,其效果相同。
>>> e = np.array([(1, 2, 3), [4, 5, 6], (7, 8, 9)])
>>> e
array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
3.2 数据类型
NumPy数组能够包含多种数据类型(见表3-1)。例如,可以使用字符串类型:
>>> g = np.array([['a', 'b'],['c', 'd']])
>>> g
array([['a', 'b'],
['c', 'd']],
dtype='|S1')
>>> g.dtype
dtype('S1')
>>> g.dtype.name
'string8'
NumPy所支持的数据类型:
3.3 dtype选项
array()函数可以接收多个参数。每个ndarray()对象都有一个与之相关联的dtype对象,该对象唯一定义了数组中每个元素的数据类型。array()函数默认根据列表或元素序列中各元素的数据类型,为ndarray()对象指定最适合的数据类型。可以用dtype选项作为函数array()的参数,明确指定dtype的类型。
例如,如要定义一个复数数组,可以像下面这样使用dtype选项:
>>> f = np.array([[1, 2, 3],[4, 5, 6]], dtype=complex)
>>> f
array([[ 1.+0.j, 2.+0.j, 3.+0.j],
[ 4.+0.j, 5.+0.j, 6.+0.j]])
3.4 自带的数组创建方法
NumPy库有几个函数能够生成包含初始值的N维数组,数组元素因函数而异。
例如,zeros()函数能够生成由shape参数指定维度信息、元素均为零的数组。举个例子,下述代码会生成一个3×3型的二维数组:
>>> np.zeros((3, 3))
array([[ 0., 0., 0.],
[ 0., 0., 0.],
[ 0., 0., 0.]])
ones()函数与上述函数相似,生成一个各元素均为1的数组。
>>> np.ones((3, 3))
array([[ 1., 1., 1.],
[ 1., 1., 1.],
[ 1., 1., 1.]])
这两个函数默认使用float64数据类型创建数组。NumPy arange()函数特别有用。它根据传入的参数,按照特定规则,生成包含一个数值序列的数组。例如,如果要生成一个包含数字0到9的数组,只需传入标识序列结束的数字作为参数即可。
下面的例子使用了两个参数,其实如上所述,只传入一个参数即可,序列默认从0开始。
>>> np.arange(0, 10)
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
如果不想以0作为起始值,可自己指定,这时需要使用两个参数:第一个为起始值,第二个为结束值。
>>> np.arange(4, 10)
array([4, 5, 6, 7, 8, 9])
还可以生成等间隔的序列。如果为arange()函数指定了第三个参数,它表示序列中相邻两个值之间的差距有多大。也被称作步长。
>>> np.arange(0, 12, 3)
array([0, 3, 6, 9])
此外,第三个参数还可以是浮点型。
这点就与Python的range()函数有所不同了,range()函数只可以使用整数作为步长。
>>> np.arange(0, 6, 0.6)
array([ 0. , 0.6, 1.2, 1.8, 2.4, 3. , 3.6, 4.2, 4.8, 5.4])
到目前为止,所创建的都是一维数组。如果要生成二维数组,仍然可以使用arange()函数,但是要结合reshape()函数。后者按照指定的形状,把一维数组拆分为不同的部分。
>>> np.arange(0, 12).reshape(3, 4)
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
另外一个跟arange()函数非常相似的函数是linspace()。它的前两个参数同样是用来指定序列的起始和结尾,但第三个参数不再表示相邻两个数字之间的距离,而是用来指定我们想把由开头和结尾两个数字所指定的范围分成几个部分。
>>> np.linspace(0,10,5)
array([ 0. , 2.5, 5. , 7.5, 10. ])
最后,来讲讲另外一个创建包含初始值的数组的方法:使用随机数填充数组。可以使用numpy.random模块的random()函数,数组所包含的元素数量由参数指定。
>>> np.random.random(3)
array([ 0.78610272, 0.90630642, 0.80007102])
每次用random()函数生成的数组,其元素均会有所不同。若要生成多维数组,只需把数组的大小作为参数传递给它。
>>> np.random.random((3,3))
array([[ 0.07878569, 0.7176506 , 0.05662501],
[ 0.82919021, 0.80349121, 0.30254079],
[ 0.93347404, 0.65868278, 0.37379618]])
4 基本操作
数组的各种运算方法
4.1 算术运算符
数组的第一类运算是使用算术运算符进行的运算。最显而易见的是为数组加上或乘以一个标量。
>>> a = np.arange(4)
>>> a
array([0, 1, 2, 3])
>>> a+4
array([4, 5, 6, 7])
>>> a*2
array([0, 2, 4, 6])
这些运算符还可以用于两个数组的运算。在NumPy中,这些运算符为元素级。也就是说,它们只用于位置相同的元素之间,所得到的运算结果组成一个新的数组。运算结果在新数组中的位置跟操作数位置相同。
>>> b = np.arange(4,8)
>>> b
array([4, 5, 6, 7])
>>> a + b
array([ 4, 6, 8, 10])
>>> a – b
array([–4, –4, –4, –4])
>>> a * b
array([ 0, 5, 12, 21])
此外,这些运算符还适用于返回值为NumPy数组的函数。可以用数组a乘上数组b的正弦值或平方根。
>>> a * np.sin(b)
array([–0. , –0.95892427, –0.558831 , 1.9709598 ])
>>> a * np.sqrt(b)
array([ 0. , 2.23606798, 4.89897949, 7.93725393])
对于多维数组,这些运算符仍然是元素级。
>>> A = np.arange(0, 9).reshape(3, 3)
>>> A
array([[0, 1, 2],
[3, 4, 5],
[6, 7, 8]])
>>> B = np.ones((3, 3))
>>> B
array([[ 1., 1., 1.],
[ 1., 1., 1.],
[ 1., 1., 1.]])
>>> A * B
array([[ 0., 1., 2.],
[ 3., 4., 5.],
[ 6., 7., 8.]])
4.2 矩阵积
选择使用*号作为元素级运算符是NumPy库比较奇怪的一点。事实上,在很多其他数据分析工具中,*在用于两个矩阵之间的运算时指的是矩阵积(mastrix produet)。而NumPy用dot()函数表示这类乘法,注意,它不是元素级的。
>>> np.dot(A,B)
array([[ 3., 3., 3.],
[ 12., 12., 12.],
[ 21., 21., 21.]])
所得到的数组中每个元素为,第一个矩阵中与该元素行号相同的元素与第二个矩阵中与该元素列号相同的元素,两两相乘后再求和。
描述矩阵积中两个元素的计算过程
矩阵积的另外一种写法是把dot()函数当作其中一个矩阵对象的方法。
>>> A.dot(B)
array([[ 3., 3., 3.],
[ 12., 12., 12.],
[ 21., 21., 21.]])
由于矩阵积计算不遵循交换律,运算对象的顺序很重要。AB不等于BA。
>>> np.dot(B,A)
array([[ 9., 12., 15.],
[ 9., 12., 15.],
[ 9., 12., 15.]])
4.3 自增和自减运算符
Python没有++或–运算符。对变量的值进行自增与自减,需要使用+=或-=运算符。这两个运算符跟前面见过的只有一点不同,运算得到的结果不是赋给一个新数组而是赋给参与运算的数组自身。
>>> a = np.arange(4)
>>> a
array([0, 1, 2, 3])
>>> a += 1
>>> a
array([1, 2, 3, 4])
>>> a –= 1
>>> a
array([0, 1, 2, 3])
这类运算符比每次只能加1的自增运算符用途更广。修改数组元素的值而不想生成新数组时,就可以使用它们。
array([0, 1, 2, 3])
>>> a += 4
>>> a
array([4, 5, 6, 7])
>>> a *= 2
>>> a
array([ 8, 10, 12, 14])
4.4 通用函数
通用函数(universal function)通常叫作ufunc,它对数组中的各个元素逐一进行操作。这表明,通用函数分别处理输入数组的每个元素,生成的结果组成一个新的输出数组。输出数组的大小跟输入数组相同。
三角函数等很多数学运算符合通用函数的定义,例如,计算平方根的sqrt()函数、用来取对数的log()函数和求正弦值的sin()函数。NumPy实现了很多通用函数。
>>> a = np.arange(1, 5)
>>> a
array([1, 2, 3, 4])
>>> np.sqrt(a)
array([ 1. , 1.41421356, 1.73205081, 2. ])
>>> np.log(a)
array([ 0. , 0.69314718, 1.09861229, 1.38629436])
>>> np.sin(a)
array([ 0.84147098, 0.90929743, 0.14112001, –0.7568025 ])
4.5 聚合函数
聚合函数是指对一组值(比如一个数组)进行操作,返回一个单一值作为结果的函数。因而,求数组所有元素之和的函数就是聚合函数。ndarray类实现了多个这样的函数。
>>> a = np.array([3.3, 4.5, 1.2, 5.7, 0.3])
>>> a.sum()
15.0
>>> a.min()
0.29999999999999999
>>> a.max()
5.7000000000000002
>>> a.mean()
3.0
>>> a.std()
2.0079840636817816