NumPy 学习指南:从入门到精通
NumPy(Numerical Python)是 Python 中用于科学计算的核心库。它提供了高性能的多维数组对象(ndarray)以及用于处理这些数组的工具。无论您是进行数据分析、机器学习、图像处理还是其他任何需要大量数值计算的领域,NumPy 都是不可或缺的工具。本指南将带您从 NumPy 的基础知识逐步深入,直至精通其高级用法。
第一部分:NumPy 入门
1.1 为什么选择 NumPy?
Python 列表虽然功能强大,但在处理大量数值数据时效率不高。NumPy 的 ndarray 对象以连续内存块存储数据,并针对 C 语言级别进行了优化,因此在执行数学运算时速度远超 Python 列表。此外,NumPy 提供了大量针对数组操作的优化函数,避免了显式的循环,使代码更简洁、更高效。
1.2 安装 NumPy
如果您已经安装了 Anaconda 或 Miniconda,NumPy 通常会预装。否则,可以通过 pip 进行安装:
bash
pip install numpy
1.3 创建 NumPy 数组
NumPy 数组是所有操作的基础。以下是几种常见的创建方法:
- 从 Python 列表或元组创建:
“`python
import numpy as np
arr1 = np.array([1, 2, 3, 4, 5])
print(arr1) # 输出: [1 2 3 4 5]
print(type(arr1)) # 输出:
arr2 = np.array([[1, 2, 3], [4, 5, 6]])
print(arr2)
# 输出:
# [[1 2 3]
# [4 5 6]]
“`
-
使用内置函数创建特定数组:
-
np.zeros(shape): 创建指定形状的全零数组。 np.ones(shape): 创建指定形状的全一数组。np.empty(shape): 创建指定形状的空数组(元素是随机值)。np.arange(start, stop, step): 创建等差数列。np.linspace(start, stop, num): 创建指定数量的等间隔数列。np.eye(N): 创建 N 阶单位矩阵。
“`python
print(np.zeros((2, 3)))
# 输出:
# [[0. 0. 0.]
# [0. 0. 0.]]
print(np.ones((3, 2), dtype=int)) # 可以指定数据类型
# 输出:
# [[1 1]
# [1 1]
# [1 1]]
print(np.arange(0, 10, 2)) # 输出: [0 2 4 6 8]
print(np.linspace(0, 1, 5)) # 输出: [0. 0.25 0.5 0.75 1. ]
“`
1.4 数组属性
了解数组的属性对于有效操作至关重要:
ndim: 数组的维度(轴的数量)。shape: 数组的形状(每个维度的大小)。size: 数组中元素的总数。dtype: 数组中元素的数据类型。itemsize: 数组中每个元素占用的字节数。
python
arr = np.array([[1, 2, 3], [4, 5, 6]])
print(f"维度: {arr.ndim}") # 输出: 维度: 2
print(f"形状: {arr.shape}") # 输出: 形状: (2, 3)
print(f"元素总数: {arr.size}") # 输出: 元素总数: 6
print(f"数据类型: {arr.dtype}") # 输出: 数据类型: int32 (或 int64, 取决于系统)
1.5 数组索引和切片
NumPy 数组的索引和切片类似于 Python 列表,但支持多维操作:
- 基本索引:
“`python
arr = np.array([10, 20, 30, 40, 50])
print(arr[0]) # 输出: 10
print(arr[-1]) # 输出: 50
arr_2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(arr_2d[0, 1]) # 访问第一行第二列的元素, 输出: 2
print(arr_2d[2, 0]) # 访问第三行第一列的元素, 输出: 7
“`
- 切片:
“`python
arr = np.array([10, 20, 30, 40, 50])
print(arr[1:4]) # 输出: [20 30 40]
print(arr[:3]) # 输出: [10 20 30]
print(arr[::2]) # 输出: [10 30 50]
arr_2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(arr_2d[:2, 1:]) # 前两行,从第二列到最后
# 输出:
# [[2 3]
# [5 6]]
print(arr_2d[1, :]) # 第二行所有元素, 输出: [4 5 6]
print(arr_2d[:, 0]) # 第一列所有元素, 输出: [1 4 7]
“`
第二部分:NumPy 基本操作
2.1 数组之间的数学运算
NumPy 最大的优势在于其向量化操作,可以直接对整个数组进行数学运算,而无需显式循环。这些操作是逐元素的。
“`python
arr1 = np.array([1, 2, 3])
arr2 = np.array([4, 5, 6])
print(arr1 + arr2) # 逐元素相加, 输出: [5 7 9]
print(arr1 * 2) # 逐元素乘以常数, 输出: [2 4 6]
print(arr1 ** 2) # 逐元素平方, 输出: [1 4 9]
print(np.sin(arr1)) # 对每个元素应用数学函数
“`
2.2 广播 (Broadcasting)
广播是 NumPy 允许不同形状的数组进行数学运算的强大机制。它会自动调整较小数组的形状以匹配较大数组,前提是它们的维度兼容。
广播规则:
1. 如果两个数组的维度不同,较低维度的数组的形状将以 1 为前缀进行扩展。
2. 在任何维度中,如果两个数组在该维度上的大小相同,或者其中一个数组在该维度上的大小为 1,则称这两个数组在该维度上是兼容的。
3. 如果维度不兼容,则会引发错误。
“`python
a = np.array([[1, 2, 3], [4, 5, 6]]) # 形状 (2, 3)
b = np.array([10, 20, 30]) # 形状 (3,)
print(a + b)
输出:
[[11 22 33]
[14 25 36]]
c = 5 # 标量也可以广播
print(a + c)
输出:
[[ 6 7 8]
[ 9 10 11]]
“`
2.3 聚合函数
NumPy 提供了许多聚合函数,用于计算数组的统计量:
np.sum(): 求和np.mean(): 求平均值np.min(),np.max(): 求最小值、最大值np.std(): 求标准差np.var(): 求方差np.cumsum(): 求累积和
可以指定 axis 参数来沿着特定轴进行计算:
“`python
arr = np.array([[1, 2, 3], [4, 5, 6]])
print(np.sum(arr)) # 所有元素求和, 输出: 21
print(np.sum(arr, axis=0)) # 沿着列求和 (对行进行操作), 输出: [5 7 9]
print(np.sum(arr, axis=1)) # 沿着行求和 (对列进行操作), 输出: [ 6 15]
print(np.mean(arr)) # 输出: 3.5
print(np.min(arr, axis=1)) # 输出: [1 4]
“`
第三部分:NumPy 数组操作和重塑
3.1 改变数组形状
reshape(): 不改变数据,只改变数组的形状。flatten()/ravel(): 将多维数组展平为一维数组。ravel()返回视图(可能),flatten()返回副本。transpose()/.T: 数组转置。
“`python
arr = np.arange(1, 10).reshape((3, 3))
print(arr)
输出:
[[1 2 3]
[4 5 6]
[7 8 9]]
print(arr.flatten()) # 输出: [1 2 3 4 5 6 7 8 9]
print(arr.T)
输出:
[[1 4 7]
[2 5 8]
[3 6 9]]
“`
3.2 数组组合和分割
np.concatenate((a1, a2, ...), axis): 沿着指定轴连接一系列数组。np.vstack((a1, a2, ...)): 垂直堆叠(按行)。np.hstack((a1, a2, ...)): 水平堆叠(按列)。
“`python
a = np.array([[1, 2], [3, 4]])
b = np.array([[5, 6]])
print(np.concatenate((a, b), axis=0)) # 垂直连接
输出:
[[1 2]
[3 4]
[5 6]]
print(np.vstack((a, b))) # 等同于 concatenate(axis=0)
c = np.array([[7], [8]])
print(np.hstack((a, c))) # 水平连接
输出:
[[1 2 7]
[3 4 8]]
“`
np.split(ary, indices_or_sections, axis): 将数组分割成多个子数组。np.hsplit(ary, indices_or_sections): 水平分割。np.vsplit(ary, indices_or_sections): 垂直分割。
“`python
arr = np.arange(16).reshape((4, 4))
print(arr)
输出:
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]
[12 13 14 15]]
arr_split = np.hsplit(arr, 2) # 水平分割成两部分
print(arr_split[0])
输出:
[[ 0 1]
[ 4 5]
[ 8 9]
[12 13]]
print(arr_split[1])
输出:
[[ 2 3]
[ 6 7]
[10 11]
[14 15]]
“`
第四部分:高级索引和布尔掩码
4.1 整数数组索引 (Fancy Indexing)
使用整数数组作为索引来选择非连续的元素。
“`python
arr = np.arange(10, 100, 10) # [10 20 30 40 50 60 70 80 90]
indices = np.array([0, 2, 5, 8])
print(arr[indices]) # 输出: [10 30 60 90]
arr_2d = np.arange(9).reshape(3, 3)
[[0 1 2]
[3 4 5]
[6 7 8]]
rows = np.array([0, 1, 2])
cols = np.array([1, 0, 2])
print(arr_2d[rows, cols]) # 访问 (0,1), (1,0), (2,2) 处的元素, 输出: [1 3 8]
“`
4.2 布尔数组索引 (Boolean Masking)
使用布尔数组(通常由条件表达式生成)作为索引来选择满足特定条件的元素。
“`python
arr = np.array([1, 5, 2, 8, 3, 9, 4, 6])
mask = (arr > 4)
print(mask) # 输出: [False True False True False True False True]
print(arr[mask]) # 输出: [5 8 9 6]
组合条件
print(arr[(arr > 4) & (arr < 7)]) # 输出: [5 6]
“`
布尔掩码在数据筛选和清洗中非常有用。
第五部分:NumPy 的线性代数
NumPy 的 linalg 模块提供了强大的线性代数功能。
np.dot()或@运算符: 矩阵乘法。np.linalg.inv(): 矩阵求逆。np.linalg.det(): 矩阵求行列式。np.linalg.eig(): 计算特征值和特征向量。
“`python
A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])
print(A @ B) # 矩阵乘法
输出:
[[19 22]
[43 50]]
print(np.linalg.inv(A)) # 矩阵求逆
输出:
[[-2. 1. ]
[ 1.5 -0.5]]
print(np.linalg.det(A)) # 行列式
输出: -2.0000000000000004 (浮点精度误差)
“`
第六部分:性能优化和注意事项
6.1 避免循环 (Vectorization)
尽可能使用 NumPy 的内置函数和向量化操作,而不是 Python 的 for 循环。这是获得高性能的关键。
“`python
低效的循环
list_data = list(range(1000000))
for i in range(len(list_data)):
list_data[i] = list_data[i] * 2
高效的 NumPy 向量化操作
np_arr = np.arange(1000000)
np_arr = np_arr * 2
“`
6.2 视图与副本
当对 NumPy 数组进行切片时,通常返回的是原始数组的视图,而不是副本。这意味着修改视图会同时修改原始数组。
python
arr = np.arange(10)
s = arr[3:7] # s 是 arr 的一个视图
s[0] = 99
print(arr) # 输出: [ 0 1 2 99 5 6 7 8 9]
要创建副本,可以使用 .copy() 方法:
python
arr = np.arange(10)
s_copy = arr[3:7].copy() # s_copy 是一个副本
s_copy[0] = 100
print(arr) # 输出: [0 1 2 3 4 5 6 7 8 9] (原始 arr 不受影响)
print(s_copy) # 输出: [100 4 5 6]
总结
本指南带您回顾了 NumPy 的核心概念和常用功能:从数组的创建、属性、索引切片,到基本的数学运算、广播机制、聚合函数,再到数组的重塑、组合、分割,以及高级的整数数组索引和布尔掩码。最后,我们还触及了线性代数操作和性能优化技巧。
NumPy 是一个庞大而强大的库,掌握其精髓需要大量的实践。通过不断地动手尝试,您将能熟练运用 NumPy 解决各种科学计算问题,并为更高级的库(如 Pandas、SciPy、Scikit-learn)打下坚实的基础。祝您学习愉快,在数据科学的旅途中一帆风顺!