2022年 11月 5日

Python数据分析基础教程(二、NumPy数组)

1、NumPy数组对象

NumPy中的 ndarray 是一个多维数组对象,该对象由两部分组成:

实际的数据;

描述这些数据的元数据;

大部分的数组操作仅仅修改元数据部分,而不改变底层的实际数据。

 ndarray 支持更高的维度的数组。

NumPy数组一般是同质的(但有一种特殊的数组类型例外,它是异质的),即数组中的所有元素类型必须是一致的。这样有一个好处:如果我们知道数组中的元素均为同一类型,该数组所需的存储空间就很容易确定下来。

与Python中一样,NumPy数组的下标也是从 0 开始的。数组元素的数据类型用专门的对象表示。

2、np.arange()创建一维数组

  1. # coding=utf-8
  2. import numpy as np
  3. #NumPy创建0~4的数组
  4. a = np.arange(5)
  5. print("a的值为:",a)
  6. #查看a的数据类型
  7. print("a的类型为:",a.dtype)
  8. #输出a的向量的维度
  9. print("a的向量的维度为:",a.shape)

输出:

a的值为: [0 1 2 3 4]
a的类型为: int32
a的向量的维度为: (5,)

 这是一个包含5个元素的向量,取值分别为0~4的整数。

使用32位的Python,得到的结果是 int32。

数组的 shape 属性返回一个元组(tuple),元组中的元素即为NumPy数组每一个维度上的大小。

上面例子中的数组是一维的,因此元组中只有一个元素。

3、创建多维数组

  1. # coding=utf-8
  2. import numpy as np
  3. #创建一个二维数组
  4. #将 arange 函数创建的数组作为列表元素,把这个列表作为参数传给 array 函数,从而创建了一个2×2的数组
  5. m = np.array([np.arange(2),np.arange(2)])
  6. print("m的值为:",m)
  7. print("m的类型为:",m.dtype)
  8. print("m的向量维度为:",m.shape)

 输出为:

m的值为: [[0 1]
 [0 1]]
m的类型为: int32
m的向量维度为: (2, 2)

 array 函数可以依据给定的对象生成数组。给定的对象应是类数组,如Python中的列表。在上面的例子中,我们传给 array 函数的对象是一个NumPy数组的列表。像这样的类数组对象是array 函数的唯一必要参数,其余的诸多参数均为有默认值的可选参数。

4、选取数组元素

  1. # coding=utf-8
  2. import numpy as np
  3. #创建二维数组,注意外层还有一个中括号,否则无法解析类型
  4. a = np.array([[1,2],[3,4]])
  5. '''
  6. 1 2
  7. 3 4
  8. '''
  9. print(a[0,0], end='')
  10. print(a[0,1])
  11. print(a[1,0], end='')
  12. print(a[1,1])

注意查看数据时,并不是a[0][0]这样,而是a[0,0]这样。

输出为:

12
34

5、NumPy 数据类型

Python支持的数据类型有整型、浮点型以及复数型,但这些类型不足以满足科学计算的需求,因此NumPy添加了很多其他的数据类型。在实际应用中,我们需要不同精度的数据类型,它们占用的内存空间也是不同的。在NumPy中,大部分数据类型名是以数字结尾的,这个数字表示其在内存中占用的位数。

bool
用一位存储的布尔类型(值为 TRUE 或 FALSE )
inti
由所在平台决定其精度的整数(一般为 int32 或 int64 )
int8
整数,范围为128至127
int16
整数,范围为32 768至32 767
int32
整数,范围为2 31 至2 31 1
int64
整数,范围为2 63 至2 63 1
uint8
无符号整数,范围为0至255
uint16
无符号整数,范围为0至65 535
uint32
无符号整数,范围为0至2 32 1
uint64
无符号整数,范围为0至2 64 1
float16
半精度浮点数(16位):其中用1位表示正负号,5位表示指数,10位表示尾数
float32
单精度浮点数(32位):其中用1位表示正负号,8位表示指数,23位表示尾数
float64 或 float 双精度浮点数(64位):其中用1位表示正负号,11位表示指数,52位表示尾数
complex64
复数,分别用两个32位浮点数表示实部和虚部
complex128 或 complex 复数,分别用两个64位浮点数表示实部和虚部

 在NumPy中,许多函数的参数中可以指定数据类型,通常这个参数是可选的。

6、数据类型对象

#数据类型对象可以给出单个数组元素在内存中占用的字节数
print(a.dtype.itemsize)

7、 动手实践:创建自定义数据类型

  1. # coding=utf-8
  2. import numpy as np
  3. #自定义商品库存的数据类型(商品名称、商品个数、商品价格)
  4. t = np.dtype([('name',str,40),('numitems',int),('price',float)])
  5. #输出类型
  6. print(t)
  7. #查看某个字段的数据类型
  8. print(t['name'])
  9. items = np.array([('小猪佩奇',20,3.5),('白酒',30,80.5),('LOL俄洛依皮肤',1,210.5)])
  10. #没有指定数据类型,默认价格为浮点数类型
  11. print(items)
  12. #指定数据类型
  13. items2 = np.array([('小猪佩奇',20,3.5),('白酒',30,80.5),('LOL俄洛依皮肤',1,210.5)],dtype=t)
  14. print(items2)

 输出为:

[(‘name’, ‘<U40’), (‘numitems’, ‘<i4’), (‘price’, ‘<f8’)]
<U40
[[‘小猪佩奇’ ’20’ ‘3.5’]
 [‘白酒’ ’30’ ‘80.5’]
 [‘LOL俄洛依皮肤’ ‘1’ ‘210.5’]]
[(‘小猪佩奇’, 20,    3.5) (‘白酒’, 30,   80.5) (‘LOL俄洛依皮肤’,  1,  210.5)]

我们创建了一种自定义的异构数据类型,该数据类型包括一个用字符串记录的名字、一个用整数记录的数字以及一个用浮点数记录的价格。

8、一维数组的索引和切片

  1. # coding=utf-8
  2. import numpy as np
  3. #创建0~8的数组
  4. a = np.arange(9)
  5. #用下标 3 ~ 7 来选取元素 3 ~ 6
  6. print(a[3:7])
  7. #也可以用下标 0 ~ 7 ,以 2 为步长选取元素
  8. print(a[:7:2])
  9. #和Python中一样,我们也可以利用负数下标翻转数组
  10. print(a[::-1])

输出为:

[3 4 5 6]
[0 2 4 6]
[8 7 6 5 4 3 2 1 0]

 9、多维数组的索引和切片

  1. # coding=utf-8
  2. import numpy as np
  3. #用 arange 函数创建一个数组并改变其维度,使之变成一个三维数组
  4. #reshape 函数的作用是改变数组的“形状”,也就是改变数组的维度,其参数为一个正整数元组,分别指定数组在每个维度上的大小。
  5. a = np.arange(24).reshape(2,3,4)
  6. #查看各维度
  7. print(a.shape)
  8. #多维数组 b 中有 0 ~ 23 的整数,共 24 个元素,是一个2×3×4的三维数组
  9. #我们可以形象地把它看做一个两层楼建筑,每层楼有12个房间,并排列成3行4列。
  10. print(a)
  11. #我们可以用三维坐标来选定任意一个房间,即楼层、行号和列号。例如,选定第1层楼、第1行、第1列的房间(也可以说是第0层楼、第0行、第0列,这只是习惯问题)
  12. print(a[0,0,0])
  13. #如果我们不关心楼层,也就是说要选取所有楼层的第1行、第1列的房间,那么可以将第1个下标用英文标点的冒号 : 来代替
  14. print(a[:,0,0])
  15. #我们还可以这样写,选取第1层楼的所有房间
  16. print(a[0,:,:])
  17. #多个冒号可以用一个省略号( ... )来代替,因此上面的代码等价于
  18. print(a[0, ...])
  19. #进而可以选取第1层楼、第2排的所有房间
  20. print(a[0,1])
  21. #再进一步,我们可以在上面的数组切片中间隔地选定元素
  22. print(a[0,1,::2])
  23. #如果要选取所有楼层的位于第2列的房间,即不指定楼层和行号,用如下代码即可
  24. print(a[...,1])
  25. #类似地,我们可以选取所有位于第2行的房间,而不指定楼层和列号:
  26. print(a[:,1])
  27. #如果要选取第1层楼的所有位于第2列的房间,在对应的两个维度上指定即可
  28. print(a[0,:,1])
  29. #如果要选取第1层楼的最后一列的所有房间,使用如下代码
  30. print(a[0,:,-1])
  31. #如果要反向选取第1层楼的最后一列的所有房间,使用如下代码
  32. print(a[0,::-1,-1])
  33. #在该数组切片中间隔地选定元素
  34. print(a[0,::2,-1])
  35. #如果在多维数组中执行翻转一维数组的命令,将在最前面的维度上翻转元素的顺序,在我们的例子中将把第1层楼和第2层楼的房间交换
  36. print(a[::-1])

输出为:

(2, 3, 4)
[[[ 0  1  2  3]
  [ 4  5  6  7]
  [ 8  9 10 11]]

 [[12 13 14 15]
  [16 17 18 19]
  [20 21 22 23]]]
0
[ 0 12]
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]]
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]]
[4 5 6 7]
[4 6]
[[ 1  5  9]
 [13 17 21]]
[[ 4  5  6  7]
 [16 17 18 19]]
[1 5 9]
[ 3  7 11]
[11  7  3]
[ 3 11]
[[[12 13 14 15]
  [16 17 18 19]
  [20 21 22 23]]

 [[ 0  1  2  3]
  [ 4  5  6  7]
  [ 8  9 10 11]]]

9、改变数组的维度 

用 ravel 、 flatten 、 reshape 和 resize 函数对NumPy数组的维度进行修改

  1. # coding=utf-8
  2. import numpy as np
  3. #创建数据并标识维度
  4. a = np.arange(24).reshape(2,3,4)
  5. print(a)
  6. #ravel函数将数组展平,不过不分配内存,只是起到显示的作用
  7. print(a.ravel())
  8. #flatten函数将数组展平,与ravel函数功能相同,不过, flatten 函数会请求分配内存来保存结果,而 ravel 函数只是返回数组的一个视图(view)
  9. print(a.flatten())
  10. #用元组设置维度,除了可以使用 reshape 函数,我们也可以直接用一个正整数元组来设置数组的维度
  11. a.shape = (6,4)
  12. print(a)
  13. # transpose转置矩阵
  14. print(a.transpose())
  15. # resize 和 reshape 函数的功能一样,但 resize 会直接修改所操作的数组
  16. a.resize(2,12)
  17. print(a)

输出为:

[[[ 0  1  2  3]
  [ 4  5  6  7]
  [ 8  9 10 11]]

 [[12 13 14 15]
  [16 17 18 19]
  [20 21 22 23]]]
[ 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23]
[ 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23]
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]
 [12 13 14 15]
 [16 17 18 19]
 [20 21 22 23]]
[[ 0  4  8 12 16 20]
 [ 1  5  9 13 17 21]
 [ 2  6 10 14 18 22]
 [ 3  7 11 15 19 23]]
[[ 0  1  2  3  4  5  6  7  8  9 10 11]
 [12 13 14 15 16 17 18 19 20 21 22 23]]

 10、数组的组合

NumPy数组有水平组合、垂直组合和深度组合等多种组合方式,我们将使用 vstack 、dstack 、 hstack 、 column_stack 、 row_stack 以及 concatenate 函数来完成数组的组合。

  1. # coding=utf-8
  2. import numpy as np
  3. a = np.arange(9).reshape(3,3)
  4. print(a)
  5. b = a * 2
  6. print(b)
  7. print()
  8. '''
  9. 1、水平组合
  10. 将 ndarray 对象构成的元组作为参数,传给hstack 函数
  11. '''
  12. print("水平组合方法一:")
  13. print(np.hstack((a,b)))
  14. #也可以用 concatenate 函数来实现同样的效果
  15. print("水平组合方法二:")
  16. print(np.concatenate((a,b),axis = 1))
  17. print()
  18. '''
  19. 2、垂直组合
  20. 垂直组合同样需要构造一个元组作为参数,只不过这次的函数变成了vstack
  21. '''
  22. print("垂直组合方法一:")
  23. print(np.vstack((a,b)))
  24. #同样,我们将 concatenate 函数的 axis 参数设置为0即可实现同样的效果。这也是 axis 参数的默认值
  25. print("垂直组合方法二:")
  26. print(np.concatenate((a,b),axis = 0))
  27. print()
  28. '''
  29. 3、深度组合
  30. 将相同的元组作为参数传给 dstack 函数,即可完成数组的深度组合。
  31. 所谓深度组合,就是将一系列数组沿着纵轴(深度)方向进行层叠组合。
  32. 举个例子,有若干张二维面内的图像点阵数据,我们可以将这些图像数据沿纵轴方向层叠在一起,这就形象地解释了什么是深度组合。
  33. '''
  34. print("垂直组合方法:")
  35. print(np.dstack((a,b)))
  36. print()
  37. '''
  38. 4、列组合
  39. column_stack 函数对于一维数组将按列方向进行组合
  40. '''
  41. print("列组合:")
  42. oned = np.arange(2)
  43. print(oned)
  44. twice_oned = 2 * oned
  45. print(twice_oned)
  46. print(np.column_stack((oned,twice_oned)))
  47. #而对于二维数组, column_stack 与 hstack 的效果是相同的
  48. print(np.column_stack((a,b)))
  49. #我们可以用 == 运算符来比较两个NumPy数组
  50. print(np.column_stack((a,b)) == np.hstack((a,b)))
  51. '''
  52. 5、行组合
  53. row_stack对于两个一维数组,将直接层叠起来组合成一个二维数组。
  54. '''
  55. print("行组合:")
  56. print(np.row_stack((oned,twice_oned)))
  57. #对于二维数组, row_stack 与 vstack 的效果是相同的
  58. print(np.row_stack((a,b)))

结果:

[[0 1 2]
 [3 4 5]
 [6 7 8]]
[[ 0  2  4]
 [ 6  8 10]
 [12 14 16]]

水平组合方法一:
[[ 0  1  2  0  2  4]
 [ 3  4  5  6  8 10]
 [ 6  7  8 12 14 16]]
水平组合方法二:
[[ 0  1  2  0  2  4]
 [ 3  4  5  6  8 10]
 [ 6  7  8 12 14 16]]

垂直组合方法一:
[[ 0  1  2]
 [ 3  4  5]
 [ 6  7  8]
 [ 0  2  4]
 [ 6  8 10]
 [12 14 16]]
垂直组合方法二:
[[ 0  1  2]
 [ 3  4  5]
 [ 6  7  8]
 [ 0  2  4]
 [ 6  8 10]
 [12 14 16]]

垂直组合方法:
[[[ 0  0]
  [ 1  2]
  [ 2  4]]

 [[ 3  6]
  [ 4  8]
  [ 5 10]]

 [[ 6 12]
  [ 7 14]
  [ 8 16]]]

列组合:
[0 1]
[0 2]
[[0 0]
 [1 2]]
[[ 0  1  2  0  2  4]
 [ 3  4  5  6  8 10]
 [ 6  7  8 12 14 16]]
[[ True  True  True  True  True  True]
 [ True  True  True  True  True  True]
 [ True  True  True  True  True  True]]
行组合:
[[0 1]
 [0 2]]
[[ 0  1  2]
 [ 3  4  5]
 [ 6  7  8]
 [ 0  2  4]
 [ 6  8 10]
 [12 14 16]]

 11、数组的分割

NumPy数组可以进行水平、垂直或深度分割,相关的函数有 hsplit 、 vsplit 、 dsplit 和split 。

我们可以将数组分割成相同大小的子数组,也可以指定原数组中需要分割的位置。

  1. # coding=utf-8
  2. import numpy as np
  3. a = np.arange(9).reshape(3,3)
  4. print(a)
  5. print("水平分割:")
  6. #下面的代码将把数组沿着水平方向分割为3个相同大小的子数组
  7. print(np.hsplit(a,3))
  8. #调用 split 函数并在参数中指定参数 axis=1 ,结果一样
  9. print(np.split(a,3,axis = 1))
  10. print()
  11. #vsplit 函数将把数组沿着垂直方向分割
  12. print("垂直分割:")
  13. print(np.vsplit(a,3))
  14. #同样,调用 split 函数并在参数中指定参数 axis=0 ,也可以得到同样的结果
  15. print(np.split(a,3,axis = 0))
  16. print()
  17. #dsplit 函数将按深度方向分割数组
  18. print("深度分割")
  19. c = np.arange(27).reshape(3,3,3)
  20. print(c)
  21. print(np.dsplit(c,3))

结果为:

[[0 1 2]
 [3 4 5]
 [6 7 8]]
水平分割:
[array([[0],
       [3],
       [6]]), array([[1],
       [4],
       [7]]), array([[2],
       [5],
       [8]])]
[array([[0],
       [3],
       [6]]), array([[1],
       [4],
       [7]]), array([[2],
       [5],
       [8]])]

垂直分割:
[array([[0, 1, 2]]), array([[3, 4, 5]]), array([[6, 7, 8]])]
[array([[0, 1, 2]]), array([[3, 4, 5]]), array([[6, 7, 8]])]

深度分割
[[[ 0  1  2]
  [ 3  4  5]
  [ 6  7  8]]

 [[ 9 10 11]
  [12 13 14]
  [15 16 17]]

 [[18 19 20]
  [21 22 23]
  [24 25 26]]]
[array([[[ 0],
        [ 3],
        [ 6]],

       [[ 9],
        [12],
        [15]],

       [[18],
        [21],
        [24]]]), array([[[ 1],
        [ 4],
        [ 7]],

       [[10],
        [13],
        [16]],

       [[19],
        [22],
        [25]]]), array([[[ 2],
        [ 5],
        [ 8]],

       [[11],
        [14],
        [17]],

       [[20],
        [23],
        [26]]])]
 

 12、数组的属性

  1. # coding=utf-8
  2. import numpy as np
  3. a = np.arange(24).reshape(2,12)
  4. print(a)
  5. #ndim 属性,给出数组的维数,或数组轴的个数
  6. print("a的维度为:",a.ndim)
  7. # size 属性,给出数组元素的总个数
  8. print("a的元素个数为:",a.size)
  9. #itemsize 属性,给出数组中的每个元素在内存中所占的字节数
  10. print("a的元素占用内存字节数:",a.itemsize)
  11. #整个数组所占的存储空间,可以用 nbytes 属性来查看。这个属性的值其实就是 itemsize 和 size 属性值的乘积
  12. print("a所有元素所占存储空间:",a.nbytes)
  13. # flat 属性将返回一个 numpy.flatiter 对象,这是获得 flatiter 对象的唯一方式
  14. # 我们无法访问 flatiter 的构造函数。
  15. # 这个所谓的“扁平迭代器”可以让我们像遍历一维数组一样去遍历任意的多维数组
  16. b = np.arange(4).reshape(2,2)
  17. print(b)
  18. f = b.flat
  19. for item in f:
  20. print(item,end='')
  21. print()
  22. #还可以用 flatiter 对象直接获取一个数组元素
  23. print(a.flat[2])
  24. #获取多个元素
  25. print(a.flat[[1,3]])
  26. #flat 属性是一个可赋值的属性。对 flat 属性赋值将导致整个数组的元素都被覆盖
  27. b.flat = 7
  28. print(b)
  29. b.flat[[1,3]] = 1
  30. print(b)

输出为:

[[ 0  1  2  3  4  5  6  7  8  9 10 11]
 [12 13 14 15 16 17 18 19 20 21 22 23]]
a的维度为: 2
a的元素个数为: 24
a的元素占用内存字节数: 4
a所有元素所占存储空间: 96
[[0 1]
 [2 3]]
0123
2
[1 3]
[[7 7]
 [7 7]]
[[7 1]
 [7 1]]

 13、数组转换成列表

  1. c = np.arange(4)
  2. #将NumPy数组转换成Python列表
  3. c.tolist()
  4. print(c)
  5. #astype函数可以在转换的时候指定数据类型
  6. c.astype(int)
  7. print(c)

输出:

[0 1 2 3]
[0 1 2 3]