Python NumPy 基础教程:从入门到精通 – wiki大全


Python NumPy 基础教程:从入门到精通

1. 引言:为什么选择 NumPy?

在 Python 的数据科学生态系统中,NumPy (Numerical Python) 是一个不可或缺的核心库。它提供了强大的 N 维数组对象,以及用于处理这些数组的各种工具。虽然 Python 内建的列表 (list) 可以存储数据,但 NumPy 数组在以下几个方面具有显著优势:

  1. 性能优化:NumPy 数组是同类型数据的集合,底层用 C/C++ 实现,运算速度远超 Python 列表。在处理大量数据时,性能优势尤为明显。
  2. 内存效率:NumPy 数组存储的是连续的内存块,数据类型统一,相比 Python 列表存储的是对象的引用,NumPy 更节省内存。
  3. 强大的数学功能:NumPy 提供了大量用于数组操作的数学函数,包括线性代数、傅里叶变换、随机数生成等,是科学计算的基础。
  4. 互操作性:NumPy 数组是许多其他科学计算库(如 SciPy, scikit-learn, Pandas, Matplotlib)的基础数据结构。

本教程将带您从 NumPy 的基础概念入手,逐步深入到高级用法,助您从入门走向精通。

2. 安装 NumPy

安装 NumPy 非常简单,通常使用 pip 工具:

bash
pip install numpy

安装完成后,在 Python 代码中导入 NumPy,习惯上使用 np 作为别名:

python
import numpy as np

3. NumPy 数组 (ndarray) 的创建

NumPy 的核心是 ndarray 对象,它是一个多维数组。

3.1 从 Python 列表创建

最常见的方法是从 Python 列表或元组创建数组。

“`python

一维数组

arr1d = np.array([1, 2, 3, 4, 5])
print(“一维数组:”, arr1d)
print(“类型:”, type(arr1d))
print(“数据类型:”, arr1d.dtype) # 默认根据元素推断类型

二维数组 (矩阵)

arr2d = np.array([[1, 2, 3], [4, 5, 6]])
print(“\n二维数组:\n”, arr2d)

指定数据类型

arr_float = np.array([1, 2, 3], dtype=np.float64)
print(“\n指定数据类型的数组:”, arr_float)
print(“指定数据类型:”, arr_float.dtype)
“`

3.2 使用内置函数创建特殊数组

NumPy 提供了一系列函数用于快速创建特定类型的数组。

  • np.zeros(shape, dtype): 创建全零数组。
  • np.ones(shape, dtype): 创建全一数组。
  • np.empty(shape, dtype): 创建未初始化数组 (值是随机的)。
  • np.arange(start, stop, step): 创建等差数列数组 (类似 Python 的 range())。
  • np.linspace(start, stop, num): 创建指定数量的等间隔数组。
  • np.eye(N): 创建 N x N 的单位矩阵。
  • np.diag(v): 创建对角线为 v 的矩阵。

“`python
print(“全零数组:\n”, np.zeros((2, 3))) # 2行3列

print(“全一数组:\n”, np.ones((3, 2))) # 3行2列

print(“未初始化数组 (随机值):\n”, np.empty((2, 2)))

print(“等差数列 (0到9):\n”, np.arange(10))
print(“等差数列 (1到10, 步长2):\n”, np.arange(1, 11, 2))

print(“等间隔数组 (0到10, 共5个点):\n”, np.linspace(0, 10, 5))

print(“单位矩阵:\n”, np.eye(3)) # 3×3 单位矩阵
“`

3.3 随机数数组

numpy.random 模块提供了强大的随机数生成功能。

  • np.random.rand(d0, d1, ..., dn): 生成 (0, 1) 区间内的浮点数。
  • np.random.randn(d0, d1, ..., dn): 生成标准正态分布 (均值0,方差1) 的浮点数。
  • np.random.randint(low, high, size): 生成指定范围内的整数。

“`python
print(“随机浮点数 (0-1):\n”, np.random.rand(2, 3))

print(“标准正态分布随机数:\n”, np.random.randn(2, 2))

print(“随机整数 (0-9, 2×3矩阵):\n”, np.random.randint(0, 10, size=(2, 3)))
“`

4. 数组属性

NumPy 数组有一些重要的属性,用于描述其结构。

  • ndim: 数组的维度 (轴的数量)。
  • shape: 数组的形状,一个元组,表示每个维度的大小。
  • size: 数组中元素的总数量。
  • dtype: 数组中元素的类型。
  • itemsize: 数组中每个元素占用的字节数。
  • nbytes: 整个数组占用的总字节数。

python
arr = np.array([[1, 2, 3], [4, 5, 6]])
print("数组:\n", arr)
print("维度 (ndim):", arr.ndim)
print("形状 (shape):", arr.shape) # (行数, 列数)
print("大小 (size):", arr.size)
print("数据类型 (dtype):", arr.dtype)
print("每个元素字节数 (itemsize):", arr.itemsize)
print("总字节数 (nbytes):", arr.nbytes)

5. 数组的索引和切片

NumPy 数组的索引和切片功能非常强大,与 Python 列表类似但更灵活。

5.1 基本索引

  • 一维数组:arr[index]
  • 多维数组:arr[row_index, col_index, ...]

“`python
arr1d = np.arange(10) # [0 1 2 3 4 5 6 7 8 9]
print(“arr1d[3]:”, arr1d[3]) # 3

arr2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(“arr2d:\n”, arr2d)
print(“arr2d[1, 2]:”, arr2d[1, 2]) # 第二行第三列的元素 (5)
print(“arr2d[0]:”, arr2d[0]) # 第一行 [1 2 3]
“`

5.2 切片

使用 : 进行切片,语法与 Python 列表相同:start:stop:step

“`python
arr1d = np.arange(10)
print(“arr1d[2:7]:”, arr1d[2:7]) # 从索引2到6 [2 3 4 5 6]
print(“arr1d[:5]:”, arr1d[:5]) # 前5个元素 [0 1 2 3 4]
print(“arr1d[5:]:”, arr1d[5:]) # 索引5及以后的元素 [5 6 7 8 9]
print(“arr1d[::2]:”, arr1d[::2]) # 每隔一个取一个 [0 2 4 6 8]

arr2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(“\n二维数组切片:”)
print(“arr2d[0:2, 1:3]:\n”, arr2d[0:2, 1:3]) # 前两行,第二列到第三列

[[2 3]

[5 6]]

print(“arr2d[:, 0]:\n”, arr2d[:, 0]) # 所有行的第一列 [1 4 7]
print(“arr2d[1, :]:\n”, arr2d[1, :]) # 第二行的所有元素 [4 5 6]
print(“arr2d[-1, -1]:”, arr2d[-1, -1]) # 最后一个元素 9
“`

重要提示:NumPy 数组的切片是原数组的视图 (view),而不是副本 (copy)。修改切片会同时修改原数组。如果需要副本,请使用 .copy() 方法。

python
arr_slice = arr2d[0:2, 1:3]
arr_slice[0, 0] = 99
print("\n修改切片后原数组:\n", arr2d) # arr2d 也被修改了

5.3 布尔索引 (花式索引)

使用布尔数组来选择元素。

“`python
arr = np.array([1, 2, 3, 4, 5, 6])
mask = (arr > 3)
print(“布尔掩码:”, mask) # [False False False True True True]
print(“大于3的元素:”, arr[mask]) # [4 5 6]

arr2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(“\narr2d 中偶数元素:\n”, arr2d[arr2d % 2 == 0]) # [2 4 6 8]
“`

5.4 整数数组索引

使用整数数组来选择任意行或列。

“`python
arr = np.arange(10, 20).reshape(2, 5)
print(“数组:\n”, arr)

[[10 11 12 13 14]

[15 16 17 18 19]]

选择特定行和列的元素

print(“选择 (0,1), (1,3) 位置的元素:”, arr[[0, 1], [1, 3]]) # [11 18]

选择特定行

print(“选择第0行和第1行:\n”, arr[[0, 1]])
“`

6. 数组形状操作

6.1 重塑 (Reshape)

reshape() 函数可以改变数组的形状,但不会改变其数据。要求新旧形状的元素总数必须相同。

“`python
arr = np.arange(12)
print(“原数组:\n”, arr)

arr_reshaped = arr.reshape(3, 4) # 3行4列
print(“重塑为3×4:\n”, arr_reshaped)

arr_reshaped2 = arr.reshape(2, 2, 3) # 2个2×3的矩阵
print(“重塑为2x2x3:\n”, arr_reshaped2)

-1 表示自动计算该维度的大小

arr_reshaped3 = arr.reshape(3, -1)
print(“自动计算列数:\n”, arr_reshaped3)

展平数组 (一维化)

print(“展平数组:”, arr_reshaped.flatten())
print(“展平数组:”, arr_reshaped.ravel()) # ravel 也是视图
“`

6.2 拼接 (Concatenation)

使用 np.concatenate()np.vstack() (垂直堆叠) 和 np.hstack() (水平堆叠) 拼接数组。

“`python
a = np.array([[1, 2], [3, 4]])
b = np.array([[5, 6], [7, 8]])

print(“数组 a:\n”, a)
print(“数组 b:\n”, b)

沿轴0 (垂直) 拼接

print(“垂直拼接 (axis=0):\n”, np.concatenate((a, b), axis=0))

[[1 2]

[3 4]

[5 6]

[7 8]]

沿轴1 (水平) 拼接

print(“水平拼接 (axis=1):\n”, np.concatenate((a, b), axis=1))

[[1 2 5 6]

[3 4 7 8]]

np.vstack 等价于 np.concatenate(…, axis=0)

print(“垂直堆叠 (vstack):\n”, np.vstack((a, b)))

np.hstack 等价于 np.concatenate(…, axis=1)

print(“水平堆叠 (hstack):\n”, np.hstack((a, b)))
“`

6.3 分割 (Splitting)

使用 np.split()np.vsplit() (垂直分割) 和 np.hsplit() (水平分割) 分割数组。

“`python
arr = np.arange(16).reshape(4, 4)
print(“原数组:\n”, arr)

垂直分割成2个数组

arr_vsplit = np.vsplit(arr, 2)
print(“垂直分割结果 (2个数组):\n”, arr_vsplit[0], “\n”, arr_vsplit[1])

水平分割成4个数组

arr_hsplit = np.hsplit(arr, 4)
print(“水平分割结果 (第一个数组):\n”, arr_hsplit[0])
“`

7. 数学运算

NumPy 数组的强大之处在于其支持快速、高效的元素级 (element-wise) 运算和矩阵运算。

7.1 元素级运算

数组与标量、数组与数组之间可以直接进行加减乘除、幂运算等,这些操作会自动应用到数组的每个元素上。

“`python
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])

print(“a + b:”, a + b) # [5 7 9]
print(“a * 2:”, a * 2) # [2 4 6]
print(“b – a:”, b – a) # [3 3 3]
print(“a ** 2:”, a ** 2) # [1 4 9]
print(“b / a:”, b / a) # [4. 2.5 2. ] (浮点数除法)

比较运算

print(“a > 2:”, a > 2) # [False False True]
“`

7.2 通用函数 (Universal Functions, ufuncs)

NumPy 提供了大量的通用函数 (ufuncs),它们对数组的每个元素进行操作。

python
arr = np.array([-2, -1, 0, 1, 2])
print("绝对值 (np.abs):", np.abs(arr))
print("平方根 (np.sqrt):", np.sqrt(np.array([1, 4, 9])))
print("指数 (np.exp):", np.exp(arr))
print("对数 (np.log):", np.log(np.array([1, np.e, np.e**2])))
print("正弦 (np.sin):", np.sin(np.array([0, np.pi/2, np.pi])))

7.3 聚合函数

用于计算数组的统计量。

  • sum(): 求和。
  • min(): 最小值。
  • max(): 最大值。
  • mean(): 平均值。
  • std(): 标准差。
  • var(): 方差。
  • argmin(): 最小值索引。
  • argmax(): 最大值索引。

可以指定 axis 参数在特定轴上进行计算。

“`python
arr = np.array([[1, 2, 3], [4, 5, 6]])
print(“数组:\n”, arr)

print(“所有元素和:”, arr.sum())
print(“行最大值:”, arr.max(axis=1)) # [3 6]
print(“列平均值:”, arr.mean(axis=0)) # [2.5 3.5 4.5]

print(“所有元素最小值索引:”, arr.argmin()) # 0 (对应元素1)
“`

7.4 线性代数

NumPy 提供了强大的线性代数功能。

  • np.dot(): 点积、矩阵乘法。
  • @ 运算符 (Python 3.5+): 矩阵乘法。
  • 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:\n”, A)
print(“矩阵 B:\n”, B)

矩阵乘法

print(“A @ B (矩阵乘法):\n”, A @ B)

或 print(“np.dot(A, B):\n”, np.dot(A, B))

矩阵求逆

inv_A = np.linalg.inv(A)
print(“A 的逆矩阵:\n”, inv_A)
print(“A @ inv_A (接近单位矩阵):\n”, A @ inv_A)

行列式

print(“A 的行列式:”, np.linalg.det(A))
“`

8. 广播 (Broadcasting)

广播是 NumPy 的一个强大功能,它描述了 NumPy 如何在算术运算期间处理具有不同形状的数组。在不需要复制数据的情况下,广播机制可以有效地执行数组运算。

广播规则
当对两个数组进行操作时,NumPy 会逐元素比较它们的形状 (从末尾维度开始)。
1. 如果两个维度相等,则它们兼容。
2. 如果其中一个维度为 1,则另一个维度兼容。
3. 如果以上两条规则都不满足,则维度不兼容,会引发 ValueError

形状较小的数组的维度会被“广播”到较大数组的形状。

“`python
a = np.array([1, 2, 3]) # 形状 (3,)
b = 2 # 标量

print(“a + b (标量广播):”, a + b) # [3 4 5]

A = np.array([[1, 2, 3], [4, 5, 6]]) # 形状 (2, 3)
b = np.array([10, 20, 30]) # 形状 (3,)

print(“\nA:\n”, A)
print(“b:”, b)

b 被广播到 A 的每一行

print(“A + b (维度广播):\n”, A + b)

[[11 22 33]

[14 25 36]]

另一个例子:增加一列

C = np.array([[1, 2], [3, 4]]) # 形状 (2, 2)
D = np.array([[10], [20]]) # 形状 (2, 1)

print(“\nC:\n”, C)
print(“D:\n”, D)

D 的列被广播到 C 的所有列

print(“C + D:\n”, C + D)

[[11 12]

[23 24]]

“`

9. 文件的输入/输出

NumPy 可以方便地保存和加载数组。

  • np.save('filename.npy', arr): 将单个数组以 .npy 格式保存。
  • np.load('filename.npy'): 从 .npy 文件加载数组。
  • np.savetxt('filename.txt', arr, delimiter=','): 将数组保存为文本文件。
  • np.loadtxt('filename.txt', delimiter=','): 从文本文件加载数组。

“`python
data = np.arange(10).reshape(2, 5)
np.save(‘my_array.npy’, data)
loaded_data = np.load(‘my_array.npy’)
print(“加载的 .npy 数据:\n”, loaded_data)

np.savetxt(‘my_data.csv’, data, delimiter=’,’)

可以用 Excel 或文本编辑器打开 my_data.csv

加载 csv 文件

loaded_csv_data = np.loadtxt(‘my_data.csv’, delimiter=’,’)
print(“加载的 .csv 数据:\n”, loaded_csv_data)
“`
(注:这里的文件操作会在当前执行脚本的目录下创建文件,请注意清理。)

10. 结论

NumPy 是 Python 进行科学计算的基石。掌握其 N 维数组对象、索引切片、形状操作、强大的数学函数以及广播机制,将极大地提升您处理数值数据的效率和能力。从基础的数组创建到复杂的线性代数运算,NumPy 为数据科学家和工程师提供了不可或缺的工具。

这仅仅是 NumPy 功能的冰山一角。要成为真正的专家,建议您继续探索以下高级主题:

  • 结构化数组 (Structured Arrays):存储混合数据类型的数组。
  • 内存视图和复制的深层理解:避免不必要的内存开销。
  • ufuncs 的更多细节:了解其工作原理和如何创建自定义 ufuncs。
  • 高级线性代数模块 numpy.linalg:更多矩阵分解、求解线性方程等功能。
  • 与 Pandas、Matplotlib 和 SciPy 的集成:NumPy 如何作为这些库的后端。

通过不断实践和深入学习,您将能够充分利用 NumPy 的强大功能,解决各种复杂的数值计算问题。


滚动至顶部