NumPy 入门教程
目录
- 为什么需要 NumPy?
- 安装 NumPy
- 核心概念:NumPy 数组
- 创建数组
- 数组的基本属性
- 索引与切片
- 数组运算
- 聚合函数
- 矩阵运算
- 实际应用示例:图像处理入门
- 总结与进阶学习
为什么需要 NumPy?
想象一下,你有一个包含一百万个数字的列表,你需要对列表中的每一个数字都乘以 2,用 Python 的原生列表怎么做?
# 原生 Python 列表 my_list = list(range(1, 1000001)) result = [x * 2 for x in my_list] # 使用列表推导式
这段代码虽然能运行,但速度相对较慢,因为它是在 Python 层面一个一个元素地进行操作。
NumPy (Numerical Python) 的出现就是为了解决这个问题。
- 高性能:NumPy 的核心是一个名为
ndarray(N-dimensional Array) 的对象,它使用 C 语言实现,数据在内存中是连续存储的,并且可以使用 CPU 的 SIMD 指令进行并行计算,这使得它的运算速度比 Python 原生列表快几个数量级。 - 丰富的函数库:NumPy 提供了大量用于数学、统计、线性代数等的函数,无需编写复杂的循环。
- 科学计算基石:NumPy 是几乎所有数据科学生态库(如 Pandas, Scikit-learn, Matplotlib, TensorFlow, PyTorch)的基石,不理解 NumPy,就很难深入这些领域。
安装 NumPy
如果你已经安装了 Anaconda,NumPy 通常已经包含在内,如果没有,你可以使用 pip 轻松安装:
pip install numpy
安装完成后,在你的 Python 脚本或 Jupyter Notebook 中导入它:
import numpy as np # 按照惯例,我们通常使用别名 'np' 来指代 NumPy
核心概念:NumPy 数组
NumPy 的核心是 ndarray 对象,我们称之为数组,它和 Python 列表有本质区别:
| 特性 | Python 列表 | NumPy 数组 |
|---|---|---|
| 数据类型 | 可以包含不同类型的数据 (e.g., [1, 'a', 3.14]) |
必须包含相同类型的数据 (e.g., 全是整数或全是浮点数) |
| 性能 | 较慢,逐元素操作 | 极快,底层优化 |
| 功能 | 通用数据结构 | 专为数值计算设计,丰富的数学函数 |
| 内存 | 较大,每个元素都是一个完整的 Python 对象 | 较小,数据是连续的块 |
创建数组
创建 NumPy 数组最常用的方法是 np.array()。
从列表创建
# 创建一维数组 (向量) a = np.array([1, 2, 3, 4, 5]) print(a) # 输出: [1 2 3 4 5] # 创建二维数组 (矩阵) b = np.array([[1, 2, 3], [4, 5, 6]]) print(b) # 输出: # [[1 2 3] # [4 5 6]]
使用内置函数创建
NumPy 提供了许多方便的函数来创建特定类型的数组。
# 创建一个全为 0 的 3x4 数组 zeros_array = np.zeros((3, 4)) print(zeros_array) # 输出: # [[0. 0. 0. 0.] # [0. 0. 0. 0.] # [0. 0. 0. 0.]] # 创建一个全为 1 的 2x2 数组 ones_array = np.ones((2, 2)) print(ones_array) # 输出: # [[1. 1.] # [1. 1.]] # 创建一个单位矩阵 (方阵,对角线为1,其余为0) identity_matrix = np.eye(3) print(identity_matrix) # 输出: # [[1. 0. 0.] # [0. 1. 0.] # [0. 0. 1.]] # 创建一个用指定数字填充的数组 full_array = np.full((2, 2), 99) print(full_array) # 输出: # [[99 99] # [99 99]] # 创建一个范围数组 (类似 range) range_array = np.arange(10) # 0 到 9 print(range_array) # 输出: [0 1 2 3 4 5 6 7 8 9] # 创建一个均匀分布的数组 linspace_array = np.linspace(0, 1, 5) # 从0到1,生成5个均匀分布的数 print(linspace_array) # 输出: [0. 0.25 0.5 0.75 1. ] # 创建一个随机数组 random_array = np.random.rand(3, 2) # 生成一个 3x2 的 [0, 1) 之间的随机数数组 print(random_array) # 输出 (每次运行结果不同): # [[0.123 0.456] # [0.789 0.012] # [0.345 0.678]]
数组的基本属性
了解数组的几个关键属性非常重要。
arr = np.array([[1, 2, 3], [4, 5, 6]])
# .shape: 数组的维度 (行数, 列数)
print(f"Shape: {arr.shape}") # 输出: Shape: (2, 3)
# .ndim: 数组的维度 (轴的数量)
print(f"Number of dimensions: {arr.ndim}") # 输出: Number of dimensions: 2
# .size: 数组中元素的总数
print(f"Size: {arr.size}") # 输出: Size: 6
# .dtype: 数组中元素的数据类型
print(f"Data type: {arr.dtype}") # 输出: Data type: int64 (在64位系统上)
# 创建指定数据类型的数组
float_arr = np.array([1, 2, 3], dtype=np.float32)
print(f"Data type: {float_arr.dtype}") # 输出: Data type: float32
索引与切片
NumPy 的索引和切片与 Python 列表非常相似,但功能更强大,尤其是在多维数组上。
一维数组
a = np.array([0, 10, 20, 30, 40, 50]) # 索引 print(a[2]) # 输出: 20 # 切片 print(a[1:4]) # 输出: [10 20 30] print(a[:3]) # 输出: [0 10 20] print(a[2:]) # 输出: [20 30 40 50] print(a[::2]) # 输出: [0 20 40] (步长为2)
多维数组
对于多维数组,可以使用逗号 来分隔不同维度的索引/切片。
b = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) # 获取单个元素 print(b[0, 1]) # 第0行,第1列 -> 输出: 2 # 获取一行 print(b[1, :]) # 第1行,所有列 -> 输出: [4 5 6] print(b[1]) # 简写,同样获取第1行 -> 输出: [4 5 6] # 获取一列 print(b[:, 0]) # 所有行,第0列 -> 输出: [1 4 7] # 获取一个子矩阵 print(b[0:2, 1:3]) # 输出: # [[2 3] # [5 6]]
数组运算
这是 NumPy 最强大的地方,你可以对整个数组进行数学运算,而无需编写循环,这被称为向量化。
a = np.array([1, 2, 3, 4]) b = np.array([10, 20, 30, 40]) # 加法 print(a + b) # 输出: [11 22 33 44] # 减法 print(b - a) # 输出: [ 9 18 27 36] # 乘法 (对应元素相乘,不是矩阵乘法) print(a * b) # 输出: [ 10 40 90 160] # 除法 print(b / a) # 输出: [10. 10. 10. 10.] # 与标量的运算 print(a * 2) # 输出: [ 2 4 6 8] print(a ** 2) # 平方 # 输出: [ 1 4 9 16] print(np.sqrt(a)) # 开方 # 输出: [1. 1.41421356 1.73205081 2. ]
聚合函数
这些函数对数组中的元素进行汇总计算。
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.max(arr)) # 输出: 6 print(np.min(arr, axis=0)) # 输出: [1 2 3] (每列的最小值) # 平均值 print(np.mean(arr)) # 输出: 3.5 # 标准差 print(np.std(arr)) # 输出: 1.707825127659933
矩阵运算
虽然 NumPy 主要使用 ndarray,但它也提供了专门的 matrix 类型,但更常见的做法是直接使用 ndarray 进行矩阵运算。
A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])
# 矩阵乘法 (点积)
# 方法1: 使用 @ 运算符 (推荐)
C_dot = A @ B
print("@ 运算符结果:\n", C_dot)
# 方法2: 使用 np.dot() 函数
C_func = np.dot(A, B)
print("\nnp.dot() 结果:\n", C_func)
# 两种方法输出相同:
# [[19 22]
# [43 50]]
# 转置矩阵
A_transposed = A.T
print("\nA的转置:\n", A_transposed)
# 输出:
# [[1 3]
# [2 4]]
实际应用示例:图像处理入门
一张灰度图可以看作一个二维数组,每个像素的值代表亮度,一张彩色图可以看作一个三维数组(高 x 宽 x 颜色通道)。
我们使用 scikit-image 库来读取图片(如果没安装,请 pip install scikit-image)。
from skimage import io
import numpy as np
# 读取一张示例图片
# 如果没有网络,可以自己找一张 local.jpg 放在脚本同目录下
try:
image = io.imread('https://upload.wikimedia.org/wikipedia/commons/thumb/5/53/Google_%22G%22_Logo.svg/200px-Google_%22G%22_Logo.svg.png')
print(f"图片形状: {image.shape}") # 输出 (高度, 宽度, 通道数)
except:
print("无法从网络加载图片,请准备本地图片。")
# 创建一个示例彩色数组
image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)
# 将图片转换为灰度图 (一种简单的方法是取颜色通道的平均值)
# image 的形状是 (height, width, 3)
# 沿着最后一个轴 (axis=2) 求平均
gray_image = np.mean(image, axis=2).astype(np.uint8)
print(f"灰度图形状: {gray_image.shape}")
# 增加图片亮度 (向量化运算的威力)
brighter_image = gray_image + 50
# 注意:像素值不能超过255,NumPy 会自动处理 (称为溢出,但结果可能不对)
# 更安全的做法是使用 np.clip
brighter_image_safe = np.clip(gray_image + 50, 0, 255)
# 显示图片 (需要安装 matplotlib: pip install matplotlib)
try:
import matplotlib.pyplot as plt
plt.figure(figsize=(10, 5))
plt.subplot(1, 3, 1)
plt.title("Original")
plt.imshow(image)
plt.subplot(1, 3, 2)
plt.title("Gray")
plt.imshow(gray_image, cmap='gray')
plt.subplot(1, 3, 3)
plt.title("Brighter (Safe)")
plt.imshow(brighter_image_safe, cmap='gray')
plt.show()
except ImportError:
print("matplotlib 未安装,无法显示图片。")
这个例子展示了如何将一张图片表示为数组,并利用 NumPy 的强大功能进行快速处理,而无需编写任何像素级别的循环。
总结与进阶学习
- NumPy 是 Python 科学计算的基石,提供了高性能的多维数组对象
ndarray。 - 核心优势:向量化运算,避免了 Python 层面的循环,速度极快。
- 关键特性:统一的数据类型、丰富的数学函数、强大的索引和切片能力。
- 必须掌握的属性:
.shape,.dtype,.ndim,.size。 - 必须掌握的函数:
np.array(),np.zeros(),np.ones(),np.arange(),np.linspace(),np.sum(),np.mean(),np.max(), (矩阵乘法)。
进阶学习
当你掌握了这些基础知识后,可以继续学习:
- 广播:NumPy 如何处理不同形状数组之间的运算。
- 排序、搜索和计数:
np.sort(),np.argsort(),np.where()等。 - 文件 I/O:使用
np.save()和np.load()保存和加载数组。 - 更多线性代数函数:在
numpy.linalg子模块中,如np.linalg.inv()(求逆),np.linalg.eig()(求特征值)。 - 结合 Pandas:学习 NumPy 如何与 Pandas 的 DataFrame 对象协同工作。
希望这份教程能帮助你顺利入门 NumPy!多动手实践,尝试创建数组、进行运算,你会发现它非常直观和强大。
