NumPy入门
NumPy(Numerical Python)是Python科学计算的基础库,提供了高性能的多维数组对象以及用于操作这些数组的工具。是几乎所有Python科学计算和数据分析库的底层依赖
它最核心的作用是用高效的多维数组来储存和计算数据
Python自带的列表也能存放数据,但是列表更偏向通用容器,而NumPy提供的数组更适合做数学计算
1. ndarray数组基础
ndarray是NumPy中最核心的数据结构,全称是"N-dimensional array",也就是N维数组,NumPy里的大部分操作,都是围绕ndarray展开的
1.1. 一维数组
一维数组可以理解为一排数据,相当于数学中的向量
用NumPy创建一维数组:
1 | import numpy as np |
输出:
1 | [1 2 3 4 5] |
1.2. 二维数组
二维数组可以理解为一个表格,相当于数学中的矩阵
用NumPy创建二维数组:
1 | import numpy as np |
输出:
1 | [[1 2 3] |
这是一个2行3列的二维数组
1.3. 三维数组
三维数组则可以理解为多个二维数组,这也是理解更多维度数组的一个思路
用NumPy创建三维数组:
1 | arr = np.array([ |
输出:
1 | [[[ 1 2 3] |
2个二维数组;每个二维数组有2行;每行有3列
1.4. ndarray的维度:ndim
ndim表示数组的维度数量
例子:
1 | arr1 = np.array([1, 2, 3]) |
输出:
1 | 1 |
1.5. ndarray的形状:shape
shape表示数组每个维度的大小
1 | arr = np.array([ |
输出:
1 | (2, 3) |
意思是2行3列
严格来说是:
axis 0 方向有 2 个元素
axis 1 方向有 3 个元素
1.6. ndarray的数据类型:dtype
dtype表示数组元素的数据类型
例子:
1 | arr = np.array([1, 2, 3]) |
输出:
1 | int64 |
如果是小数:
1 | arr = np.array([1.1, 2.2, 3.3]) |
输出可能是:
1 | float64 |
NumPy数组通常要求元素类型统一
创建数组时也可以手动指定类型:
1 | arr = np.array([1, 2, 3], dtype=np.float64) |
输出:
1 | [1., 2., 3.] |
注意这里不是四舍五入,而是直接截断小数部分
1.7. ndarray的元素个数:size
size表示数组中总共有多少个元素
1 | arr = np.array([ |
输出:
1 | 6 |
这个数组有2行3列,一共6个元素
1.8. ndarray每个元素占多少字节:itemsize
1 | arr = np.array([1, 2, 3], dtype=np.int32) |
输出:
1 | 4 |
因为int32占32bit,也就是4字节
2. 数组创建
在NumPy中,创建数组最常用的对象是ndarray。数组可以从Python列表创建,也可以按照指定形状、指定范围或指定初始值创建
2.1. np.array()
np.array()是最常用的创建数组的方法
np.array()可以把Python中已有的数据转换成NumPy数组
完整函数签名:
1 | numpy.array( |
最常用的三个参数:
1 | np.array(object, dtype=None, ndmin=0) |
例子:
1 | import numpy as np |
输出:
1 | [1 2 3] |
指定数据类型:
1 | a = np.array([1, 2, 3], dtype=float) |
输出:
1 | [1. 2. 3.] |
2.2. np.zeros()
np.zeros()用来创建一个全是0的数组
完整函数签名:
1 | np.zeros( |
最常用的两个参数:
1 | np.zeros(shape, dtype=None) |
例子:
1 | import numpy as np |
输出:
1 | [0. 0. 0. 0. 0.] |
可以用元组来创建多维数组:
1 | a = np.zeros((2, 3)) |
输出:
1 | [[0. 0. 0.] |
与**np.zeros()**用法相似,**np.ones()**用来创建全1数组
2.3. np.empty()
np.empty()用来创建一个不初始化值的数组
完整函数签名:
1 | np.empty( |
与**np.zeros()**用法相似,最主要区别只是不初始化元素
例子:
1 | import numpy as np |
输出类似于:
1 | [8.62134873e-316 0.00000000e+000 1.51751028e-147 1.26489931e-300 |
np.empty()里面的值不是随机数,而是内存里原来残留的数据。
2.4. np.arange()
np.arange()是Numpy里用来创建等差数组的函数
完整函数签名:
1 | numpy.arange( |
例子:
1 | arr = np.arange(5) |
输出:
1 | [0 1 2 3 4] |
2.5. np.linspace()
np.linspace()是NumPy里用来创建等间隔数组的函数
完整函数签名:
1 | numpy.linspace( |
例子:
1 | start = np.array([0, 10]) |
输出:
1 | [[ 0. 10. ] |
这里的含义是:
第1列:从0到1生成5个数
第2列:从10到20生成5个数
2.6. np.full()
np.full()是NumPy里用来创建用同一个值填满的数组
完整函数签名:
1 | numpy.full( |
例子:
1 | import numpy as np |
输出:
1 | [7 7 7 7 7] |
2.7. np.eye()
np.eye()是NumPy里用来创建二维对角线数组的函数
完整函数签名:
1 | numpy.eye( |
例子:
1 | np.eye(3) |
输出:
1 | [[1. 0. 0.] |
3.数组引索和切片
3.1. 一维数组引索
一维数组可以理解为一排数据,或者数学中的一个向量
与列表相似,NumPy的数组中的每个元素有自己的编号,也就是引索
数组:
1 | arr = np.array([10, 20, 30, 40, 50]) |
1 | 元素: 10 20 30 40 50 |
例子:
1 | import numpy as np |
输出:
1 | 10 |
3.2. 一维数组切片
基本格式:
1 | arr[start:stop:step] |
切片遵循左开右闭
例子:
1 | import numpy as np |
输出:
1 | [20 30 40] |
NumPy切片是视图,这意味着切片并不是一份完全独立的新数据,因此修改切片的同时也会修改原数组。这也是NumPy数组切片与Python列表切片最重要的区别
例子:
1 | import numpy as np |
输出:
1 | [200 30 40] |
因此,当我们想拷贝一份独立的新数据时可以使用copy():
1 | part = arr[1:4].copy() |
例子:
1 | arr = np.array([10, 20, 30, 40, 50]) |
输出:
1 | [200 30 40] |
3.3. 二维数组引索
二维数组可以理解为由行和列组成的表格
引索基本语法:
1 | arr[行索引, 列索引] |
例子:
1 | import numpy as np |
输出:
1 | 10 |
获取一整行/列:
1 | import numpy as np |
输出:
1 | [40 50 60] |
3.4. 二维数组切片
基本格式:
1 | arr[行切片, 列切片] |
例子:
1 | import numpy as np |
输出:
1 | [[20 30] |
这里需要注意,如果二维数组引索其中一行,则得到的是一个一维数组;但如果二维数组通过切片取其中一行,则得到的仍是一个二维数组
例子:
1 | import numpy as np |
输出:
1 | [10 20 30] |
3.5. 花式引索
花式索引也叫高级索引,它允许使用整数列表或整数数组,同时选择多个不连续的位置
例子:
1 | import numpy as np |
输出:
1 | [10 30 50] |
其中[0, 2, 4]表示需要选择的引索位置
二维数组:
1 | import numpy a np |
输出:
1 | [[10 20 30] |
这里arr[[0, 2]]表示选择第0行和第2行
同时指定多个行列坐标:
1 | import numpy a np |
输出:
1 | [20 60 70] |
这里是把行索引和列索引一一对应
1 | 行索引:0 1 2 |
上面等价于:
1 | arr[0, 1] |
选择指定行和指定列形成矩形区域用np.ix_():
1 | import numpy as np |
输出:
1 | [[10 30] |
与普通的切片不同,花式索引通常返回一个新的数组副本:
1 | arr = np.array([10, 20, 30, 40, 50]) |
输出:
1 | [1000 30 50] |
但是直接通过花式索引赋值仍然可以修改原数组:
1 | arr = np.array([10, 20, 30, 40, 50]) |
输出:
1 | [ 0 20 0 40 0] |
3.6. 布尔索引
布尔索引使用由True和False组成的布尔数组筛选数据
True → 保留对应元素
False → 不保留对应元素
例子:
1 | import numpy as np |
输出:
1 | [10 30 50] |
布尔数组的形状通常需要与被筛选的数组相匹配
与花式索引相同,布尔索引返回的也是副本,并且直接通过布尔引索赋值之后仍可以修改原数组
3.6.1. 通过条件生成布尔数组
布尔索引最常见的用法不是手动编写True和False,而是通过条件生成
例子:
1 | import numpy as np |
输出:
1 | [False False False True True] |
3.6.2. 多个条件组合
NumPy中不能直接使用Python中的and和or来进行逻辑运算
可以使用:
1 | & 与 同时满足两个条件 |
例子:
1 | arr[(arr > 20) & (arr < 50)] |
输出:
1 | [30 40] |
3.6.2. 二维数组布尔索引
例子:
1 | import numpy as np |
输出:
1 | [60 70 80 90] |
需要注意,结果变成了一维数组,因为NumPy会把所有满足条件的元素依次取出,然后组成一维数组
布尔条件也可以用于筛选整行:
1 | arr = np.array([ |
输出:
1 | [[10 20] |
4. 数组运算
NumPy的核心优势之一,就是能够直接对整个数组进行运算
4.1. 标量运算
当数组和标量进行运算时,NumPy会让这个标量分别和数组中的每个元素运算
例子:
1 | import numpy as np |
输出:
1 | [11 12 13 14] |
二维数组在与标量进行运算时也是逐元素进行
4.2. 数组运算
数组和数组运算则是作用于两个数组对应位置的元素上
例子:
1 | import numpy as np |
输出:
1 | [5 7 9] |
这里需要注意乘法:
1 | import numpy as np |
输出:
1 | [4, 10, 18] |
这里是逐元素乘法,而不是线性代数中的矩阵乘法
在NumPy中矩阵乘法的运算法是**@**
4.3. 比较运算
比较发生在每一个元素上,返回的不是一个布尔值,而是一个布尔数组
例子:
1 | import numpy as np |
输出:
1 | [False False True True] |
数组之间比较也是对应位置逐元素的比较
4.4. 逻辑运算
4.4.1. 与&
与表示两个条件必须同时成立
例子:
1 | import numpy as np |
输出:
1 | [False False True True True True] |
4.4.2. 或|
或表示两个条件只要有一个成立即可
例子:
1 | import numpy as np |
输出:
1 | [ True False False False False True] |
4.4.3. 非~
非会把0和1反转为1和0
例子:
1 | import numpy as np |
输出:
1 | [ True True True False False False] |
4.4.4. 异或^
异或表示两个条件中恰好一个成立
例子:
1 | import numpy as np |
输出:
1 | [False True True False] |
4.4.5. all()
判断是否所有元素都是True
例子:
1 | arr = np.array([10, 20, 30]) |
输出:
1 | True |
4.4.6. any()
判断是否至少有一个元素为True
例子:
1 | import numpy as np |
输出:
1 | True |
4.5. 向量化计算
不自己写Python循环逐个处理元素,而是直接对整个NumPy数组执行运算
普通Python循环:
1 | nums = [1, 2, 3, 4] |
输出:
1 | [2, 4, 6, 8] |
NumPy向量化写法:
1 | import numpy as np |
输出:
1 | [2 4 6 8] |
虽然向量化的本质依旧是逐元素运算,不过这些操作都是由NumPy底层完成的,通常比Python循环更快
前面讲过的几种运算基本都是通过向量化运算进行的
向量化计算的核心思维是“不要想如何遍历每一个元素,而是想能否把整个规则写进一个数组表达式”
5. 数学函数
NumPy提供了大量的数学函数方便我们进行运算,这些数学函数大多数都属于通用函数,即会对数组中的元素进行批量,逐元素计算,并返回新的数组
5.1. np.abs()
用于计算数组中每个元素的绝对值
例子:
1 | import numpy as np |
输出:
1 | [[1 2 3] |
对于NumPy数组来说,下面两种写法效果基本相同:
1 | np.abs(a) |
在NumPy代码中通常写成np.abs(),这样能够明确表示调用的是NumPy数学函数
5.2. np.sqrt()
用于计算每个元素的非负平方根
数学运算:
$$
y = \sqrt{x}
$$
例子:
1 | import numpy as np |
输出:
1 | [0. 1. 2. 3. 4. 5.] |
结果是浮点数,这是因为平方根结果不一定是整数
5.3. np.exp()
用于计算以自然常数e为底数的指数
数学运算:
$$
y = e^x
$$
例子:
1 | import numpy as np |
输出:
1 | [ 1. 2.71828183 7.3890561 20.08553692] |
NumPy也提供了常量:
1 | np.e |
所以:
1 | np.e ** a |
等同于上面的写法
5.4. np.log()
用于计算以自然常数e为底数的对数
数学运算:
$$
y = \log_e x
$$
例子:
1 | import numpy as np |
输出:
1 | [0. 1. 2.] |
如果想计算任意底数的对数方程可以使用数学上的换底公式:
$$
\log_b x = \frac{\ln x}{\ln b}
$$
以3为例:
1 | import numpy as np |
输出:
1 | [0. 1. 2. 3.] |
5.5. 三角函数
1 | np.sin() |
NumPy也提供了圆周率:
1 | np.pi |
5.5.1. np.sin()
正弦函数
例子:
1 | import numpy as np |
输出:
1 | [0.0000000e+00 5.0000000e-01 1.0000000e+00 1.2246468e-16] |
5.5.2. np.cos()
余弦函数
例子:
1 | import numpy as np |
输出:
1 | [ 1.000000e+00 5.000000e-01 6.123234e-17 -1.000000e+00] |
5.5.3. np.tan()
正切函数
例子:
1 | import numpy as np |
输出:
1 | [0. 1.] |
5.5.4. np.deg2rad()
用于把角度制转换成弧度制
例子:
1 | import numpy as np |
输出:
1 | [0. 0.52359878 1.57079633 3.14159265] |
5.5.5. np.rad2deg()
用于把弧度制转换成角度制
例子:
1 | import numpy as np |
输出:
1 | [ 0. 30. 90. 180.] |
5.5.6. np.arcsin()
反正弦函数
输入三角函数值,返回弧度
例子:
1 | import numpy as np |
输出:
1 | [0. 0.52359878 1.57079633] |
5.5.7. np.arccos()
反余弦函数
输入三角函数值,返回弧度
例子:
1 | import numpy as np |
输出:
1 | [ 0. 60. 90.] |
5.5.8. np.arctan()
反正切函数
输入三角函数值,返回弧度
例子:
1 | import numpy as np |
输出:
1 | [ 0. 45.] |
5.6. 取整函数
这里先提前说一下,以下取整函数的取整不等于转换为整数类型,数据类型依旧是浮点型
5.6.1. np.floor()
向下取整
floor(x)即取得小于或等于x的最大整数
例子:
1 | import numpy as np |
输出:
1 | [-3. -2. -1. 0. 1. 2.] |
5.6.2. np.ceil()
向上取整
ceil(x)即取得大于或等于x的最小整数
例子:
1 | import numpy as np |
输出:
1 | [-2. -1. -0. 1. 2. 3.] |
5.6.3. np.trunc()
向零取整,即直接删除小数部分
例子:
1 | import numpy as np |
输出:
1 | [-2. -1. -0. 0. 1. 2.] |
5.6.4. np.round()
四舍五入到指定小数位数
简单函数签名:
1 | np.round( |
例子:
1 | import numpy as np |
输出:
1 | [3.14 2.72 1.23] |
但是对于正好位于两个整数中间的数,例如:
1 | 0.5 |
NumPy 不是简单地全部“向上取整”,而是取距离最近的偶数,也叫做银行家舍入或round half to even
1 | 0.5 → 0 选择偶数0 |
还有一点:decimals可以是负数,当decimals是负数的时候则可以对十位、百位等进行取整数
5.6.5. np.rint()
将元素取到最接近的整数值,并同样使用中间值取最近偶数的规则
例子:
1 | import numpy as np |
输出:
1 | [1. 2. 2. 3.] |
6. 统计函数和axis
这一节的函数大多属于聚合函数或归约函数
聚合函数:把一组数据汇总成统计结果
归约函数:沿某个轴合并元素,使数组维度或元素数量减少
6.1. axis
axis表示沿着哪个维度进行运算
对于二维数组:
1 | a.shape == (行数, 列数) |
因此:
1 | axis = 0:沿着第0个维度,也就是行的方向计算,最终每一列得到一个结果 |
6.2. np.sum()
对于指定维度进行求和
完整函数签名:
1 | np.sum( |
例子:
1 | import numpy as np |
6.3. np.mean()
对于指定维度计算算术平均值
数学公式:
$$
\bar{x} = \frac{x_1+x_2+...+x_n}{n}
$$
完整函数签名:
1 | np.mean( |
例子:
1 | import numpy as np |
6.4. np.max()
用于得到指定维度的最大值
完整函数签名:
1 | np.max( |
例子:
1 | import numpy as np |
6.5. np.min()
用于得到指定维度的最小值
完整函数签名:
1 | np.min( |
例子:
1 | import numpy as np |
6.6. np.var()
用于得到指定维度的方差
方差用于描述一组数据与平均值之间的离散程度
数学公式:
$$
\sigma^2 = \frac{1}{N}\sum_{i=1}^{N} (x_i - \bar{x})^2
$$
完整函数签名:
1 | np.var( |
这里详细说一下自由度修正量(Delta Degrees of Freedom)
NumPy的除数是 N - ddof
默认情况下ddof=0 也就是只除以N
如果修改为ddof=1 也就是除以N-1
例子:
1 | import numpy as np |
6.7. np.std()
用于得到指定维度的标准差
标准差其实就是方差的平方根:
$$
\sigma = \sqrt{\sigma^2}
$$
完整函数签名:
1 | np.std( |
例子:
1 | import numpy as np |
6.8. np.argmax()
用于搜索最大值的位置
函数签名:
1 | np.argmax( |
前面的np.max()是返回最大值本身,np.argmax()则是返回最大值所在的索引
例子:
1 | import numpy as np |
6.9. np.argmin()
用于搜索最小值的位置
函数签名:
1 | np.argmin( |
例子:
1 | import numpy as np |
7. 数组形状变换
7.1. reshape()
用来在不改变元素总数的情况下,重新组织数组的形状
函数签名:
1 | np.reshape( |
copy这个参数默认是None,即能返回视图时返回视图,不能返回视图时返回拷贝
数组方法形式:
1 | array.reshape(shape, *, order='C', copy=None) |
例子:
1 | import numpy as np |
输出:
1 | [ 0 1 2 3 4 5 6 7 8 9 10 11] |
这里转换时元素总数必须相等比如:
1 | a.reshape(3, 4) |
这些转换都是12个元素,所以都是合法的
可以使用-1自动推导维度
reshape()允许其中一个维度写成-1,由NumPy根据元素总数自动计算,一个形状最多只有一个-1
1 | import numpy as np |
输出:
1 | [[ 0 1 2 3] |
这里进行了一个简单的数学运算自动推导出缺失维度的元素数量
7.2. flatten()
将多维数组展平成一维数组
函数签名:
1 | ndarray.flatten(order='C') # 引索遍历顺序,默认是按照C语言风格 |
注意它是ndarray的方法,而不是np.flatten()
flatten()返回是副本,不是视图
例子:
1 | a = np.array([ |
输出:
1 | [1 2 3 4 5 6] |
7.3. ravel()
ravel()也会把数组展平成一维
函数签名:
1 | np.ravel( |
也有方法形式:
1 | a.ravel(order='C') |
它与flatten()最大的区别是ravel()尽可能返回视图,只有必要时才能创建副本
例子:
1 | a = np.array([ |
输出:
1 | [1 2 3 4 5 6] |
7.4. transpose()
用来交换数组的轴
函数签名:
1 | np.transpose( |
方法形式:
1 | a.transpose(*axes) |
例子,二维数组转置:
1 | a = np.array([ |
输出:
1 | (2, 3) |
包括axes参数:
1 | import numpy as np |
输出:
1 | (2, 3, 4) |
执行流程:
1 | 新 axis=0 ← 原 axis=1 |
不传axes则默认把所有的轴的顺序反转:
1 | import numpy as np |
输出:
1 | (4, 3, 2) |
7.5. .T
.T是数组的转置属性:
1 | array.T |
相当于:
1 | array.transpose() |
.T会返回转置数组的视图
.T大多用于快速获取二维矩阵的转置
例子:
1 | import numpy as np |
输出:
1 | [[1 4] |
7.6. concatenate()
用来沿着一个已经存在的轴连接多个数组
函数签名:
1 | np.concatenate( |
例子:
1 | a = np.array([1, 2, 3]) |
输出:
1 | [1 2 3 4 5 6] |
二维数组沿axis=0连接
1 | import numpy as np |
输出:
1 | [[1 2] |
沿哪个轴连接,哪个轴的长度可以不同;其他轴必须相同
当axis=None时,所有数组会先被展平,再连接:
1 | a = np.array([ |
输出:
1 | [1 2 3 4 5 6] |
7.7. stack()
用来沿着一个新创建的轴堆叠多个数组
函数签名:
1 | np.stack( |
例子:
1 | import numpy as np |
输出:
1 | [[1 2 3] |
8. 广播机制
广播机制决定了不同形状的数组能不能直接进行运算,以及运算后结果的形状是什么
8.1. 标量广播
标量就是单独的一个数
1 | 10 |
NumPy标量和Python标量都可以参与广播
1 | import numpy as np |
输出:
1 | [[11 12 13] |
标量可以与任意形状的数组广播
8.2. 一维数组和二维数组广播
首先要区分以下几种数组:
1 | a = np.array([1, 2, 3]) |
一维数组和二维数组可以按列匹配:
1 | import numpy as np |
输出:
1 | [[11 22 33] |
判断广播时,从形状的最后一个维度开始向前比较:
1 | a.shape = (2, 3) |
最后一维是3和3,因此可以广播
每一行加不同的值:
1 | import numpy as np |
输出:
1 | [[11 12 13] |
8.3. 广播规则
1.从最后一个维度开始比较
2.对应维度满足两个维度相等或其中一个维度为1
例子:
1 | 3 和 3:兼容 |
3.缺少的前置维度按照1处理
例子:
1 | A.shape = (2, 3, 4) |
4.结果每个维度取较大的值
例子:
1 | A.shape = (3, 1) |
8.4. 广播相关函数
8.4.1. np.broadcast_shapes()
可以计算多个形状广播后的结果
1 | import numpy as np |
输出:
1 | (2, 3) |
如果两数组不可广播则会报错
8.4.2. np.broadcast_to()
可以显式查看数组广播后的样子
1 | import numpy as np |
输出:
1 | [[1 2 3] |
9. 随机数
NumPy的随机数功能位于numpy.random模块中。它不仅能生成随机小数,随机整数,还能生成符合正态分布等概率分布的数据,以及完成数组打乱和随机抽样
我们首先需要通过**np.random.default_rng()**函数创建一个随机数生成器对象:
1 | import numpy as np |
输出:
1 | Generator(PCG64) |
也可以直接指定随机种子:
1 | import numpy as np |
9.1. random()
random()用来生成位于以下区间的随机浮点数:[0.0,1.0)
它服从连续均匀分布,即区间中的不同位置具有相同的被抽取机会
函数签名:
1 | rng.random( |
例子:
1 | import numpy as np |
输出:
1 | [0.77395605 0.43887844 0.85859792 0.69736803 0.09417735] |
虽然random()本身只能随机[0.0,1.0)之间的小数,但是可以通过数学的方法来生成指定区间的小数
比如我们想生成:
$$
[a,b)
$$
可以使用公式:
$$
a+(b-a)x
$$
x为random()生成的随机数
9.2. integers()
用来生成随机整数
函数签名:
1 | rng.integers( |
例子:
1 | import numpy as np |
输出:
1 | 0 |
9.3. normal()
用于生成符合正态分布,也称高斯分布的随机数
正态分布的概率密度函数是:
$$
f(x)=\frac{1}{\sigma\sqrt{2\pi}}
e^{-\frac{(x-\mu)^2}{2\sigma^2}}
$$
$\mu$是均值
$\sigma$是标准差
$\sigma^2$是方差
函数签名:
1 | rng.normal( |
例子:
1 | import numpy as np |
输出:
1 | [[171.82830248 163.76009536 174.50270717] |
9.4. 随机种子
计算机生成的随机数是伪随机数
这个随机数是根据算法从一个初始状态不断计算出来,这个初始信息通常由随机种子提供
9.4.1. 不设置种子
1 | import numpy as np |
再次运行整个程序时,结果通常不同,因为 seed=None 时,生成器会从操作系统获取新的、不可预测的熵
9.4.2. 设置种子
1 | import numpy as np |
输出:
1 | [0.77395605 0.43887844 0.85859792] |
用相同种子重新创建随机数生成器则仍然会得到相同的起始序列
固定种子的主要作用是让实验结果可以复现
当同一个生成器连续调用时:
1 | import numpy as np |
输出:
1 | [0.77395605 0.43887844 0.85859792] |
两次结果并不相同,原因是随机数生成器具有内部状态。第一次生成随机数之后,内部状态向前推进;第二次会从新的状态继续生成
9.5. 打乱数组
9.5.1. shuffle()
函数签名:
1 | rng.shuffle( |
打乱一维数组:
1 | import numpy as np |
输出:
1 | [5 3 4 2 1] |
这里会直接原地打乱数组,因此不需要用变量来接收返回值,即使接收了也会是None
打乱二维数组:
1 | import numpy as np |
输出:
1 | [[5 6] |
默认axis=0,因此打乱的是行
axis=1:
1 | import numpy as np |
输出:
1 | [[3 2 1] |
9.5.2. permutation()
同样是打乱数组,与shuffle()不同的是,shuffle()是原地修改,而permutation()则是返回打乱后的副本
函数签名:
1 | rng.permutation( |
例子:
1 | import numpy as np |
输出:
1 | [1 2 3 4 5] |
可以看到原来的arr没有变化
9.6. 随机抽样
随机抽样主要使用:
1 | rng.choice() |
函数签名:
1 | rng.choice( |
抽取一个元素:
1 | import numpy as np |
输出:
1 | 10 |
抽取多个元素:
1 | import numpy as np |
输出:
1 | [10 40 40] |
因为默认是放回的,所以同一个元素可以被重复抽取
指定抽取概率p:
1 | import numpy as np |
这里表示:
1 | A 的概率为 0.7 |
p中的各个概率需要满足相加和为1
二维数组抽样:
1 | import numpy as np |
输出:
1 | [[1 2] |
10. 缺失值和特殊值
10.1. np.nan
nan是Not a Number的缩写,表示“不是一个有效数字”
1 | import numpy as np |
输出:
1 | nan |
它本质上是一种特殊的浮点数表示。NumPy官方将其定义为IEEE754标准中的Not a Number
np.nan可以主动表示缺失值:
1 | import numpy as np |
输出:
1 | [90. 85. nan 88.] |
np.nan 从数学意义上表示“不是一个数字”,只是在数据分析中经常被用来表示缺失值
非法数学运算也可能会产生nan:
1 | import numpy as np |
输出:
1 | nan |
当一个计算中包含nan时,结果也会变成nan
nan不等于自身:
1 | import numpy as np |
输出:
1 | False |
nan表示一个不确定或无效的数值,两个不确定值不能认为一定相等
如果想判断缺失值可以使用np.isnan函数:
1 | import numpy as np |
输出:
1 | True |
np.nan是浮点特殊值,因此普通整数数组不能直接保存它
1 | import numpy as np |
输出:
1 | [ 1. 2. nan 4.] |
虽然其他元素写的是整数,但为了保存np.nan,NumPy会把整个数组转换成浮点数组
如果先创建整数数组,再赋值np.nan则会报错
10.2. np.inf
inf是infinity的缩写,表示正无穷大
1 | import numpy as np |
输出:
1 | inf |
NumPy浮点数组除以零时,通常会产生无穷值,并伴随警告
1 | import numpy as np |
输出:
1 | [ inf -inf] |
10.3. np.isnan()
用于判断输入的元素是否为nan
函数签名:
1 | np.isnan( |
它是一个通用函数,也就是ufunc,它会逐元素判断输入是否为nan
例子:
1 | import numpy as np |
输出:
1 | [False True False True] |
10.4. np.isinf()
用于判断输入的元素是否为正无穷或负无穷
函数签名:
1 | np.isinf( |
例子:
1 | import numpy as np |
输出:
1 | [ inf -inf] |
如果想单独判断正无穷和负无穷可以使用:
1 | np.isposinf() |
10.5. np.nanmean()
np.nanmean()用于计算算术平均值,但会忽略数组中的nan
$$
平均值=\frac{所有非nan元素之和}{非nan元素的数量}
$$
np.mean()的计算中包含nan,所以整个结果会变成nan。而np.nanmean()则会忽略nan
函数签名:
1 | np.nanmean( |
例子:
1 | import numpy as np |
输出:
1 | 3.25 |
11. 文件读写
NumPy中常见的文件读写可以分为两组
| 保存格式 | 保存函数 | 读取函数 | 特点 |
|---|---|---|---|
| NumPy 二进制格式 | np.save() |
np.load() |
保留数组的 shape、dtype 等信息 |
| 普通文本格式 | np.savetxt() |
np.loadtxt() |
文件可直接阅读,也方便其他软件使用 |
11.1. np.save()
函数签名:
1 | np.save( |
输出:
1 | import numpy as np |
执行后,当前目录会生成:
1 | data.npy |
当file是字符串或Path对象,并且文件名没有以.npy结尾时,NumPy会自动添加.npy
.npy文件不仅保存数组元素,还保存了数组的shape、dtype、内存排列信息和实际数据
因此可以:
1 | loaded = np.load("data.npy") |
输出:
1 | [[1 2 3] |
11.2. np.load()
可以读取.npy文件、.npz文件、部分pickle文件
函数签名:
1 | np.load( |
默认情况下,NumPy 会把数组数据读取到内存中,但是如果文件非常大,例如有几十GB,而你只想读取其中一小部分,把整个文件加载到内存中会非常浪费
内存映射的核心特点是:数组数据主要保存在磁盘上,程序访问某一部分数据时,再按需读取对应部分,而不是一次性把整个数组加载进内存
可用模式:
1 | mmap_mode = None # 不使用内存映射 |
一个.npy文件不只有数组的数据,还会包含头部信息,例如:数组的dtype、数组的shape、数组是否按Fortran顺序存储、文件格式版本。通常文件头非常小,默认的 10000 已经足够
读取.npy文件时,返回一个数组;读取.npz文件时,返回一个类似字典的NpzFile对象
11.2.1. 加载.npy
.npy文件通常只保存一个数组
例子:
1 | import numpy as np |
输出:
1 | [[10 20 30] |
11.2.2. 加载.npz
.npz可以保存多个数组
例子:
1 | import numpy as np |
输出:
1 | ['names', 'scores'] |
11.3. np.savetxt()
用于将NumPy数组保存为文本文件(.txt、.csv等)
函数签名:
1 | np.savetxt( |
例子:
1 | import numpy as np |
输出:
1 | x,y |
11.4. np.loadtxt()
用来从格式比较规则的文本文件中读取数据。默认返回浮点数组
函数签名:
1 | np.loadtxt( |