访问博客查看 本文 最新内容,排版更美观ヾ(•ω•`)o 如有错误欢迎指出~
Python 系列学习笔记:
- Python笔记 #1 基础语法
- Python笔记 #2 NumPy
- Python笔记 #3 Matplotlib
学习 Machine Learning 的时候发现需要用许多矩阵运算和画图的库,本文将以实用主义的方式记录每次遇到的新用法。
2021 年贵系的暑培新增了「科学计算」内容,本文部分内容参考了清华 LZJ 同学的教程,部分参考自 NumPy 中文官网 参考手册。本文将持续更新。
常用的库
计算机领域,有用的数据特指结构化的数据,即符合一定的语法规范,才能方便计算机处理。这些数据包括表格型的数据,多维数组型的数据,由键位列关联起来的多张表数据等等。针对这些数据,Python 都有相应库去处理。
数据处理:
NumPy:是 Numerical Python 的缩写,用于处理大规模多维数组,矩阵计算等,有成熟的 C 接口,很多 Python 的第三方库都基于 NumPy 实现(例如 Python 的计算机视觉库 cv2 )。
Pandas:提供了更高级的数据结构和函数,适用于处理表格化、结构化的数据。
科学计算:
- SciPy:Python 的科学计算库,可以处理积分微分、线性代数、最优化问题、信号处理、统计学等问题。
- Scikit-learn:Python 的机器学习工具包,可以处理机器学习的众多计算问题,如支持向量机,聚类,特征选择等问题。
- Statmodels:处理统计学和经济学问题的库,与 scikit-learn 相比包含更多经典模型。
数据可视化:
- Matplotlib:最经典的二维数据可视化库,结合 NumPy 使用可以成为 Matlab 的有力替代品。
自动化办公:
docx:处理 Doc 文档。
pptx:处理 Powerpoint 幻灯片。
openpyxl:处理 Excel 表格。
NumPy 基础
NumPy 底层是用 C 写的,所以可以很方便的操作内存,这点与 Python 的原生数据结构不同。因此,NumPy 在处理大型数据时速度更快,内存开销也更小(和原生 Python 相比)。
ndarray 对象
ndarray 是 NumPy 的核心数据结构,全称是 N-dimensional Array,N 维数组,一个快速、灵活的大型数据集容器,内部储存同一类型的数据。
ndarray 有两个重要属性,dtype
和 shape
,分别表示元素的数据类型和形状参数,此外还有 ndim
(维数), size
(元素个数), itemsize
(每个元素字节数) 等属性。
-
arr.dtype
、np.dtype(arr)
:返回arr
的数据类型,常见类型如下:
类型 | 描述 |
---|---|
int16/uint16 | 有符号和无符号 16 位整数 |
int32/uint32 | 有符号和无符号 32 位整数 |
int64/uint64 | 有符号和无符号 64 位整数 |
float32 | 标准单精度浮点数,兼容 C 的 float |
float64 | 标准双精度浮点数,兼容 C 的 double |
float128 | 拓展精度浮点数 |
complex128 | 基于 float64 的复数 |
bool | 布尔值,为 True 或 False |
-
arr2 = arr1.astype(np.float64)
:改变原数据类型为目标类型。 -
arr.shape
、np.shape(arr)
:返回arr
的形状参数,通常赋值给变量(m, n)
,不同的参数含义如下:
arr |
arr.shape |
arr.dtype |
含义 |
---|---|---|---|
array(1) | () |
int32 | 单个整数 |
array(1.) | () |
float64 | 单个浮点数 |
array([1, 2]) | (2,) |
int32 | 一维数组,2 个元素 |
array([[1], [2]]) |
(2, 1) |
int32 | 二维数组,2 行 1 列 |
array([[1, 2]]) | (1, 2) |
int32 | 二维数组,1 行 2 列 |
array([[1, 2], [3, 4]]) |
(2, 2) |
int32 | 二维数组,2 行 2 列 |
array([[[1, 1], [1, 1]], [[1, 1], [1, 1]]]) |
(2, 2, 2) |
int32 | 三维数组 |
-
arr.shape[0]
、arr.shape[1]
:返回arr
的第 0 维、第 1 维的长度。 -
arr.size
:返回元素个数,通常赋值给变量m
,用于生成等长的数组。
ndarray 构造
根据不同的需求,有各种构造 ndarray 的方法。
- 已有容器转化数组
生成 ndarray 的基本函数是 np.array()
,其参数可以是 list 、tuple 或 另一个 ndarray,会自动识别类型。其函数接口如下:
"""
obj: 传入原数组
dtype: 指定数据类型,默认根据原数组推断为 int32 或 float64
copy: 是否拷贝(不共享内存)
order: 数组存储风格,C 按行优先,F 按列优先,K 自适应
ndmin: 最小维数
"""
np.array(object, dtype = None, copy = True, order = 'K', subok = False, ndmin = 0)
样例测试如下:
# list
arr1 = np.array([1, 2, 3]) # arr1.shape = (3,)
arr2 = np.array([[1, 2], [3, 4]]) # arr2.shape = (2, 2)
# tuple
arr3 = np.array((1, 2, 3)) # arr3.shape = (3,)
arr4 = np.array(((1, 2), (3, 4))) # arr4.shape = (2, 2)
# float
arr5 = np.array([1, 2, 3.14]) # arr5.dtype = float64
arr6 = np.array([1, 2, 3], dtype = np.float64)
# merge
arr7 = np.array([arr1, arr5]) # arr7.shape = (2, 3)
- 生成已初始化数组
NumPy 自带初始化函数,用来生成默认数组,以下为样例:
arr1 = np.zeros((2, 3)) # 全 0. 数组,arr1.shape = (2, 3)
arr2 = np.ones(10) # 全 1. 数组,arr2.shape = (10,)
arr3 = np.empty((3, 4, 5)) # 未初始化数组,arr3.shape = (3, 4, 5)
需要注意的是,传入的实参为生成数组的 shape
,必须用元组表示!且生成的数组默认类型都为 float64。
若要生成高维数组,除了用 np.array()
拼接两个低维数组,通常都是用 np.empty()
实现。
- 基于范围生成数组
生成方式与 Matlab 类似,这里给出函数接口:
# 范围在 [start, stop) 之间,缺省其一则视为 [0, stop),步长为 step
np.arange(start, stop, step, dtype)
# 生成等差数列,[start, stop] 之间,限定总数为 num,endpoint 表示是否包含 stop 端点
np.linspace(start, stop, num=50, endpoint=True)
# 生成等比数列,注意范围是 [base^start, base^stop] 之间,限定总数为 num
np.logspace(start, stop, num=50, endpoint=True, base=10.0)
- 生成随机数组
常用的有三种方式,这里给出函数接口:
# 生成 0 到 1 之间的浮点数,参数为各个维度
np.random.rand(d0, d1, ..., dn)
# 生成 [low, high) 之间的整数,缺省其一则视为 [0, high),size 用元组表示
np.random.randint(low, high, size=None, dtype=int)
# 生成 均值为 0,方差为 1 的正态分布浮点数,参数为各个维度
np.random.randn(d0, d1, ..., dn)
- 从外部文件导入
最常用的方式,从外部文件导入大量数据集,这里列出样例:
# 导入 .txt 文件,每行作为数组的一行,分隔符为逗号,选中前 2 列
data = np.loadtxt('ex1data1.txt', delimiter=',', usecols=(0, 1))
x = data[:, 0] # 第 0 列作为自变量
y = data[:, 1] # 第 1 列作为因变量
m = y.size # 元素组数定义为 m
当 .txt 文件有空缺数据时,需要使用复杂度更高的函数:
# 导入有空缺的 .txt 文件,每行作为数组的一行,分隔符为逗号,选中前 2 列
data = np.genfromtxt('ex1data1.txt', delimiter=',', usecols=(0, 1))
导入其他格式文件或许需要用到其他辅助库:
import numpy as np
import scipy.io as scio
# 导入 .mat 文件需要用到 scipy.io 模块,这是一个二进制字典文件
data = scio.loadmat('ex6data1.mat')
X = data['X'] # 获取字典键 'X'
y = data['y'].flatten() # 获取字典键 'y',展开成一维
m = y.size
ndarray 索引
ndarray 容器的赋值主要用索引完成,其基本特性:浅拷贝,即多个对象共用一块内存。NumPy 官方解释是,NumPy 主要用于处理大量数据,所以不希望用缺省复制的方式,否则会引起各种内存问题。
索引语法和原生 Python 类似,用数字和冒号表示左闭右开范围:
# 索引 [5,8) 位
arr1 = np.arange(6) # 内存中 [0, 1, 2, 3, 4, 5]
arr2 = arr1[3:4]
# 缺省,索引全部值
arr2[:] = 6 # 内存中 [0, 1, 2, 6, 6, 5]
# 如果不想索引原数组
arr3 = arr1[1:2].copy() # 用 .copy() 可以强制拷贝
对于高维数组,如果想通过赋值实现降维,可以使用切片索引:
arr1 = np.array([[1, 2, 3], [4, 5, 6]])
print(arr1[1][0], arr1[1, 0]) # 4 4,前者为 Python 列表索引
print(arr1[:1, 0:]) # [[1 2 3]]
print(arr1[:, 0]) # [1 4]
print(arr1[:, :1]) # [[1] [4]]
由第一个例子注意到,不同于原生 Python,高维数组还可以通过 [x,y,z]
的方式在不同维度之间索引,同时还可以用冒号加数字表示左闭右开范围。
特别注意最后两个例子,在第二维上虽然范围都是 0,但前者只用了数字,后者还用了冒号,导致结果的维数并不相同。这说明:使用冒号不会降维,哪怕确实范围中只有一列。
此外,NumPy 中还有一种布尔索引的有趣用法,不是本文重点,这里举个例子:
# 按 50% 概率,随机抽取序列
arr = np.arange(10) # 生成 待抽取 的序列
p = np.random.rand(10) # 生成 0 - 1 之间的随机小数
print(arr[p < 0.5]) # 利用标量语法,转化为 bool 数组,再索引 arr
# 浓缩成一行
print(np.arange(10)[np.random.rand(10) < 0.5])
ndarray 变形
ndarray 可通过一些自带的函数改变形状(维度),以便完成一些复杂的矩阵运算。
-
arr.flatten()
:返回一份深拷贝的展开成一维的数组,默认按行优先展开order='C'
,用于矩阵时默认展开到(n,1)
。 -
arr[:,np.newaxis]
:将一维arr
增加到二维数组(m,1)
。常用于生成列向量,才能点乘系数矩阵。 -
arr[np.newaxis,:]
:将一维arr
增加到二维数组(1,m)
。 -
np.c_[arr1, arr2]
:将二维数组按列相连,要求行数一致。如果对象是一维数组,视作(m,1)
的二维数组。-
np.column_stack((arr1, arr2))
:类似np.c_
的函数形式,要求传入元组。
-
-
np.r_[arr1, arr2]
:将二维数组按行相连,要求列数一致。如果对象是一维数组或常数,则仍拼成一维数组。-
np.row_stack((arr1, arr2))
:类似np.r_
的函数形式,要求传入元组。
-
-
np.reshape(arr,(5,5))
:调整形状,默认按行展开后填到新形状,要求规模匹配。 -
np.set_printoptions(formatter={'float': '{: 0.6f}'.format})
:设置全局精度。
常用数学函数
ndarray 的另一特点是支持类标量语法的计算,标量会作用在数组的每一个元素上,例如:arr + 2
、arr * 10
、arr[:] = 2
等。此外,还支持一系列运算函数,以下所有函数同样支持 数组变量.函数名(参数)
形式调用:
普通运算
-
np.sum(arr[1,:])
:对一维数组arr[1,:]
,求和,得到一个值。 -
np.sum(arr, 0)
:对二维数组arr
,求每一列和,塌缩成(列数,)
。 -
np.sum(arr, 1)
:对二维数组arr
,求每一行和,塌缩成(行数,)
。 -
np.max(arr)
:对整个数组,求最大值,得到一个值。 -
np.max(arr, 0)
:对二维数组arr
,求每一列最大值,塌缩成(列数,)
。 -
np.argmax(arr,0)
:对二维数组arr
,求每一列最大值的索引,塌缩成(列数,)
。 -
np.sin(arr)
、np.cos(arr)
、np.tan(arr)
:作用于每个元素求三角函数。 -
np.power(arr, n)
:作用于每个元素求n
次幂。
矩阵运算
-
arr1 @ arr2
:矩阵点乘,要求第一个矩阵的列数等于第二个矩阵的行数,一维数组视作二维数组的行向量或列向量。-
np.dot(arr1, arr2)
:同上,矩阵点乘 -
np.matmul(arr1, arr2)
:同上,矩阵点乘,支持高维矩阵的 Broadcast。
-
-
arr1 * arr2
:对应位置相乘,要求两个矩阵各个维度长度相等。-
np.multiply(arr1, arr2)
:同上,对应位置相乘。
-
-
arr1.T
:求数组转置,数组没有求逆、共轭的函数。-
np.transpose(arr1)
:同上,求数组转置,高维数组需指定索引。
-
线性代数
-
np.linalg.inv(arr)
:求逆矩阵,要求二维数组。 -
np.linalg.pinv(arr)
:求伪(广义)逆矩阵,要求二维数组。 -
np.linalg.det(arr)
:求行列式(标量),要求二维数组。 -
np.linalg.solve(A,b)
:解形如 Ax=b 线性方程组。 -
np.linalg.eigvals(arr)
:求矩阵特征值。 -
np.linalg.svd(arr)
:求矩阵的 SVD 分解。 -
np.linalg.norm(arr, axis=0, ord=1)
:按列求向量 1 范数。 -
np.linalg.norm(arr, axis=0, ord=2)
:按列求向量 2 范数。 -
np.linalg.norm(arr, axis=0, ord=np.inf)
:按列求向量无穷范数。
统计运算
-
np.mean(arr)
:求所有样本平均值。 -
np.mean(arr, 0)
:对二维数组arr
,求每一列样本平均值,塌缩成(列数,)
。 -
np.average(arr, weights=None)
:求样本加权平均值。 -
np.std(arr)
:求所有样本标准差。 -
np.std(arr, ddof=1)
:求所有样本无偏标准差。 -
np.std(arr, axis=0, ddof=1)
:对二维数组arr
,求每一列样本无偏标准差,塌缩成(列数,)
。 -
np.var(arr)
:求所有样本方差,相当于标准差的平方。 -
np.cov(arr)
:对一维数组arr
,相当于求自身的协方差(退化为无偏方差),即无偏标准差的平方。 -
np.cov(arr)
:对二维数组arr
,返回一个二维数组,元素[i,j]
代表arr[i]
与arr[j]
两列元素的协方差。
NumPy 进阶
matrix 对象
除了 ndarray,NumPy 针对二维数组还专门设置了一个矩阵对象。matrix 的大部分性质与 ndarray 无异,但多了一些功能函数:
-
np.mat([[1,2],[5,7]])
:声明矩阵,注意维数必须为 2。 -
np.mat([1,2,3])
:会被强制转化为(1,3)
的二维矩阵。 -
np.mat(arr1)
、np.asmatricx(arr1)
:从数组转到矩阵。 -
np.asarray(mat1)
:从矩阵转到数组。 -
mat1.T
、mat1.H
、mat1.I
:求转置矩阵、共轭矩阵、逆矩阵。 -
mat1 * mat2
:点乘,要求第一个矩阵的列数等于第二个矩阵的行数。 -
np.multiply(mat1, mat2)
:对应位置相乘,要求两个矩阵各个维度长度相等。
Broadcast 广播机制
NumPy 中对于两个 ndarray 的加减乘除都是标量操作,即对应位置元素之间的操作。并且,当两个数组的形状不同时,NumPy 自带的 Broadcast 机制会自动扩展数组进行操作。
例如,在归一化变量时,我们使用:
# 元素减去平均值,除以无偏标准差
data = (data - data.mean(0)) / data.std(axis=0, ddof=1)
很明显,data
和塌缩后的 np.mean(data, 0)
形状不相同,但仍可以相减。
广播兼容的条件有两种:
- 两个不同维度的数组的后缘维度(trailing dimension,即从末尾开始算起的维度)的轴长度相符。如:
(4,3)
与(3,)
;(5,6,7)
与(6,7)
。 - 两个相同维度的数组,但其中在某一维度上仅为 1。如
(4,5,6)
与(4,1,6)
、(4,5,1)
。