第 5 课:NumPy 基础:数组与向量化运算
学习目标
完成本课学习后,你将能够:
- 理解 NumPy 的核心数据结构
ndarray,并能创建基本数组。 - 掌握向量化运算的概念,并利用 NumPy 进行高效的元素级数学运算。
- 熟练运用数组的索引、切片和形状操作来访问与修改数据。
- 认识并使用一些常用的 NumPy 数学与统计函数。
核心概念
1. 为什么需要 NumPy?
Python 原生的列表(list)虽然灵活,但在处理大量数值数据时效率极低。NumPy(Numerical Python)是 Python 科学计算的基础包,它提供了高性能的多维数组对象 ndarray 和用于操作这些数组的工具。在机器学习中,几乎所有数据都会被表示为数组(向量、矩阵、张量),NumPy 就是处理这些数据的“基石”。
2. ndarray:N维数组对象
ndarray 是 NumPy 的核心。它是一个由相同类型元素组成的多维网格。你可以把它想象成一个超级加强版的“列表”,但内部数据在内存中是连续存储的,并且支持高效的数学运算。
3. 向量化(Vectorization)
这是 NumPy 的魔法所在。向量化意味着用数组表达式代替显式的循环。它使得代码更简洁、更易读,并且速度快得多,因为底层运算由高度优化的 C 代码执行。
# 普通Python循环(慢)
result = []
for i in range(1000000):
result.append(i * 2)
# NumPy向量化(快)
import numpy as np
result = np.arange(1000000) * 2 # 一行代码,速度提升数十甚至数百倍
4. 数组的形状(Shape)与维度
数组的形状是一个元组,描述了每个维度的大小。一个一维数组 (5,) 有5个元素;一个二维数组 (3, 4) 有3行4列。理解形状对于正确使用索引和广播运算至关重要。
代码示例
1. 创建数组
import numpy as np # 约定俗成的导入方式
# 从Python列表创建
arr1 = np.array([1, 2, 3, 4, 5])
print("一维数组:", arr1)
print("类型:", type(arr1)) # <class 'numpy.ndarray'>
print("元素类型:", arr1.dtype) # int64 (根据系统可能不同)
# 创建二维数组
arr2d = np.array([[1, 2, 3], [4, 5, 6]])
print("二维数组:\n", arr2d)
print("形状:", arr2d.shape) # (2, 3) 表示 2行3列
# 使用内置函数快速创建
zeros = np.zeros((3, 4)) # 3行4列的全0数组
ones = np.ones((2, 2)) # 2行2列的全1数组
eye = np.eye(3) # 3x3的单位矩阵
range_arr = np.arange(0, 10, 2) # 类似range,但生成数组 [0, 2, 4, 6, 8]
linspace_arr = np.linspace(0, 1, 5) # 在0到1之间均匀取5个点 [0., 0.25, 0.5, 0.75, 1.]
print("单位矩阵:\n", eye)
print("arange数组:", range_arr)
print("linspace数组:", linspace_arr)
2. 向量化运算
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) # [1 4 9 16]
print("比较:", a > 2) # [False False True True] -> 布尔数组
# 与标量运算(广播)
print("所有元素加5:", a + 5) # [6 7 8 9]
print("所有元素乘0.5:", a * 0.5) # [0.5 1. 1.5 2.]
# 函数也是向量化的
print("平方根:", np.sqrt(a)) # [1. 1.41421356 1.73205081 2. ]
print("指数:", np.exp(a)) # [ 2.71828183 7.3890561 20.08553692 54.59815003]
3. 索引与切片
arr = np.arange(10) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
# 一维索引与切片(和Python列表几乎一样)
print("第三个元素:", arr[2]) # 2 (索引从0开始)
print("切片[2:5]:", arr[2:5]) # [2 3 4]
print("带步长的切片:", arr[1:8:2]) # [1 3 5 7]
# 二维索引与切片
matrix = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
print("第二行第三列元素:", matrix[1, 2]) # 6 (行索引, 列索引)
print("第一行:", matrix[0, :]) # [1 2 3]
print("第二列:", matrix[:, 1]) # [2 5 8]
print("子矩阵:\n", matrix[:2, 1:]) # 取前2行,第2列及之后 [[2 3] [5 6]]
# **重要概念:视图(View)而非复制**
slice_view = arr[3:6] # 创建一个视图
slice_view[0] = 100 # 修改视图会影响原数组!
print("修改后的原数组:", arr) # [ 0 1 2 100 4 5 6 7 8 9]
# 如果需要副本,使用.copy()
slice_copy = arr[3:6].copy()
slice_copy[0] = 200 # 不影响原数组
4. 常用数学与统计函数
data = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
# 聚合函数(沿轴计算)
print("所有元素的和:", np.sum(data)) # 45
print("每列的和 (axis=0):", np.sum(data, axis=0)) # [12 15 18]
print("每行的和 (axis=1):", np.sum(data, axis=1)) # [ 6 15 24]
print("最大值:", np.max(data)) # 9
print("平均值:", np.mean(data)) # 5.0
print("标准差:", np.std(data)) # 2.58198889747...
# 通用函数
print("最大值的索引:", np.argmax(data)) # 8 (展平后的索引)
print("每列最大值的索引:", np.argmax(data, axis=0)) # [2 2 2]
实践练习
练习1:基础数组操作
要求:
- 创建一个包含 1 到 20 的整数的一维数组。
- 将该数组重塑为一个 4 行 5 列的二维数组。
- 打印出该数组的形状、元素总数和所有元素的数据类型。 预期输出示例:
形状: (4, 5)
元素总数: 20
数据类型: int64
练习2:向量化统计
要求:
给定学生成绩二维数组 scores = np.array([[85, 90, 78], [92, 88, 95], [76, 82, 89]]),其中每一行代表一个学生,每一列代表一门课程(语文、数学、英语)。
- 计算每个学生三门课程的总分。
- 计算每门课程所有学生的平均分。
- 找出成绩最高的学生及其对应的总分。 预期输出示例:
学生总分: [253 275 247]
课程平均分: [84.33333333 86.66666667 87.33333333]
最高总分: 275
练习3:索引与切片
要求:
使用 np.arange(1, 26).reshape(5, 5) 创建一个 5x5 的矩阵。
- 提取矩阵的中心 3x3 子矩阵。
- 将该子矩阵的所有元素都设置为 0。
- 打印修改后的整个 5x5 矩阵。 预期输出示例:
[[ 1 2 3 4 5]
[ 6 0 0 0 10]
[11 0 0 0 15]
[16 0 0 0 20]
[21 22 23 24 25]]
常见错误
- 索引混淆:NumPy 数组索引从 0 开始,不是 1。访问
arr[1]是第二个元素。 - 形状不匹配进行运算:尝试对形状
(3,)和(4,)的数组直接相加会引发ValueError。运算前务必检查或调整形状。 - 整数除法陷阱:在 Python 3 中,
a / b对整数数组得到的是浮点结果(如np.array([5]) / 2->[2.5])。如果需要整数结果,用//或.astype(int)转换。 - 混淆视图和复制:切片操作(如
arr[2:5])返回的是原数组的视图,修改视图会影响原数据。如果你需要独立的副本,务必使用.copy()方法。 - 忘记轴(axis)参数:聚合函数(如
sum,mean)如果不指定axis,会计算整个数组的结果。axis=0表示沿垂直方向(跨行)计算,得到每列的结果;axis=1表示沿水平方向(跨列)计算,得到每行的结果。
小结
本节课我们学习了 NumPy 最基础也最重要的概念:
- ndarray 是 NumPy 的核心,它是同质、高效的多维数组。
- 向量化 使我们能用简洁的数组表达式替代低效的循环,是高性能计算的关键。
- 索引与切片 用于访问和修改数组的子集,需要注意是视图操作。
- 常用函数(如
sum,mean,max,reshape)提供了强大的数据操作和统计能力。
掌握 NumPy 数组是进入机器学习和数据科学领域必须迈出的第一步。下一课,我们将学习基于 NumPy 构建的、更专注于数据分析和处理的 Pandas 库。
练习编辑器
rust
Loading...