本文整理自《Python数据分析实战》
1. ndarray:NumPy库的心脏
1.1 array()函数
最常用于创建数组的方法是使用array()
函数,参数为单层或嵌套列表:
# 单层列表
>>> np.array([1,2,3])
array([1, 2, 3])
# 嵌套列表
>>> np.array([[1,2,3],[4,5,6]])
array([[1, 2, 3],
[4, 5, 6]])
参数还可以是嵌套元组或元组列表:
# 嵌套元组
>>> np.array(((1,2,3),(4,5,6)))
array([[1, 2, 3],
[4, 5, 6]])
# 元组列表
>>> np.array([(1,2,3),(4,5,6)])
array([[1, 2, 3],
[4, 5, 6]])
此外,参数可以是元组或列表组成的列表:
>>> np.array([(1,2,3),[4,5,6],(7,8,9)])
array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
1.2 数据类型
数据类型 | 描述 |
---|---|
bool_ | 以字节存储的布尔值(True 或 False) |
int_ | 默认的整数类型(和 C 的 long 一样,是 int64 或者 int32) |
intc | 和 C 的 int 相同(一般为 int64 或 int32) |
intp | 用于下标的整数(和 C 的 ssize_t 相同,一般为int64 或者 int32) |
int8 | 字节(-128 到 127) |
int16 | 整数(-32768 到 32767) |
int32 | 整数(-2147483648 到 2147483647) |
int64 | 整数(-9223372036854775808 到 9223372036854775807) |
uint8 | 无符号整数(0 到 255) |
uint16 | 无符号整数(0 到 65535) |
uint32 | 无符号整数(0 到 4294967295) |
uint64 | 无符号整数(0 到 18446744073709551615) |
float_ | float64 的简写 |
float16 | 半精度浮点:1位符号,5位指数,10位尾数 |
float32 | 单精度浮点:1位符号,8位指数,23位尾数 |
float64 | 双精度浮点:1位符号,11位指数,52位尾数 |
complex_ | complex128 的简写 |
complex64 | 由两个32位浮点(实部和虚部)组成的复数 |
complex128 | 由两个64位浮点(实部和虚部)组成的复数 |
1.3 dtype选项
array()
函数默认根据列表或元素序列中各元素的数据类型,为ndarray()
对象指定最合适的数据类型。可以用dtype
选项作为函数array()
的参数,明确指定dtype的类型。
比如要定义一个复数数组:
>>> np.array([[1,2,3],[4,5,6]],dtype=complex)
array([[ 1.+0.j, 2.+0.j, 3.+0.j],
[ 4.+0.j, 5.+0.j, 6.+0.j]])
1.4 自带的数组创建方法
NumPy库有几个函数能够生成包含初始值的N维数组:
- zeros()
- ones()
- arange()
- reshape()
- linspace()
- random()
zeros()
函数能够生成由shape参数指定维度信息、元素均为零的数组:
>>> np.zeros((3,3))
array([[ 0., 0., 0.],
[ 0., 0., 0.],
[ 0., 0., 0.]])
ones()
函数与zeros()
函数相似,生成元素均为1的数组:
>>> np.ones((3,3))
array([[ 1., 1., 1.],
[ 1., 1., 1.],
[ 1., 1., 1.]])
arange()
函数根据参数生成包含一个数值序列的数组:
# 生成从0到9的数组
>>> np.arange(10)
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
# 生成从4到9的数组
>>> np.arange(4,10)
array([4, 5, 6, 7, 8, 9])
# 按步长为2生成从0到9的数组
>>> np.arange(0,10,2)
array([0, 2, 4, 6, 8])
# 按步长为0.6生成从4到9的数组
>>> np.arange(4,10,0.6)
array([ 4. , 4.6, 5.2, 5.8, 6.4, 7. , 7.6, 8.2, 8.8, 9.4])
reshape()
函数通常结合arange()
函数使用,改变数组的型:
# 将一维数组a变为3*4的数组
>>> a=np.arange(12).reshape((3,4))
>>> a
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
# 将a变回为一维数组
>>> a.reshape((12,))
array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
linspace()
函数跟arange()
函数相似,前两个参数指定序列的开头和结尾,第三个参数指定将数组拆分成几个部分:
>>> np.linspace(0,10,5)
array([ 0. , 2.5, 5. , 7.5, 10. ])
random()
函数使用随机数填充数组:
# 生成一维数组
>>> np.random.random(3)
array([ 0.0092522 , 0.44961339, 0.85684498])
# 生成多维数组
>>> np.random.random((3,3))
array([[ 0.50311642, 0.25961784, 0.30587642],
[ 0.55388356, 0.92739877, 0.26140058],
[ 0.63482092, 0.45938232, 0.84053653]])
2. 基本操作
2.1 算术运算符
算术运算符可以用于数组和标量之间:
>>> a=np.arange(4)
>>> a
array([0, 1, 2, 3])
>>> a+4
array([4, 5, 6, 7])
>>> a-4
array([-4, -3, -2, -1])
>>> a*4
array([ 0, 4, 8, 12])
>>> a/2
array([0, 0, 1, 1])
还可以用于两个数组之间,这两个数组的元素位置必须相同,也就是具有相同的型:
>>> 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])
>>> a/b
array([0, 0, 0, 0])
此外,算术运算符还适用于返回值为NumPy数组的函数:
>>> 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(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.]])
2.2 矩阵积
NumPy用dot()
函数表示矩阵积:
>>> np.dot(A,B)
array([[ 3., 3., 3.],
[ 12., 12., 12.],
[ 21., 21., 21.]])
另外一种写法:
>>> A.dot(B)
array([[ 3., 3., 3.],
[ 12., 12., 12.],
[ 21., 21., 21.]])
矩阵积不遵循交换律:
>>> B.dot(A)
array([[ 9., 12., 15.],
[ 9., 12., 15.],
[ 9., 12., 15.]])
2.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])
a*=2
>>> a
array([0, 2, 4, 6])
2.4 通用函数
通用函数对数组中的各个元素逐一进行操作,生成的结果组成一个新的输出数组。
三角函数等许多数学运算符合通用函数的定义:
>>> 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 ])
2.5 聚合函数
聚合函数对一组值操作,返回一个单一值作为结果:
>>> a=np.array([3.3,4.5,1.2,5.7,0.3])
>>> a
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
3. 索引机制、切片和迭代方法
3.1 索引机制
3.1.1 一维数组
要获取数组的单个元素,指定元素的索引即可:
>>> a=np.arange(10,16)
>>> a
array([10, 11, 12, 13, 14, 15])
>>> a[4]
14
还可以用负数作为索引,和Python序列的负数索引一致:
>>> a[-1]
15
>>> a[-5]
11
方括号里传入多个索引值,可以同时选择多个元素:
>>> a[[1,3,5]]
array([11, 13, 15])
3.1.2 二维数组
要获取或选取矩阵中的元素,索引值为[行索引,列索引]
:
>>> A=np.arange(10,19).reshape((3,3))
>>> A
array([[10, 11, 12],
[13, 14, 15],
[16, 17, 18]])
>>> A[1,2]
15
3.2 切片操作
3.2.1 一维数组
切片操作指抽取数组的一部分元素生成新数组,该数组是指向相同缓冲区的视图。
语法格式:
a[行索引:列索引:步长]
# 行索引缺省:默认为0
# 列索引缺省:默认为该数组的索引最大值
# 步长缺省:默认为1
如想抽取或查看数组的一部分:
>>> a=np.arange(10,16)
>>> a
array([10, 11, 12, 13, 14, 15])
>>> a[1:5]
array([11, 12, 13, 14])
>>> a[1:5:2]
array([11, 13])
>>> a[::2]
array([10, 12, 14])
>>> a[:5:2]
array([10, 12, 14])
>>> a[:5:]
array([10, 11, 12, 13, 14])
3.2.2 二维数组
二维数组需要分别指定行和列的索引值。
语法格式:
A[行索引:列索引:步长 , 行索引:列索引:步长]
例如:
>>> A=np.arange(10,19).reshape((3,3))
>>> A
array([[10, 11, 12],
[13, 14, 15],
[16, 17, 18]])
# 抽取第一行
>>> A[0,:]
array([10, 11, 12])
# 抽取第一列
>>> A[:,0]
array([10, 13, 16])
# 抽取一个范围
>>> A[0:2,0:2]
array([[10, 11],
[13, 14]])
# 抽取不连续的行或列,要把索引放在数组里
>>> A[[0,2],0:2]
array([[10, 11],
[16, 17]])
3.3 数组迭代
可以使用for
语句对数组迭代:
>>> a=np.arange(10)
>>> a
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> for i in a:
... print i
...
0
1
2
3
4
5
6
7
8
9
如果想遍历矩阵的每个元素,可以用for
语句遍历A.flat
:
>>> for item in A.flat:
... print item
...
10
11
12
13
14
15
16
17
18
NumPy还提供了一个处理循环的方法apply_along_axis()
函数。这个函数接收三个参数:
apply_along_axis(聚合函数或通用函数,轴,数组)
# 如果axis选项的值为0,按列进行迭代;值为1,则按行
例如先求每一列的平均数,再求每一行的平均数:
>>> np.apply_along_axis(np.mean,axis=0,arr=A)
array([ 13., 14., 15.])
>>> np.apply_along_axis(np.mean,axis=1,arr=A)
array([ 11., 14., 17.])
如果使用通用函数,实际上是按照指定的轴逐元素遍历数组,所以按行或列迭代结果是一样的:
>>> def foo(x):
... return x/2
...
>>> np.apply_along_axis(foo,axis=1,arr=A)
array([[5, 5, 6],
[6, 7, 7],
[8, 8, 9]])
>>> np.apply_along_axis(foo,axis=0,arr=A)
array([[5, 5, 6],
[6, 7, 7],
[8, 8, 9]])
4. 条件和布尔数组
如果对数组使用条件运算符,将会得到由布尔值组成的数组:
>>> A=np.random.random((4,4))
>>> A
array([[ 0.86118588, 0.35409283, 0.7896701 , 0.57151539],
[ 0.36440236, 0.46306654, 0.61989782, 0.94423436],
[ 0.63706687, 0.85048501, 0.82146301, 0.13815874],
[ 0.83226105, 0.83114022, 0.35471327, 0.64492001]])
>>> A<0.5
array([[False, True, False, False],
[ True, True, False, False],
[False, False, False, True],
[False, False, True, False]], dtype=bool)
可以把条件表达式放在方括号中,将会返回一个满足条件的元素组成的新数组:
>>> A[A<0.5]
array([ 0.35409283, 0.36440236, 0.46306654, 0.13815874, 0.35471327])
5. 形状变换
除了通过reshape()
函数改变数组的形状之外,还可以通过将表示新形状的元组直接赋给数组的shape
属性:
>>> a=np.arange(12)
>>> a
array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
>>> a.shape=(3,4)
>>> a
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
可以使用ravel()
函数将数组转为一维数组:
>>> a=a.ravel()
>>> a
array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
或者直接改变数组shape
的值:
>>> a.shape=(12)
>>> a
array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
另外还能用transpose()
函数实现交换行列位置的矩阵转置:
>>> a.shape=(3,4)
>>> a
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
>>> a.transpose()
array([[ 0, 4, 8],
[ 1, 5, 9],
[ 2, 6, 10],
[ 3, 7, 11]])
6. 数组操作
6.1 连接数组
NumPy使用栈的概念来连接数组。vstack()
函数执行垂直入栈,hstack()
函数执行水平入栈:
>>> A=np.ones((3,3))
>>> B=np.zeros((3,3))
# 垂直连接
>>> np.vstack((A,B))
array([[ 1., 1., 1.],
[ 1., 1., 1.],
[ 1., 1., 1.],
[ 0., 0., 0.],
[ 0., 0., 0.],
[ 0., 0., 0.]])
# 水平连接
>>> np.hstack((A,B))
array([[ 1., 1., 1., 0., 0., 0.],
[ 1., 1., 1., 0., 0., 0.],
[ 1., 1., 1., 0., 0., 0.]])
还有两个用于多个数组之间栈操作的函数是column_stack()
函数和row_stack()
函数。这两个函数一般将一维数组作为列或行压入栈,以形成一个新的二维数组:
>>> a=np.array([0,1,2])
>>> b=np.array([3,4,5])
>>> c=np.array([6,7,8])
# 以列连接
>>> np.column_stack((a,b,c))
array([[0, 3, 6],
[1, 4, 7],
[2, 5, 8]])
# 以行连接
>>> np.row_stack((a,b,c))
array([[0, 1, 2],
[3, 4, 5],
[6, 7, 8]])
6.2 数组切分
连接数组的逆操作就是把一个数组分成几部分。水平切分用hsplit()
函数,垂直切分用vsplit()
函数:
>>> A=np.arange(16).reshape((4,4))
>>> A
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11],
[12, 13, 14, 15]])
# 水平切分
>>> [B,C]=np.hsplit(A,2)
>>> B
array([[ 0, 1],
[ 4, 5],
[ 8, 9],
[12, 13]])
>>> C
array([[ 2, 3],
[ 6, 7],
[10, 11],
[14, 15]])
# 垂直切分
>>> [B,C]=np.vsplit(A,2)
>>> B
array([[0, 1, 2, 3],
[4, 5, 6, 7]])
>>> C
array([[ 8, 9, 10, 11],
[12, 13, 14, 15]])
split()
函数可以把数组分为几个不对称的部分,需要指定被切分部分的索引。指定axis=1
时,索引是列索引;指定axis=0
时,是行索引:
>>> [A1,A2,A3]=np.split(A,[1,3],axis=1)
>>> A1
array([[ 0],
[ 4],
[ 8],
[12]])
>>> A2
array([[ 1, 2],
[ 5, 6],
[ 9, 10],
[13, 14]])
>>> A3
array([[ 3],
[ 7],
[11],
[15]])