前言
想了解 Numpy 的人基本上都是要和数据打交道的,Numpy对数据操作的方法多,底层也是使用 C 实现的,也就是说Numpy处理数据的速度是比较快的,这也体现了 Python 胶水语言的特性。Numpy 也被称为机器学习三剑客之一,另外的就是 Pandas 和 Matplotlib 了,虽然当前有诸如 scikit-learn 机器学习包以及 Pytorch、TensorFlow 深度学习框架,这些包和框架都少不了对数据的操作,当然也少不了对数据进行预处理,这些包和框架也支持与 Numpy 中的数据格式(ndarray)进行交互,所以我认为学好 Numpy 的操作也有利于更深入地了解一些高级的包和框架的使用。
对于 Numpy 来说,官方文档内容相当多,并且是英文的,难道我们需要全部学习一遍吗?我想,如果经常在数据处理领域中摸爬滚打的话,是需要的,但是我也相信二八定理,我们经常使用的也就是 Numpy 中的 20%左右,至于剩下的内容,需要我们在业余时间补回来,在需要的时候能够快速想起来,不需要做的特别熟练,如果特别熟练就更好了。下面的内容是自己总结的,方便自己看,也希望方便大家看。
程序运行环境:window10 Python3.7 (Anaconda) Numpy 1.18.1
文档参考:官方文档[1],中文文档[2]
1 安装与导入
如果安装了 Anaconda,就不需要再安装Numpy了,在安装 Anaconda 的时候就安装了 Numpy 及其相关的包。当然你可以使用如下命令安装:
conda install numpy
或者:
pip install numpy
在 Python 编程社区中,大家会对经常使用的包设置一个都比较认可的别名,Numpy 的别名是 np,使用别名编程也更加简洁,实际编程导入如下:
import numpy as np
当然,你也可以自定义别名,但是为了使得代码更具可读性可交流性,最好还是使用大家比较认可的别名。
先了解一下 Numpy 中的数据类型:
array = np.array([1, 2, 3, 4]) # 从一个list中创建Numpy类型的数据
print(type(array)) #
即使 Numpy 中只有一个元素,其也是 ndarray 的数据类型。其实也可理解普通的 number(int, float)类型可看为是标量,而 ndarray 数据类型是数组、向量或矩阵。
2 创建 ndarray 类型数据及相关信息
通常,可以从 list 类型的数据进行创建,也可以从 pandas 中 dataframe 类型中获取,生成一个 array,注:一个 ndarray数据类型都是相同的(底层 c 语言处理,速度快),否则会按照 int->float(np.float)->str(object)进行类型转换。
array1 = np.array([[1,2,3],
[4,5,6],
[7,8,9]]) # 使用一个二维的list生成一个二维的ndarray
array2 = np.array([10, 11, 12, 13]) # 创建一个1维的ndarray
2.1 常用属性
查看数组的形状,在矩阵计算时,数据的维度要满足矩阵计算要求。
print(array1.shape, array2.shape) # 返回元组类型 (3, 3) (4,)
查看数组的维度,
print(array1.ndim, array2.ndim) # 2 1
查看元素的数据类型,需要满足一定的精度
print(array1.dtype) # 默认为int32类型(根据机器决定)
查看数组中共有多少个数据,
print(array1.size) # 9
补充: 在创建 ndarray 的时候,可以选择使用什么样的数据类型,如使用 float 类型:
array3 = np.array([1, 3, 5], dtype=np.float)
print(array3.dtype) # float64
2.2 创建特殊类型的 ndarray
在进行矩阵计算时,可以快速创建如全 0 矩阵,单位矩阵,全 1 矩阵,并指定对应的维度等。
在一定范围内创建等间隔的 ndarray,该方法类型与 Python 中的 range 函数相似,但更强大。
array = np.arange(10, 15, 0.5, dtype=np.float)
print(array) # [10. 10.5 11. 11.5 12. 12.5 13. 13.5 14. 14.5]
拓展: 创建特殊函数以 10 为底的 log 对数
array = np.logspace(0, 1, 5) # [0, 1],10^0, 10^0.25 .. 10^1
print(array) # [ 1. 1.77827941 3.16227766 5.62341325 10. ]
快速创建行向量(不凸显矩阵特性,如果表型矩阵的话 shape 应为(1,4))
array = np.r_[0:2:0.5]
print(array) # [0. 0.5 1. 1.5]
print(array.shape) # (4,)
快速创建列向量(凸显矩阵特性)
array = np.c_[0:2:0.5]
print(array)
print(array.shape)
"""
[[0. ]
[0.5]
[1. ]
[1.5]]
(4, 1)
"""
创建全 0 矩阵,只有一个参数默认为行向量,如果有两个参数需用则使用元组的方式传递参数
array1 = np.zeros(3, dtype=np.float)
array2 = np.zeros((2, 3), dtype=np.float)
print(array1)
print(array2)
"""
[0. 0. 0.]
[[0. 0. 0.]
[0. 0. 0.]]
"""
创建全 1 矩阵,类似全 0 矩阵
array1 = np.ones(3, dtype=np.float)
array2 = np.ones((2, 3), dtype=np.float)
print(array1)
print(array2)
"""
[1. 1. 1.]
[[1. 1. 1.]
[1. 1. 1.]]
"""
创建一个在 2,10,之间有 6 个元素的 ndarray,并将其形状改为(2,3)
array = np.linspace(2, 10, 6)
print(array)
array = array.reshape(2, -1) # -1为占位,numpy可以推测后面的值
print(array)
"""
[ 2. 3.6 5.2 6.8 8.4 10. ]
[[ 2. 3.6 5.2]
[ 6.8 8.4 10. ]]
"""
创建一个单位矩阵
array = np.eye(5) # 等同于np.identity(5)
print(array)
"""
[[1. 0. 0. 0. 0.]
[0. 1. 0. 0. 0.]
[0. 0. 1. 0. 0.]
[0. 0. 0. 1. 0.]
[0. 0. 0. 0. 1.]]
"""
3 矩阵计算
矩阵计算涉及到矩阵对应位置元素操作,矩阵乘法操作。
3.1 单个矩阵操作
为矩阵所有元素加减乘除,取对数,取平方,取平方根等,还有正余弦,指数等。
array = np.arange(2, 5, 0.5, dtype=np.float).reshape(3, -1)
print(array)
print(2 + array)
print(array - 2)
print(array/2)
print(2*array)
print(np.log(array))
print(np.power(array, 2))
print(np.sqrt(array))
矩阵转置
array = np.arange(2, 5, 0.5, dtype=np.float).reshape(3, -1)
print(array.T)
print(np.transpose(array))
"""
[[2. 3. 4. ]
[2.5 3.5 4.5]]
[[2. 3. 4. ]
[2.5 3.5 4.5]]
"""
矩阵的数值运算
随机生成一个 shape 为(3,2),元素范围在 0-10 之间的矩阵,并计算整个矩阵各个元素的和、找出最大值、最小值,找到每行\列最大值和最小值以及各行\列的和。
np.random.seed(1) # 设置随机数种子,保证每次生成的随机数相同
array = 10*np.random.random((3, 2))
print(array)
print(array.sum()) # 整个矩阵各个元素的和
print(array.sum(axis=1)) # 各行的和
print(array.sum(axis=0)) # 各列的和
print(array.max()) # 整个矩阵中的最大值
print(array.max(axis=1)) # 各行的最大值
print(array.max(axis=0)) # 各列的最大值
# 最小值使用.min即可,操作如上
"""
[[4.17022005e+00 7.20324493e+00]
[1.14374817e-03 3.02332573e+00]
[1.46755891e+00 9.23385948e-01]]
16.788879311798276
[11.37346498 3.02446947 2.39094486]
[ 5.6389227 11.14995661]
7.203244934421581
[7.20324493 3.02332573 1.46755891]
[4.17022005 7.20324493]
"""
说明 :如果不设置 axis 维度参数的话,则都为整个 array 的元素来说,但一般运用都只是算某个维度的 sum,max,min。
矩阵中各个元素累乘,矩阵各元素的平均值,矩阵中值,矩阵标准差,矩阵方差,矩阵当前元素减去前面元素的差,每个元素变成当前元素+前面所有元素的和
array = np.arange(1, 13).reshape(3, -1)
print(array)
print(array.prod()) # 元素累乘
print(array.mean()) # 矩阵各元素平均值
print(np.median(array)) # 矩阵中值
print(array.std()) # 各元素标准差
print(array.var()) # 各元素方差
print(np.diff(array)) # 当前元素减去前面元素的差
print(np.cumsum(array)) # 每个元素变成当前元素+前面所有元素的和
"""
[[ 1 2 3 4]
[ 5 6 7 8]
[ 9 10 11 12]]
479001600
6.5
6.5
3.452052529534663
11.916666666666666
[[1 1 1]
[1 1 1]
[1 1 1]]
[ 1 3 6 10 15 21 28 36 45 55 66 78]
"""
根据条件修改矩阵数值:如比 4 小的全部为 4,比 8 大的全部为 8,四舍五入(可指定精度):
array = np.linspace(1, 10, 9, dtype=np.float).reshape(3, -1)
print(array)
print(np.clip(array, 4, 8))
# 对第一位小数点进行四舍五入,默认四舍五入到整数
print(array.round(decimals=1))
"""
[[ 1. 2.125 3.25 ]
[ 4.375 5.5 6.625]
[ 7.75 8.875 10. ]]
[[4. 4. 4. ]
[4.375 5.5 6.625]
[7.75 8. 8. ]]
[[ 1. 2.1 3.2]
[ 4.4 5.5 6.6]
[ 7.8 8.9 10. ]]
"""
矩阵求逆
a = np.arange(1, 10).reshape(3, 3)
print(a)
print(np.linalg.inv(a)) #
"""
[[1 2 3]
[4 5 6]
[7 8 9]]
[[ 3.15251974e+15 -6.30503948e+15 3.15251974e+15]
[-6.30503948e+15 1.26100790e+16 -6.30503948e+15]
[ 3.15251974e+15 -6.30503948e+15 3.15251974e+15]]
"""
3.2 两个矩阵操作
两个形状相同的矩阵对应元素操作,比较元素大小
array = np.arange(2, 5, 0.5, dtype=np.float).reshape(3, -1)
array1 = np.arange(3, 6, 0.5, dtype=np.float).reshape(3, -1)
print(array)
print(array1)
print(array1 + array)
print(array - array1)
print(array/array1)
print(array1*array) # 等同于 np.multiply(array1, array)
print(np.log(array))
print(np.power(array, array1))
print(array > 2.5)
print(array > array1)
矩阵相乘
array1 = np.arange(2, 10).reshape(2, -1)
array2 = np.arange(4, 12).reshape(-1, 2)
# 方式1
print(array1.dot(array2))
# 方式2
print(np.dot(array1, array2))
# 方式3
print(array1 @ array2)
4 切片和索引
4.1 获取指定值的索引
获取一个矩阵最大值、最小值以及非零索引。
array = np.array([[-6, 6, 1],
[-2, 1, 7],
[0, 2, 0]])
print(np.argmin(array)) # 获取矩阵最小值索引
print(np.argmax(array)) # 获取矩阵最大值索引
print(np.argmin(array, axis=0)) # 获取矩阵每列的最小值索引
print(np.nonzero(array)) # 可以理解第1个array代表行,第2个代表列
"""
0
5
[0 1 2]
(array([0, 0, 0, 1, 1, 1, 2], dtype=int64), array([0, 1, 2, 0, 1, 2, 1], dtype=int64))
4.2 获取矩阵块数据
获取矩阵中具体的某个值,获取矩阵指定行,获取矩阵指定列,获取指定的行和列。
array = np.array([[-6, 6, 1],
[-2, 1, 7],
[0, 2, 0]])
print(array[1,2], array[1][2]) # 获取索引为1行2列的值
print(array[1], array[:][1]) # 获取索引为1的行,array[:][1]:所有行中的第1行
print(array[:, 1]) # 索引为1的列,返回为一个行向量(确定只有1列返回行)
print(array[:, 1:2]) # 索引为1的列,返回为一个列向量(:则是切片,保持原来的维度)
"""
7 7
[-2 1 7] [-2 1 7]
[6 1 2]
[[6]
[1]
[2]]
"""
数据迭代,迭代矩阵的行或列。
array = np.array([[-6, 6, 1],
[-2, 1, 7],
[0, 2, 0]])
# 行迭代
for row in array:
print(row)
# 列迭代
for col in array.T:
print(col)
"""
[-6 6 1]
[-2 1 7]
[0 2 0]
[-6 -2 0]
[6 1 2]
[1 7 0]
"""
将矩阵降低一维(内存降低一维),对于二维矩阵来说,最后变成一个行向量
array = np.array([[-6, 6, 1],
[-2, 1, 7],
[0, 2, 0]])
print(array.flatten())
# [-6 6 1 -2 1 7 0 2 0]
4.3 使用 bool 索引进行切片
通过布尔类型选择数据, 布尔类型的矩阵也可以通过是矩阵的比较得到(ndarray > a, ndarray == ndarray 等)
a = np.arange(0, 100, 20)
mask = np.array([0, 0, 1, 0, -1], dtype=bool) # 0表示False,0之外的表示True
print(a)
print(mask)
print(a[mask]) # 通过布尔类型来选择数据
"""
[ 0 20 40 60 80]
[False False True False True]
[40 80]
"""
5 扩展与分解
5.1 矩阵合并
多个行向量合并,矩阵合并,
a = np.arange(1, 4)
b = np.arange(4, 7)
c = np.vstack((a, b, a, b)) # 垂直合并
print(c)
d = np.hstack((a, b, a, b)) # 水平合并,就是拼接
print(d)
c1 = np.vstack((a.T, b.T, a.T, b.T))
"""
[[1 2 3]
[4 5 6]
[1 2 3]
[4 5 6]]
[1 2 3 4 5 6 1 2 3 4 5 6]
"""
a = np.arange(1, 5).reshape(2, -1)
b = np.arange(5, 9).reshape(2, -1)
print(np.vstack((a,b))) # 垂直合并
print(np.hstack((a,b))) # 水平合并,就是拼接
"""
[[1 2]
[3 4]
[5 6]
[7 8]]
[[1 2 5 6]
[3 4 7 8]]
"""
补充: 对于行向量,使用转置方法返回还是行向量。
行向量转为列向量(矩阵)
a = np.arange(1, 5)
print(a.reshape(1, -1)) # 错误方法
print(a[:, np.newaxis]) # 转换成列向量
"""
[[1 2 3 4]]
[[1]
[2]
[3]
[4]]
"""
列向量的合并
a = np.arange(1, 5)[:, np.newaxis]
b = np.arange(5, 9)[:, np.newaxis]
c = np.concatenate((a, b, a)) # 列向量的拼接,矩阵的拼接, axis参数默认为0,在垂直方向拼接(行的拼接)
print(c)
d = np.concatenate((a, b, a), axis=1) # 在水平方向拼接(列的拼接)
print(d)
"""
[[1]
[2]
[3]
[4]
[5]
[6]
[7]
[8]
[1]
[2]
[3]
[4]]
[[1 5 1]
[2 6 2]
[3 7 3]
[4 8 4]]
"""
5.2 矩阵分解
将矩阵沿水平、垂直方向分解
a = np.arange(12).reshape(3, 4)
print(a)
# 均匀分开
print(np.split(a, 2, axis=1)) # 垂直方向(从上到下劈一刀)均匀(4/2=2)分成2份
print(np.split(a, 3, axis=0)) # 水平方向(从左到右劈三刀)均匀(3/1=3)分成3份
"""
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
[array([[0, 1],
[4, 5],
[8, 9]]), array([[ 2, 3],
[ 6, 7],
[10, 11]])]
[array([[0, 1, 2, 3]]), array([[4, 5, 6, 7]]), array([[ 8, 9, 10, 11]])]
"""
# 非均匀分开
# 垂直方向分三份,最多的在第一份, 方法同:np.vsplit(a, 3)
print(np.array_split(a, 3, axis=1))
# 水平方向分两份,最多的在第一份, 方法同:np.hsplit(a, 2)
print(np.array_split(a, 2, axis=0))
"""
[array([[0, 1],
[4, 5],
[8, 9]]), array([[ 2],
[ 6],
[10]]), array([[ 3],
[ 7],
[11]])]
[array([[0, 1, 2, 3],
[4, 5, 6, 7]]), array([[ 8, 9, 10, 11]])]
"""
将列向量转换为行向量
array = np.arange(12)[:, np.newaxis]
print(array.squeeze())
print(array.flatten())
# [ 0 1 2 3 4 5 6 7 8 9 10 11]
# [ 0 1 2 3 4 5 6 7 8 9 10 11]
print(array.reshape(3, 4).flatten()) # 可将高维数据亚平,但squeeze()不行
# [ 0 1 2 3 4 5 6 7 8 9 10 11]
6 随机模块
设置随机数种子,保持每次得到的数据一致
np.random.seed(10)
从 0-10 中随机选出 5 个随机数行向量
print(np.random.choice(11, size=5)) # 选出的元素可重复,但不可是
"""
[7 0 6 9 9]
"""
随机生成指定维度,满足 0-1 均匀分布的矩阵(当 rand()中无参数,则返回一个标量的随机数值)
print(np.random.rand(3, 2)) # 返回服从[0, 1)均匀分布的随机样本值
"""
[[0.8027575 0.09280081]
[0.51815255 0.86502025]
[0.82914691 0.82960336]]
"""
返回区间在[0, a)上指定维度的整数矩阵
print(np.random.randint(15, size=(2,3)))
"""
[[10 9 8]
[14 7 3]]
"""
返回区间在自定义区间上指定维度的整数矩阵
print(np.random.randint(10, 15, size=(2,3)))
"""
[[11 11 13]
[14 10 11]]
"""
mu = 0 # 均值
sigma = 1 # 方差
print(np.random.normal(mu, sigma, (2, 5)))
"""
[[ 0.37475197 -0.47338275 -0.45452082 -0.08533806 1.50318838]
[ 1.16064112 -0.4829414 -1.80662901 -0.91544761 -0.06973398]]
"""
对一数据,随机打散 ()
array = np.arange(10).reshape(2,5)
print(array)
np.random.shuffle(array)
print(array)
"""
[[0 1 2 3 4]
[5 6 7 8 9]]
[[5 6 7 8 9]
[0 1 2 3 4]]
"""
7 其它操作
在使用时需要注意,数据是否共有内存,避免修改一地方的值,另外一个地方的值也随之变化。
a = np.arange(5)
b = a
c = b
d = c # a,b,c,d指向同一数据地址
print(d is a, c is a, b is a) # True True True
d[0] = 100
print(a) # d改动,a随着变化
e = a.copy() # 拷贝一份a给e,a,e不共享内存
print(e is a)
e[0] = 20
print(e, a)
"""
True True True
[100 1 2 3 4]
False
[20 1 2 3 4] [100 1 2 3 4]
"""
矩阵元素排序
array = np.array([[-6, 6, 1],
[-2, 1, 7],
[0, 2, 0]])
# 默认对每行的数据从左至右,从小到大排序, 参数axis默认为0
print(np.sort(array))
print(np.sort(array, axis=1)) # 对列进行排序,从上到下,从小到大排序
"""
[[-6 1 6]
[-2 1 7]
[ 0 0 2]]
[[-6 1 0]
[-2 2 1]
[ 0 6 7]]
"""
后记
Numpy 能够操作的内容很多,本文也只是个人总结,很多表述也不一定正确,不过基本上能够应付日常操作了,更多操作也还需学习官方文档。当 Numpy、Pandas、Matplotlib 以及更多包配合起来就能够做出很多有意思的事情了。
参考资料
[1] 官方文档地址: https://numpy.org/
[2] 中文文档地址 : https://www.numpy.org.cn/user/