『Python 干货』#2 NumPy(简明)

访问博客查看 本文 最新内容,排版更美观ヾ(•ω•`)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 有两个重要属性,dtypeshape,分别表示元素的数据类型形状参数,此外还有 ndim (维数), size (元素个数), itemsize (每个元素字节数) 等属性。

  • arr.dtypenp.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.shapenp.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 + 2arr * 10arr[:] = 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.Tmat1.Hmat1.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)

你可能感兴趣的:(『Python 干货』#2 NumPy(简明))