概述

作为Python的一种科学计算的拓展程序库,Numpy(Numerical Python) 提供了更加强大的N维数组对象nparray ,用于存储以及处理矩阵,提供了线性代数、傅里叶变换和随机数生成函数

引入方式:

1
import numpy as np

nparray 是 Numpy 提供的N维数组对象(内部的值不应该理解为坐标!)

N维数组是机器学习和神经网络的主要数据结构

只有当该数组对象中的所有元素类型相同,才能充分发挥 Numpy 优势

其特点:

  1. 不需要再对数组每一元素进行遍历运算
  2. 设置专门的数组对象(底层由C实现),经过优化,提升运算速度
  3. 由于科学计算中,一个维度所有数据的类型往往相同,而提供的数组对象采用相同的数据类型,节省运算和存储空间

Numpy更加详细的操作可参阅:光城同学的笔记

数组创建

创建基本数组

格式:x = np.array(list/tuple, dtype=np.类型)

0维:即标量,如 3.14

一维数组:比如一个特征向量

1
2
3
4
5
a = np.array([0, 1, 2, 3, 4]) 
print(type(a)) # <class 'numpy.ndarray'>
b = np.array([9, 8, 7, 6, 5])
c = a ** 2 + b ** 3 # 维度相同,直接运算
print(c) # 结果为[729 513 347 225 141]

二维数组:比如一个样本-特征矩阵

1
2
3
4
5
6
7
8
d = np.array([[0, 1, 2, 3, 4], [9, 8, 7, 6, 5]])
print(d.shape) # (2, 5)
print(d.ndim) # 维度:2
print(d.size) # 2*5=10
d = np.array([[0, 1, 2, 3, 4],
[5, 6, 7, 8, 9],
[10, 11, 12, 13, 14]])
print(d.ndim) # 维度仍然是二维

三维数组:比如RGB图片(宽×\times×\times通道(即长))

1
2
3
4
5
6
7
8
9
d = np.array([
[[0, 1, 2, 3, 4],
[5, 6, 7, 8, 9],
[10, 11, 12, 13, 14]],
[[0, 1, 2, 3, 4],
[5, 6, 7, 8, 9],
[10, 11, 12, 13, 14]]
])
print(d.ndim) # 维度是三维,因为访问的下标格式是:d[a][b][c]

创建特殊数组

  • 从数值范围创建数组:

    1
    e = np.arange(15) # 返回ndarray类型,元素从0至n-1
  • 创建全 1 数组:

    1
    2
    e = np.ones((3, 6))# 传入元组(3,6),返回3*6的全1数组
    e = np.ones_like(d)# 根据数组d的形状生成全1数组
  • 创建单位矩阵:

    1
    e = np.eye(5) # 创建n*n的单位矩阵,默认是浮点数类型
  • 类型转换:

    1
    e = e.astype(np.int64)  # 类型转换

索引与切片

索引:获取数组中特定位置元素的过程

切片:获取数组元素子集的过程

例如:

1
2
3
a = np.array([1, 2, 3, 4, 5, 6])
print(a[2:4]) # [3, 4] 一维数组的切片
print(a[1 : 4 : 2]) # [起始编号:终止编号(不含):步长],故返回[2,4]

高级索引:

在多维数组的切片中,使用 , 来区分维度

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
a = np.arange(24).reshape((2, 3, 4))
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]]]
'''
print(a[-1, -2, -3])
# 返回17
print(a[:, 1, -3])
# 不考虑第一维度,第二维度索引为1,第三维度索引为-3的元素,故返回[ 5 17]
print(a[:, 1:3, :])
# 只考虑第二维度在1到3的元素
print(a[:, :, ::2])
# 按步长为2返回元素

创建随机数组

1
2
3
4
5
6
a = np.random.rand(3, 4, 5) 
# 生成3*4*5数组,每一元素随机取自浮点数[0, 1),且满足均匀分布
a = np.random.randn(3, 4, 5)
# 生成3*4*5数组,每一元素随机取自浮点数[0, 1),满足正态分布
a = np.random.randint(800, 900, (3, 8))
# 生成3*8数组,范围取自[800, 900)

数组的算术运算

数组与标量间的运算

1
2
3
a = np.arange(24).reshape(3, 8)
a = a / a.mean() # 相当于对矩阵每一元素除以整个矩阵的平均值
b = a + 3 # 相当于对矩阵每一元素加3

数组之间的运算

1
2
3
4
5
6
a = np.array([10, 2, 9])
# print(np.square(a)) # 对数组每一元素取平方
b = np.array([9, 8, 7])
print(a + b) # [19 10 16]
print(a > b) # [ True False True]
print(np.maximum(a, b)) # [10 8 9]

Numpy 数组间的基础运算是一对一,当两数组的 shape() 相等,则两个数组相同位置的元素进行运算。

数组广播

若两数组的 shape() 不同,则会自动触发广播机制。如下:

1
2
3
4
5
6
a = np.array([[0, 0, 0],
[10, 10, 10],
[20, 20, 20],
[30, 30, 30]])
b = np.arange(3)
print(a + b)
1
print(np.arange(3) + 5)
1
print(np.ones((3,3)) + np.arange(3))
1
print(np.arange(3).reshape((3, 1)) + np.arange(3))

数据的保存及读入

CSV文件格式:以逗号作为分隔,只能存储一维/二维的数据

1
2
3
a = np.arange(100).reshape(5, 20)
np.savetxt('woc.txt', a, fmt='%.1f', delimiter=',') # 保存数据
b = np.loadtxt('woc.txt', dtype=np.int32, delimiter=',') # 读入数据

多维数据的存取:

1
2
3
4
5
a = np.arange(100).reshape(5, 10, 2)
# a.tofile("wtf.txt", sep=",", format='%d') # 逐一列出元素
a.tofile("a.dat", sep=",", format='%d') # 备份为二进制文件
b = np.fromfile("a.dat", dtype=np.int32, sep=",").reshape(5, 10, 2)
# 第三个参数count为读入元素个数,-1表示读入整个文件

保存与读入 .npy 文件:

1
2
np.save("wtf.npy", a)
b = np.load("wtf.npy")

Numpy统计函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
a = np.arange(15).reshape(3, 5)
'''
[[ 0 1 2 3 4]
[ 5 6 7 8 9]
[10 11 12 13 14]]
'''
print(np.sum(a)) # 105
print(np.mean(a)) # 7.0
print(np.mean(a, axis=0))
# axis=0,代表逐列计算(每一列各自运算),所以得到[5. 6. 7. 8. 9.]
print(np.mean(a, axis=1))
# axis=1,代表逐行计算(每一行各自运算),所以得到[ 2. 7. 12.]
print(np.average(a, axis=0, weights=[10, 5, 1]))
# 加权运算 [2.1875 3.1875 4.1875 5.1875 6.1875]
print(np.max(a)) # 14
print(np.argmax(a))
# 最大值的坐标(扁平化后):14(即第14个元素)
print(np.unravel_index(np.argmax(a), a.shape))
# 将扁平化坐标转换为多维下标:(2, 4)

梯度函数

一维数组:

1
2
3
4
5
6
#XY坐标轴连续三个X坐标对应的Y轴值:a,b,c,其中b的梯度是(c-a)/2
a = np.random.randint(0, 20, (5))
print(a)
# [12 11 19 15 8]
print(np.gradient(a))
# [-1. 3.5 2. -5.5 -7. ]

二维数组:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
a = np.random.randint(0, 10, (3, 5))
print(a)
'''
[[1 2 3 3 5]
[1 9 6 6 2]
[5 2 8 7 6]]
'''
print(np.gradient(a)) # 由于是二维的数值,在计算梯度时需要考虑两个方向
'''
[
array([[ 0. , 7. , 3. , 3. , -3. ],
[ 2. , 0. , 2.5, 2. , 0.5],
[ 4. , -7. , 2. , 1. , 4. ]]),
array([[ 1. , 1. , 0.5, 1. , 2. ],
[ 8. , 2.5, -1.5, -2. , -4. ],
[-3. , 1.5, 2.5, -1. , -1. ]])
]
'''