Python NumPy库基础
import numpy as np
ndarray
数组对象
- 固定大小;相同数据类型;高效快速的矢量算术运算。
- 序号从
0
开始;ndarray
对象的维度 (dimensions) 称为 轴 (axis),轴的个数叫做 秩 (rank)。
创建 ndarray
对象
-
numpy.array()
函数:np.array(object, dtype = None, copy = True, order = None, subok = False, ndmin = 0)
参数:
object
表示数组或嵌套的数列;dtype
可选参数,表示数组元素的数据类型;copy
可选参数,指出对象是否需要复制;order
描述创建数组的存储顺序,'C'
为行方向,'F'
为列方向,默认值'A'
表示任意方向;subok
默认返回一个与基类类型一致的数组;ndmin
指定生成数组的最小维度。
import numpy as np np.array([1, 2, 3, 4, 5]) # 列表转换为数组 np.array((1, 3, 5, 7, 9)) # 元组转换为数组 np.array(range(10)) # range 对象转换为数组 l = [[1., 2., 3.], [4., 5., 6.]] np.array(l) # 二维数组 l = [[[1, 2], [3, 4]], [[6, 7], [8, 9]]] np.array(l) #三维数组
-
numpy.zeros()
函数:用于创建一个m
行n
列的全0
数组,dtype
指定数组类型np.zeros((m, n), dtype = np.float64)
-
numpy.ones()
函数:创建一个全1
数组np.ones((m, n), dtype = np.float64)
注意:
zeros()
和ones()
函数中第一个参数(m, n)
为 元组类型。 -
numpy.random.rand()
函数:创建一个指定形状的随机数组,数组元素服从 \(0 \sim 1\) 均匀分布的随机样本,取值范围 \([0, 1)\) 不包括1
np.random.rand(3) np.random.rand(3, 4) # 注意传参方式与之前不同
-
numpy.arange()
函数:类似于range()
函数,用于创建一个等差序列的ndarray
数组np.arange(8) np.arange(10, 20, 2) # array([10, 12, 14, 16, 18]) np.arange(10, 20, 3) # array([10, 13, 16, 19])
函数将根据参数推断出元素类型。
-
numpy.linespace(beg, end, n)
函数:创建一个以beg
为起点,end
为终点,等分成n
个元素的 等差数组np.linspace(10, 20, 5) # 包含5个数的等差数组 # array([10. , 12.5, 15. , 17.5, 20. ]) np.linspace(1, 10, 5, endpoint=False) # 不包含终点 # array([1. , 2.8, 4.6, 6.4, 8.2])
当
endpoint = False
时,步长step = (end - beg) / num
,序列从beg
开始,每次加上step
。默认
dtype = np.float64
。 -
numpy.empty()
函数:数组元素随机产生np.empty((m, n), dtype = np.float64, order='C')
默认数据类型为
numpy.float64
,np.empty((3, 4)) # 元素是接近于0的随机数 np.empty((6, ), dtype=list) # array([None, None, None, None, None, None], dtype = object) # 指定数据类型为 list 对象,创建空数组
-
numpy.zeros_like()
函数(ppt中没有):根据另一个数组的形状来创建一个新的零数组np.zeros_like(a, dtype=None, order='K', subok=True, shape=None)
参数:
a
用于确定形状和数据类型的数组;dtype
可选,缺省则使用数组a
的数据类型;order
可选{'C', 'F', 'A', 'K'}
,控制数组元素的存储顺序;subok
可选,为True
则新数组将保留输入数组a
的子类类型;shape
整数或整数元组,指定要覆盖的形状。
ndarray
对象常用属性
ndim
正整数表示数组的维度,即数组的秩 (rank)。shape
N
个正整数组成的元组类型,每个元素对应个维度的大小,N
为数组的秩。dtype
数组的数据类型,每个ndarray
对象只有一种dtype
类型。size
数组元素的总个数,即shape
属性中元组元素的乘积。T
对二维数组来说即转置矩阵
ndarray
对象基本操作
-
reshape()
改变数组形状:numpy.reshape(arr, new_shape, order='C') # 不改变输入数组 ndarray.reshape(new_shape, order='C') # 不改变原数组,返回改变形状后的原数组的视图 (view)
参数:
arr
表示需要修改形状的数组;new_shape
为整数或整数组成的元组,表示修改后的数组形状。要求新数组的形状应当与原数组元素数量及形状兼容,否则 抛出异常;order
表示数组元素的存储顺序,取值{'C', 'F', 'A'}
分别表示行顺序、列顺序、原顺序;默认为'C'
行顺序;
arr = np.array([1, 2, 3, 4, 5, 6]) newArr = np.reshape(arr, (2, 3)) # 默认 'C' 行顺序 # array([[1, 2, 3], [4, 5, 6]]) newArr = np.reshape(arr, (2, 3), order='F') # array([1, 3, 5], [2, 4, 6])
ndarray.reshape(new_shape, order='C')
不会原地修改数组,而是返回一个新的数组视图,即新数组和原数组共享相同的数据,但表现为不同的形状。 -
resize()
改变数组形状,并根据需要补充或丢弃部分元素:numpy.resize(arr, new_shape) # 不改变输入数组
参数:
arr
是需要改变形状的数组;new_shape
是整数或整数组成的元组,指定新数组的形状。
说明:
numpy.resize()
不会改变arr
,而是返回一个新的改变了形状的数组。- 如果
new_shape
指定的大小大于原始数组arr
的大小,那么arr
中的元素会被重复,按照'C'
顺序(行优先)填充新数组; - 如果
new_shape
指定的大小小于原始数组arr
的大小,则新数组会包含原始数组中的前new_shape
大小的元素。
与
ndarray.reshape()
不同,ndarray.resize()
原地修改 数组:# 与 numpy.resize() 不同,对数组进行原地修改 ndarray.resize(new_shape, refcheck=True)
参数:
new_shape
是整数或整数组成的元组,指定新数组的形状;refcheck
布尔值,默认为True
。如果为True
,在数组的内存被其他变量引用时,该方法会抛出异常;如果为False
,则不会进行这样的检查。
说明:
- 当
new_shape
指定的总大小小于原始数组的总大小时,多余的数据会被丢弃。 - 当
new_shape
指定的总大小大于原始数组的总大小时,会在数组的末尾添加未初始化的数据(通常为0
,但不保证)
-
ndarray.astype()
改变数组元素的数据类型,返回一个新数组ndarray.astype(dtype)
即使指定的数据类型
dtype
与原始数据相同,也会创建一个新数组。 -
ndarray.flatten()
将二维或三维数组快速扁平化,返回一个一维数组ndarray.flatten(order='C')
参数:
- 默认
order='C'
按照行方向降维。
- 默认
-
transpose()
根据指定的顺序改变数组的维度matrix = np.array[[1, 2], [3, 4], [5, 6]] np.transpose(matrix) # 相当于转置矩阵 matrix.transpose(matrix) # 创建一个新数组 # [[1, 3, 5], [2, 4, 6]] matrix.T # ndaray.T 属性实现线性代数中矩阵转置功能 np.transpose(arr3d, axes) # axes 为整数元组,指定轴的新顺序
-
numpy.hstack()
沿水平方向合并多个数组np.hstack((arr1, arr2))
输入为合并数组的元组或列表;要求合并的数组除列数以外其它如行数等必须相等。
-
numpy.vstack()
沿垂直方向合并多个数组np.vstack((arr1, arr2))
输入为合并数组的元组或列表;要求合并数组除第一个轴(即行数)之外的其它所有轴的尺寸上具有一致性。
-
numpy.concatenate()
在任意指定轴上连接多个数组np.concatenate((arr1, arr2), axis=0)
参数:
- 被连接数组的元组或列表
axis
指定连接的轴;默认axis=0
即沿第一个轴连接。
所有被连接数组必须在除指定轴意外的所有其它轴上具有相同的维度,否则
concatenate()
将抛出错误。 -
索引和切片:
-
ndarray
对象的基本索引和切片得到的都是原始数组的视图(修改视图也会修改原始数组)。 -
一维数组的索引:与列表结构类似
data = np.array(range(6)) # array([0, 1, 2, 3, 4, 5]) data[0] data[-1] data[3:]
-
多维数组索引:
data = np.arange(9).reshape(3, 3) # array([0, 1, 2], [3, 4, 5], [6, 7, 8]) data[2] data[2, :] data[2:, :] # [6 7 8] data[2][0] data[2, 0] # 6 data[:2, 1:] # [[1 2] # [4 5]] data[:, :2] # [[0 1] # [3 4] # [6 7]]
-
布尔值索引:使用一个与目标数组 同形 的布尔数组来指定哪些元素应该被选中。
data = np.arange(6).reshape(2, 3) # [[0 1 2] # [3 4 5]]
创建布尔数组时,可以使用逻辑运算符 (
&
,|
,~
) 来组合多个条件。注意每个条件必须被括号包围,以避免运算符优先级引起的错误。# 创建布尔数组 mask = data > 3 # [[False False False] # [False True True]] # 选择小于2或大于3的数 mask = (data < 2) | (data > 3) # [[True True False] # [False True True]]
当在索引操作中使用布尔数组时,NumPy 返回一个由所有在布尔数组中对应为
True
的位置的元素组成的 一维数组。布尔索引会返回数组中数据的 副本,而不是视图;即通过布尔索引得到的新数组是原始数据的一个副本,修改它不会影响原数组。
selected_data = data[mask] # [0 1 4 6]
但是,当使用布尔索引表达式直接作为赋值目标是,NumPy 不会先创建一个副本;相反,它直接在原始数组上按照布尔索引指示的位置进行修改:
data[mask] = 10 # [[10 10 2] # [3 10 10]]
NumPy 的索引操作理解上可以分为两类:
- 获取数据:当使用布尔索引来获取数据时(如
selected_data = data[mask]
),返回的是满足条件的数据的副本。这种情况下,你获取的selected_data
是原数组中选定数据的一维副本。 - 设置数据:当布尔索引用于赋值操作时(如
data[mask] = value
),这个操作直接影响原数组,按照布尔数组指定的模式修改数据。这里不创建副本,因为操作的目的是修改原数组。
ChatGPT
- 获取数据:当使用布尔索引来获取数据时(如
-
NumPy 常用统计函数
函数 | 描述 | 函数 | 描述 |
---|---|---|---|
mean() , nanmean() |
均值 | argmax() |
最大值的索引 |
sum() , nansum() |
求和 | argmin() |
最小值的索引 |
max() |
最大值 | argsort() |
排序后的索引 |
min() |
最小值 | cumsum() |
累加 |
std() |
标准差 | cumprod() |
累乘 |
median() |
中位数 | average() |
加权平均数 |
-
几乎 所有统计函数在关于二维数组(或多维数组)的运算时都需要注意参数
axis
的取值。对于二维数组,如果未设置,则针对所有元素进行操作;如果
axis = 0
, 表示对第一个轴(行)进行操作,即沿着纵轴操作;如果axis = 1
,表示对第二个轴(列)进行操作,即沿着横轴进行操作。 -
大部分统计函数既可以作为
ndarray
的方法调用(ndarray.mean()
更加直接和简洁),也可以作为 NumPy 模块的顶层函数来调用(numpy.mean()
可以接受任何 “数组样式” 的对象,包括列表等)。 -
NaN 安全函数:NumPy 提供了一些 NaN 安全的函数,如
nanmean()
,nansum()
等,这些函数在处理包含 NaN 值得数组时会忽略 NaN。
NumPy 常用函数
-
numpy.all()
对所有元素进行与操作,用于判断所有元素都满足条件;只有所有元素取值为True
,函数返回结果才为True
。np.all(arr > 5)
-
numpy.any()
对所有元素进行或操作,用于判断是否至少一个元素满足条件。只要任意一个元素取值为True
,函数返回结果即为True
。np.any(arr > 5)
-
numpy.unique()
函数返回数组中所有不同结果,并按照从小到大排序。np.unique(arr)
NumPy 数组运算
NumPy 数组的向量化
NumPy数组的向量化是一种使用数组操作来实现循环或迭代的技术,从而避免显式Python级的循环。向量化操作是NumPy中性能优化的核心,因为它们通常在底层由优化过的C代码执行,比纯Python代码执行得更快。
简单来说,就是使用简单的数组表达式替代循环操作。
向量化操作的主要优势:
- 性能提升:向量化操作比等效的 Python 循环快很多,尤其是处理大数据时;
- 代码简洁、易读。
实现向量化的方式:
-
利用 广播机制;
-
使用 NumPy 的通用函数 (ufuncs);
-
“形式向量化”
numpt.vectorize
def func(x): return x * x - 2 * x + 1 vectorized_func = np.vectorize(func) arr = np.arange(4) result = vectorized_func(arr)
np.vectorize
虽然提供了向量化接口,但实际上它只是一个循环的包装器,并没有提高性能。但在无法直接引用ufuncs
的情况下,它使得代码向量化成为可能。
NumPy 广播机制
- 作为多维向量的组合,数组计算大多在相同形状的数组之间进行,要求被处理的数组维度以及每个维度大小是相等的,这事的数组操作应用在 元素 上,即数组元素一一对应的操作。
- 当进行操作的两个数组的形状不完全相同时,NumPy 会尝试“广播” (broadcasting) 较小数组的形状以匹配较大数组的形状。
广播遵遵循以下规则:
- 对齐维度:从数组的最后一个维度开始向前对齐,即将数组的维度 右对齐;
- 维度兼容性:数组形状必须要符合广播机制的要求,即对于两个数组的对应维度
- 它们在这个维度的长度相同;
- 或其中一个数组在该维度的长度为
1
。
- 增加维数:当两个数组的维数不同时,将在低维度数组的前面添加长度为
1
的轴来增加维数,直到两者维数相同; - 扩展维度:当某个轴的长度为
1
时,沿此轴的运算都用该轴上的第一组值。
在实际内存中,数组并没有真的扩展,但 NumPy 会在运算时逻辑上扩展数组。
np.arange(3) + 5
# [5 6 7]
np.ones((3, 3)) + np.arange(3)
# [[1. 2. 3.]
# [1. 2. 3.]
# [1. 2. 3.]]
np.arange(3).reshape(3, 1) + np.arange(3)
# [[0 1 2]
# [1 2 3]
# [2 3 4]]
当无法直接使用广播机制时,可以使用 numpy.tile()
人工扩展数组的维度:
-
numpy.tile()
函数用于沿指定轴重复数组多次np.tile(arr, reps)
参数:
arr
需要被重复的数组;reps
:一个整数或整数元组,指定数组在每个维度上的重复次数。
如果
reps
是一个整数,那么原数组将在所有轴上重复;如果是一个元组,则分别指定每个轴的重复次数;元组长度可以小于数组的轴数,这时从最后一个轴开始应用重复次数。tile()
返回一个新数组,而不是原地修改。
ufunc 通用函数
函数 | 描述 | 函数 | 描述 |
---|---|---|---|
numpy.add() |
元素相加,等同于 + |
numpy.subtract() |
元素相减,等同于 - |
numpy.multiply() |
元素相乘,等同于 * |
numpy.divide() |
元素相除,等同于 / |
numpy.ceil() |
向上取整 | numpy.floor() |
向下取整 |
numpy.rint() |
四舍五入 | numpy.isnan() |
判断是否为NaN |
numpy.sin() |
正弦函数 | numpy.cos() |
余弦函数 |
numpy.tan() |
正切函数 | ||
numpy.exp() |
计算 e 的 x 次方 |
numpy.log() |
自然对数 |
numpy.log10() |
以 10 为底的对数 | ||
numpy.greater() |
逐元素大于 > |
numpy.less |
逐元素小于 < |
numpy.equal() |
逐元素等于 == |