
文章目录
📖 开篇导读
在之前的课程中,我们学习了Python的基础语法、文件操作、数据库操作以及办公自动化。这些技能已经能够处理日常的大部分编程任务。但是,当遇到大量数值计算、科学计算或数据分析时,Python原生的列表和循环往往会显得力不从心——性能低下,代码冗长。
这时,我们需要一个强大的数值计算库:NumPy(Numerical Python)。NumPy是Python科学计算的基石,它提供了高性能的多维数组对象(n维数组,ndarray)以及丰富的数组运算函数。Pandas、SciPy、Scikit-learn、TensorFlow、PyTorch等数据科学和机器学习库都建立在NumPy的基础之上。
💡 工作场景:
- 数据科学:数据清洗、特征工程、统计分析。
- 机器学习:处理特征矩阵、标签向量。
- 图像处理:图像本质就是三维数组(高度、宽度、通道)。
- 信号处理:音频、金融时间序列数据。
本课将系统学习NumPy的核心功能:
- ndarray数组的创建、属性、数据类型
- 索引、切片、布尔索引、花式索引
- 数组运算:向量化操作、广播机制
- 通用函数(ufunc)、聚合函数
- 线性代数运算
- 数组的存储与加载
- 底层原理:内存布局、视图与副本
学完本课,你将掌握NumPy的基本使用,为后续学习Pandas、Matplotlib打下坚实的基础。
🎯 学习目标
| 目标编号 | 具体掌握内容 | 对应面试/工作价值 |
|---|---|---|
| 1️⃣ | 理解NumPy的ndarray与Python列表的区别 | 性能优化意识 |
| 2️⃣ | 掌握创建数组的各种方法(array, zeros, ones, arange, linspace, random) | 数据生成 |
| 3️⃣ | 熟练使用索引、切片、布尔索引、花式索引获取数据 | 灵活访问子集 |
| 4️⃣ | 理解向量化运算和广播机制,写出高效代码 | 避免Python循环 |
| 5️⃣ | 掌握通用函数(ufunc)进行元素级运算 | 快速数学计算 |
| 6️⃣ | 了解数组的内存布局(C-order/F-order)、视图与副本的区别 | 底层优化 |
🔥 面试考点:“NumPy数组和Python列表的区别?”“什么是广播机制?”“如何理解视图(View)和副本(Copy)?”“axis参数的含义?”
📚 知识点理论精讲
一、NumPy简介与安装
1.1 NumPy是什么?
NumPy是一个开源的Python科学计算库,主要提供了:
- 高性能的ndarray(n维数组)对象。
- 快速向量化运算,避免Python循环。
- 丰富的数学函数(三角函数、指数、对数等)。
- 线性代数、傅里叶变换、随机数生成等功能。
1.2 安装
pip install numpy
在Jupyter Notebook或Python脚本中导入:
import numpy as np # 约定俗成的别名
二、ndarray:多维数组对象
2.1 ndarray与Python列表的区别
| 特性 | Python列表 | NumPy ndarray |
|---|---|---|
| 元素类型 | 可以任意类型混搭 | 所有元素类型相同 |
| 内存占用 | 较大(每个元素是独立的Python对象) | 连续内存,紧凑 |
| 运算速度 | 慢(循环逐个处理) | 快(向量化,C语言实现) |
| 功能 | 基础序列操作 | 丰富数学、统计、线性代数函数 |
# 示例对比
lst = [1, 2, 3]
arr = np.array([1, 2, 3])
print(lst * 2) # [1,2,3,1,2,3] 重复列表
print(arr * 2) # [2,4,6] 元素逐个乘以2
2.2 创建数组
从列表创建
import numpy as np
# 一维
arr1 = np.array([1, 2, 3])
# 二维
arr2 = np.array([[1,2,3], [4,5,6]])
# 指定数据类型
arr3 = np.array([1,2,3], dtype=np.float32) # dtype: int32, float64, bool等
常用创建函数
# 全零数组
zeros = np.zeros((3,4)) # 3行4列全0
# 全一数组
ones = np.ones((2,3))
# 单位矩阵
eye = np.eye(5) # 5x5单位矩阵
# 未初始化(内容随机)
empty = np.empty((2,2))
# 等差数列
arange = np.arange(0, 10, 2) # [0,2,4,6,8]
# 等间隔点
linspace = np.linspace(0, 1, 5) # [0, 0.25, 0.5, 0.75, 1]
# 随机数
random_arr = np.random.rand(3,3) # 均匀分布[0,1)
randn_arr = np.random.randn(3,3) # 标准正态分布
randint_arr = np.random.randint(0, 10, size=(2,3)) # 整数
2.3 数组属性
arr = np.array([[1,2,3], [4,5,6]])
print(arr.shape) # (2,3) 维度大小
print(arr.ndim) # 2 维度数
print(arr.size) # 6 元素总数
print(arr.dtype) # int64 元素类型
print(arr.itemsize) # 8 每个元素字节数
print(arr.nbytes) # 48 总字节数
2.4 数据类型
NumPy支持的数据类型比Python更丰富,常用:
| 类型 | 描述 | 示例 |
|---|---|---|
int8, int16, int32, int64 | 有符号整数 | dtype=np.int32 |
uint8, uint16, uint32, uint64 | 无符号整数 | 常用于图像(0-255) |
float16, float32, float64 | 浮点数 | dtype=np.float64 |
bool | 布尔 | dtype=bool |
complex64, complex128 | 复数 | dtype=np.complex128 |
三、索引与切片
3.1 基础索引
与Python列表类似,但多维数组支持多轴索引。
arr = np.array([[1,2,3], [4,5,6], [7,8,9]])
print(arr[0, 1]) # 2 (第0行,第1列)
print(arr[1]) # [4,5,6] (第1行)
print(arr[1][2]) # 6 (等价于 arr[1,2])
3.2 切片
切片语法:[start:stop:step, start:stop:step]
print(arr[0:2, 1:3]) # 第0-1行,第1-2列 -> [[2,3],[5,6]]
print(arr[:, 1]) # 所有行的第1列 -> [2,5,8]
print(arr[::2, ::2]) # 隔行隔列 -> [[1,3],[7,9]]
注意:切片返回的是原数组的视图(view),不是副本。修改视图会影响原数组。
sub = arr[0:2, 0:2]
sub[0,0] = 99
print(arr[0,0]) # 99 (原数组也被修改)
3.3 布尔索引
使用布尔数组筛选元素。
arr = np.array([1, 2, 3, 4, 5])
mask = arr > 3
print(mask) # [False, False, False, True, True]
print(arr[mask]) # [4,5]
# 直接在条件中
print(arr[arr > 3]) # [4,5]
# 二维示例
arr2d = np.array([[1,2],[3,4],[5,6]])
print(arr2d[arr2d[:,1] > 3]) # 第2列大于3的行 -> [[3,4],[5,6]]
3.4 花式索引(Fancy Indexing)
使用整数数组作为索引。
arr = np.array([10, 20, 30, 40, 50])
indices = [1, 3, 4]
print(arr[indices]) # [20,40,50]
# 二维
arr2d = np.arange(12).reshape(3,4)
# 使用两个列表,分别指定行和列
print(arr2d[[0,2], [1,2]]) # (0,1)和(2,2)位置的元素 -> [1,10]
# 如果想取多行多列的子集,可以用np.ix_
rows = np.array([0,2])
cols = np.array([1,2])
print(arr2d[np.ix_(rows, cols)]) # 得到2x2矩阵
四、数组运算——向量化
向量化是指对数组元素进行批量操作,无需显式循环。
4.1 算术运算
a = np.array([1,2,3])
b = np.array([4,5,6])
print(a + b) # [5,7,9]
print(a * b) # [4,10,18]
print(a ** 2) # [1,4,9]
print(np.sqrt(a)) # [1, 1.414, 1.732]
4.2 标量运算
print(a + 10) # [11,12,13]
print(a * 2) # [2,4,6]
4.3 矩阵乘法(点积)
A = np.array([[1,2],[3,4]])
B = np.array([[5,6],[7,8]])
print(np.dot(A, B)) # 矩阵乘法
# 或使用 @ 运算符 (Python 3.5+)
print(A @ B)
五、广播机制(Broadcasting)
当两个数组形状不同时,NumPy会尝试自动广播,使它们能够进行运算。
5.1 广播规则
- 从尾部维度对齐。
- 两个数组的某个维度长度相同,或其中一个长度为1,或其中一个缺失。
- 如果不满足,抛出
ValueError。
# 例1: 一维数组广播到二维数组
arr = np.ones((3,4))
vec = np.array([1,2,3,4])
result = arr + vec # vec形状(4,)扩展为(3,4)
print(result.shape) # (3,4)
# 例2: 列向量广播
col = np.array([[1],[2],[3]]) # (3,1)
row = np.array([10,20,30]) # (3,)
result = col + row # 自动广播到(3,3)
5.2 手动添加维度
使用np.newaxis或reshape。
a = np.array([1,2,3])
b = a[:, np.newaxis] # (3,1)
print(b.shape) # (3,1)
六、通用函数(ufunc)
通用函数是对数组中每个元素进行快速运算的函数。
6.1 一元ufunc
arr = np.array([-1, 0, 1, 2])
np.abs(arr) # 绝对值
np.sqrt(arr) # 平方根
np.exp(arr) # 指数 e^x
np.log(arr) # 自然对数
np.sin(arr) # 三角函数
6.2 二元ufunc
a = np.array([1,2,3])
b = np.array([4,5,6])
np.add(a, b) # 等价于 a+b
np.maximum(a, b) # 逐元素取最大值
np.minimum(a, b) # 逐元素取最小值
6.3 聚合函数
arr = np.array([[1,2],[3,4]])
print(np.sum(arr)) # 10
print(np.mean(arr)) # 2.5
print(np.std(arr)) # 标准差
print(np.min(arr)) # 1
print(np.max(arr)) # 4
# 指定轴 axis: 0列方向,1行方向
print(np.sum(arr, axis=0)) # [4,6] 每列和
print(np.sum(arr, axis=1)) # [3,7] 每行和
七、形状操作
7.1 reshape与resize
arr = np.arange(12)
# reshape返回新数组,不修改原数组
reshaped = arr.reshape(3,4)
# resize修改原数组(或返回None)
arr.resize(3,4)
7.2 展平(ravel vs flatten)
arr = np.array([[1,2],[3,4]])
# ravel 返回视图(如果可能)
flat_view = arr.ravel()
# flatten 返回副本
flat_copy = arr.flatten()
7.3 拼接与分裂
# 垂直拼接
a = np.array([[1,2],[3,4]])
b = np.array([[5,6],[7,8]])
vstack = np.vstack((a, b)) # (4,2)
# 水平拼接
hstack = np.hstack((a, b)) # (2,4)
# 合并通过轴
concat = np.concatenate((a, b), axis=0)
# 分裂
arr = np.arange(12).reshape(3,4)
split = np.split(arr, 3, axis=0) # 分成三个子数组
八、内存布局、视图与副本
8.1 内存布局
NumPy数组存储在连续的内存块中,有两种顺序:
- C-order(行优先):默认,最后轴变化最快。
- F-order(列优先):Fortran风格。
arr = np.arange(12).reshape(3,4)
print(arr.flags['C_CONTIGUOUS']) # True
arr_f = np.array(arr, order='F')
8.2 视图(View)与副本(Copy)
- 视图:共享数据,修改视图影响原数组。切片、
ravel()(通常)返回视图。 - 副本:独立数据,修改副本不影响原数组。
copy()、flatten()返回副本。
arr = np.arange(10)
view = arr[::2] # 视图
copy = arr.copy() # 副本
view[0] = 999
print(arr[0]) # 999 被修改
九、线性代数
NumPy提供了linalg子模块。
A = np.array([[1,2],[3,4]])
# 矩阵的逆
inv = np.linalg.inv(A)
# 行列式
det = np.linalg.det(A)
# 特征值、特征向量
eigvals, eigvecs = np.linalg.eig(A)
# 解线性方程组 Ax = b
b = np.array([5,6])
x = np.linalg.solve(A, b)
十、随机数生成
# 设置随机种子保证可重复性
np.random.seed(42)
# 均匀分布[0,1)
rand = np.random.rand(3,3)
# 标准正态分布
randn = np.random.randn(3,3)
# 整数随机
randint = np.random.randint(0, 10, size=(3,3))
# 随机排列
arr = np.arange(10)
np.random.shuffle(arr) # 原地打乱
perm = np.random.permutation(10) # 返回新数组
# 随机选择
choice = np.random.choice([1,2,3], size=5, p=[0.1,0.6,0.3]) # 带权重
十一、数据存储与加载
11.1 文本文件
# 保存为一维或二维txt
arr = np.arange(12).reshape(3,4)
np.savetxt('data.txt', arr, fmt='%d', delimiter=',')
# 加载
loaded = np.loadtxt('data.txt', delimiter=',')
11.2 二进制文件(.npy)
np.save('arr.npy', arr)
loaded = np.load('arr.npy')
# 多个数组压缩保存
np.savez('multiple.npz', a=arr, b=arr*2)
data = np.load('multiple.npz')
print(data['a'])
💻 代码案例实操
案例1:基础创建与属性
"""
array_basics.py
创建数组并查看属性
"""
import numpy as np
# 从列表创建
arr1 = np.array([1, 2, 3, 4, 5])
print("一维数组:", arr1)
print("shape:", arr1.shape, "ndim:", arr1.ndim)
# 二维
arr2 = np.array([[1, 2], [3, 4], [5, 6]])
print("二维数组:\n", arr2)
print("shape:", arr2.shape)
# 常用创建方法
zeros = np.zeros((2, 3))
ones = np.ones((2, 3))
eye = np.eye(4)
range_arr = np.arange(0, 10, 2)
lin = np.linspace(0, 1, 5)
print("zeros:\n", zeros)
print("arange:", range_arr)
案例2:索引与切片
"""
indexing_slicing.py
演示各种索引方式
"""
import numpy as np
arr = np.arange(1, 13).reshape(3, 4)
print("原始数组:\n", arr)
# 基础索引
print("第1行第2列:", arr[0, 1])
# 切片
print("前2行,第2-4列:\n", arr[0:2, 1:4])
print("所有行第2列:", arr[:, 1])
# 布尔索引
mask = arr > 6
print("大于6的元素:", arr[mask])
# 花式索引
rows = [0, 2]
cols = [1, 3]
print("指定位置:", arr[rows, cols]) # 得到 [arr[0,1], arr[2,3]]
# 想要子矩阵使用 np.ix_
print("子矩阵:\n", arr[np.ix_(rows, cols)])
案例3:向量化运算与广播
"""
vectorization_broadcast.py
演示数组运算的高效性和广播机制
"""
import numpy as np
import time
# 对比列表与数组的速度
def list_square(n):
lst = list(range(n))
result = [x**2 for x in lst]
return result
def array_square(n):
arr = np.arange(n)
result = arr**2
return result
n = 10_000_000
start = time.time()
list_square(n)
print(f"列表推导耗时: {time.time()-start:.4f}s")
start = time.time()
array_square(n)
print(f"NumPy向量化耗时: {time.time()-start:.4f}s")
# 广播示例
a = np.array([[1,2,3],[4,5,6]])
b = np.array([10,20,30])
print("a + b 广播结果:\n", a + b)
c = np.array([[1],[2]])
d = np.array([10,20,30])
print("c + d 广播结果:\n", c + d)
案例4:通用函数与聚合
"""
ufunc_aggregation.py
演示ufunc和聚合函数,以及axis参数
"""
import numpy as np
arr = np.random.randn(3, 4) # 3行4列正态分布随机数
print("原始数组:\n", arr)
# 一元ufunc
print("绝对值前3个元素:\n", np.abs(arr[0:1]))
# 聚合函数
print("总和:", np.sum(arr))
print("均值:", np.mean(arr))
print("按行求和:", np.sum(arr, axis=1))
print("按列求均值:", np.mean(arr, axis=0))
# 累积运算
print("累积和:", np.cumsum(arr, axis=1))
print("累积乘积:", np.cumprod(arr, axis=1))
案例5:形状操作
"""
shape_manipulation.py
演示reshape, ravel, flatten, concatenate
"""
import numpy as np
arr = np.arange(12)
print("原始:", arr)
# reshape
reshaped = arr.reshape(3, 4)
print("reshape(3,4):\n", reshaped)
# ravel (视图)
flat = reshaped.ravel()
flat[0] = 99
print("原数组被修改:\n", reshaped)
# flatten (副本)
flattened = reshaped.flatten()
flattened[0] = 88
print("原数组不变:\n", reshaped)
# 拼接
a = np.array([[1,2],[3,4]])
b = np.array([[5,6],[7,8]])
v = np.vstack((a,b))
h = np.hstack((a,b))
print("垂直拼接:\n", v)
print("水平拼接:\n", h)
# 分割
split = np.split(reshaped, 3, axis=0)
print("分割为3部分:\n", split)
案例6:线性代数求解方程组
"""
linear_algebra.py
使用NumPy解线性方程组
"""
import numpy as np
# 方程组:
# 2x + y = 5
# x + 3y = 6
A = np.array([[2, 1], [1, 3]])
b = np.array([5, 6])
# 方法1: 求逆
x_inv = np.linalg.inv(A) @ b
print("逆矩阵法解:", x_inv)
# 方法2: solve
x_solve = np.linalg.solve(A, b)
print("solve法解:", x_solve)
# 验证
print("Ax:", A @ x_solve)
案例7:随机数模拟掷骰子
"""
dice_simulation.py
使用随机数模拟掷骰子并统计频率
"""
import numpy as np
np.random.seed(42)
n = 100000
dice = np.random.randint(1, 7, size=n)
# 统计各面次数
values, counts = np.unique(dice, return_counts=True)
print("点数:", values)
print("频率:", counts / n)
# 期望值约1/6 ≈ 0.16667
print("理论概率:", 1/6)
案例8:图像处理简单示例(数组操作)
"""
image_demo.py
模拟图像处理(二维灰度图)
"""
import numpy as np
# 模拟一个灰度图像(8x8,像素0-255)
img = np.random.randint(0, 256, size=(8,8), dtype=np.uint8)
print("原始图像:\n", img)
# 翻转(上下)
img_flipud = np.flipud(img)
print("上下翻转:\n", img_flipud)
# 亮度增强(乘以1.5,截断到255)
bright = np.clip(img * 1.5, 0, 255).astype(np.uint8)
print("亮度增强:\n", bright)
# 二值化(大于128为255,否则0)
binary = np.where(img > 128, 255, 0).astype(np.uint8)
print("二值化:\n", binary)
⚠️ 易错点避坑总结
| 序号 | 坑点描述 | 后果 | 解决方案 |
|---|---|---|---|
| 1 | 误以为切片返回副本 | 修改切片导致原数组改变 | 需要副本时使用.copy() |
| 2 | 不同数据类型的数组运算导致类型提升 | 结果精度可能超出预期 | 明确指定dtype或使用.astype() |
| 3 | 广播规则理解错误 | 维度不匹配导致ValueError | 检查形状,使用np.newaxis调整 |
| 4 | np.arange与np.linspace混淆 | 步长参数理解偏差 | 记住arange是步长,linspace是点数 |
| 5 | 花式索引与切片混淆 | 花式索引返回副本,不是视图 | 注意性能影响 |
| 6 | axis参数方向错误 | 对行求和/列求和混淆 | 记住axis=0是跨行(垂直),axis=1是跨列(水平) |
| 7 | 使用reshape时元素总数必须匹配 | ValueError | 确保新形状元素总数与原数组相同 |
| 8 | 随机数未设种子导致结果不可重现 | 调试困难 | 测试时设置np.random.seed() |
| 9 | 忘记导入numpy或使用别名np | NameError | 习惯import numpy as np |
| 10 | 在循环中逐个操作NumPy数组元素 | 失去向量化优势,速度极慢 | 改用向量化运算或ufunc |
📝 课后实战练习题
第1题:数组创建与运算
创建一个3x3的矩阵,元素为1-9,将其形状改为1x9,然后计算每个元素的平方根,再求和。
第2题:布尔索引筛选
生成一个包含100个随机整数的数组(范围0-100),筛选出所有大于60的元素,并计算其平均值。
第3题:矩阵乘法
定义两个矩阵A(2x3)和B(3x2),使用np.dot或@计算乘积,并验证形状。
第4题:使用广播实现归一化
有一个二维数组,形状(10,4),对每一列进行标准化:(列元素 - 列均值) / 列标准差。不使用循环。
第5题:统计考试成绩
给定一个5x3的成绩矩阵(5名学生,3门课),计算每个学生的总分、平均分,并找出总分最高的学生的索引。
第6题:正弦波信号生成
生成一个从0到4π,共1000个点的等间距序列,计算其正弦值,并找出正弦值大于0.5的点的数量。
第7题:图像处理(数组切片)
创建一个15x15的灰度图像(随机整数0-255),将中心5x5区域设置为0(黑色),四周设置为255(白色),并显示(用matplotlib,可选)。
第8题:解线性方程组
使用NumPy求解以下方程组:
3x + y = 9
x + 2y = 8
输出x和y的值。
🧠 知识点思维导图总结
🔜 下节课预告
NumPy是数据科学的基石。下一节课我们将学习Pandas,它是基于NumPy构建的数据分析库,提供了DataFrame等高级数据结构,让数据处理更加便捷。
第42课:Pandas核心用法:DataFrame、数据清洗与统计分析实战
内容包括:
- Series与DataFrame的创建和操作
- 数据的读取与写入(CSV、Excel)
- 数据清洗:缺失值处理、重复值删除、数据类型转换
- 数据筛选、排序、分组聚合
- 数据合并与连接(merge、concat)
- 实战:电影评分数据分析
Pandas是数据科学家的必备工具,学完后你将能高效处理表格数据。
🌟 学习鼓励:NumPy是Python数据科学生态的心脏。虽然它的语法相对简单,但理解向量化思维和广播是提高代码性能的关键。请多动手练习,尤其是对比Python原生循环和NumPy向量化的性能差异,你会对NumPy的强大有更深刻的体会。下一节课我们将进入Pandas,让数据分析变得更简单!
🔗《50节课 Python 从入门到精通》系列课程导航
🌟 感谢您耐心阅读到这里!
💡 如果本文对您有所启发欢迎:
👍 点赞📌 收藏 📤 分享给更多需要的伙伴。
🗣️ 期待在评论区看到您的想法, 共同进步。
🔔 关注我,持续获取更多干货内容~
🤗 我们下篇文章见~
2285

被折叠的 条评论
为什么被折叠?



