NumPy 学习指南:从入门到精通 – wiki词典

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)打下坚实的基础。祝您学习愉快,在数据科学的旅途中一帆风顺!

滚动至顶部