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

2021-10-23  本文已影响0人  Hwcoder

访问博客查看 本文 最新内容,排版更美观ヾ(•ω•`)o 如有错误欢迎指出~

Python 系列学习笔记:

学习 Machine Learning 的时候发现需要用许多矩阵运算和画图的库,本文将以实用主义的方式记录每次遇到的新用法。

2021 年贵系的暑培新增了「科学计算」内容,本文部分内容参考了清华 LZJ 同学的教程,部分参考自 NumPy 中文官网 参考手册。本文将持续更新。

常用的库

计算机领域,有用的数据特指结构化的数据,即符合一定的语法规范,才能方便计算机处理。这些数据包括表格型的数据,多维数组型的数据,由键位列关联起来的多张表数据等等。针对这些数据,Python 都有相应库去处理。

数据处理

科学计算

数据可视化

自动化办公

NumPy 基础

NumPy 底层是用 C 写的,所以可以很方便的操作内存,这点与 Python 的原生数据结构不同。因此,NumPy 在处理大型数据时速度更快,内存开销也更小(和原生 Python 相比)。

ndarray 对象

ndarray 是 NumPy 的核心数据结构,全称是 N-dimensional Array,N 维数组,一个快速、灵活的大型数据集容器,内部储存同一类型的数据。

ndarray 有两个重要属性,dtypeshape,分别表示元素的数据类型形状参数,此外还有 ndim (维数), size (元素个数), itemsize (每个元素字节数) 等属性。

类型 描述
int16/uint16 有符号和无符号 16 位整数
int32/uint32 有符号和无符号 32 位整数
int64/uint64 有符号和无符号 64 位整数
float32 标准单精度浮点数,兼容 C 的 float
float64 标准双精度浮点数,兼容 C 的 double
float128 拓展精度浮点数
complex128 基于 float64 的复数
bool 布尔值,为 True 或 False
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 三维数组

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 可通过一些自带的函数改变形状(维度),以便完成一些复杂的矩阵运算。

常用数学函数

ndarray 的另一特点是支持类标量语法的计算,标量会作用在数组的每一个元素上,例如:arr + 2arr * 10arr[:] = 2 等。此外,还支持一系列运算函数,以下所有函数同样支持 数组变量.函数名(参数) 形式调用:

普通运算

矩阵运算

线性代数

统计运算

NumPy 进阶

matrix 对象

除了 ndarray,NumPy 针对二维数组还专门设置了一个矩阵对象。matrix 的大部分性质与 ndarray 无异,但多了一些功能函数:

Broadcast 广播机制

NumPy 中对于两个 ndarray 的加减乘除都是标量操作,即对应位置元素之间的操作。并且,当两个数组的形状不同时,NumPy 自带的 Broadcast 机制会自动扩展数组进行操作。

例如,在归一化变量时,我们使用:

# 元素减去平均值,除以无偏标准差
data = (data - data.mean(0)) / data.std(axis=0, ddof=1)

很明显,data 和塌缩后的 np.mean(data, 0) 形状不相同,但仍可以相减。

广播兼容的条件有两种:

上一篇 下一篇

猜你喜欢

热点阅读