构建AI智能体:九、AI数据科学NumPy — 不可不知、由点及面抽丝剥茧+趣味范例
NumPy是Python科学计算的核心库,提供高性能的多维数组对象和丰富运算工具。本文全面介绍了NumPy的核心功能:1. 性能优势:对比Python列表,NumPy数组运算速度快30倍(0.001567秒 vs 0.045234秒)2. 核心功能:- 数组创建:np.array()、np.zeros()、np.arange()等 数组操作:索引切片、形状变换、数学运算 广播机制:处理不同形状数组
NumPy(Numerical Python)是 Python 科学计算生态系统的核心库,提供了高性能的多维数组对象和用于处理这些数组的工具。如:矩阵数据类型、矢量处理,以及精密的运算库。它是几乎所有数据科学、机器学习和科学计算库的基础。广泛应用于数据分析、机器学习等领域,是 Pandas 、 Scikit-learn 等库的基础。
下图是NumPy通过蒙特卡洛方法估算圆周率π,并提供详细的代码实现和可视化,感兴趣可在后文根据详细注解一探究竟;
一、NumPy的优势
1. 与Python 列表的比较
与Python列表的比较,直观体现性能优势:
import numpy as np
import time
# Python 列表的性能问题
python_list = list(range(1000000))
start_time = time.time()
result = [x * 2 for x in python_list]
end_time = time.time()
print(f"Python列表计算时间: {end_time - start_time:.6f}秒")
# NumPy 数组的性能优势
numpy_array = np.arange(1000000)
start_time = time.time()
result = numpy_array * 2
end_time = time.time()
print(f"NumPy数组计算时间: {end_time - start_time:.6f}秒")
结果展示:
Python列表计算时间: 0.045234秒
NumPy数组计算时间: 0.001567秒
2、主要优势
-
由于底层用 C 语言实现,运算速度极快
-
数组元素在内存中连续存储,效率极高
-
向量化操作,避免显式循环,一次操作整个数组
-
广播机制,处理不同形状的数组之间的逐元素运算
-
丰富的数学函数、线性代数、随机数生成等
二、由浅入深、循序渐进
1. 核心对象:ndarray
1.1 创建数组
import numpy as np
# 从列表创建
arr1 = np.array([1, 2, 3, 4, 5])
print("从列表创建:", arr1)
# 创建特殊数组
zeros = np.zeros((3, 4)) # 全0数组
ones = np.ones((2, 3)) # 全1数组
empty = np.empty((2, 2)) # 未初始化数组
full = np.full((2, 2), 7) # 填充指定值
identity = np.eye(3) # 单位矩阵
print("全0数组:\n", zeros)
print("单位矩阵:\n", identity)
# 创建序列数组
range_arr = np.arange(0, 10, 2) # 类似range,但返回数组
linear_arr = np.linspace(0, 1, 5) # 等间隔数列
random_arr = np.random.rand(3, 3) # 随机数组
print("arange创建:", range_arr)
print("linspace创建:", linear_arr)
print("随机数组:\n", random_arr)
输出结果:
从列表创建: [1 2 3 4 5]
全0数组:
[[0. 0. 0. 0.]
[0. 0. 0. 0.]
[0. 0. 0. 0.]]
单位矩阵:
[[1. 0. 0.]
[0. 1. 0.]
[0. 0. 1.]]
arange创建: [0 2 4 6 8]
linspace创建: [0. 0.25 0.5 0.75 1. ]
随机数组:
[[0.39898071 0.38841929 0.25706792]
[0.4805703 0.79778008 0.96680553]
[0.75744912 0.35779339 0.96884091]]
1.2 数组属性
import numpy as np
# 创建一个示例数组
arr = np.array([[1, 2, 3], [4, 5, 6]])
print("数组:", arr)
print("形状:", arr.shape) # 数组维度
print("大小:", arr.size) # 元素总数
print("维度:", arr.ndim) # 轴的数量
print("数据类型:", arr.dtype) # 元素类型
print("每个元素字节数:", arr.itemsize)
print("总字节数:", arr.nbytes)
输出结果:
组: [[1 2 3] [4 5 6]]
形状: (2, 3)
大小: 6
维度: 2
数据类型: int32
每个元素字节数: 4
总字节数: 24
2. 数组操作
2.1 索引和切片
import numpy as np
# 创建示例数组
arr = np.arange(1, 13).reshape(3, 4)
print("原始数组:\n", arr)
# 基本索引
print("第一个元素:", arr[0, 0])
print("第一行:", arr[0])
print("第一列:", arr[:, 0])
# 切片操作
print("前两行:\n", arr[:2])
print("后两列:\n", arr[:, -2:])
print("子数组:\n", arr[1:3, 1:3])
# 布尔索引
bool_mask = arr > 5
print("布尔掩码:\n", bool_mask)
print("大于5的元素:", arr[bool_mask])
# 花式索引
print("选择特定行:", arr[[0, 2]])
print("选择特定列:", arr[:, [1, 3]])
输出结果:
始数组:
[[ 1 2 3 4]
[ 5 6 7 8]
[ 9 10 11 12]]
第一个元素: 1
第一行: [1 2 3 4]
第一列: [1 5 9]
前两行:
[[1 2 3 4]
[5 6 7 8]]
后两列:
[[ 3 4]
[ 7 8]
[11 12]]
子数组:
[[ 6 7]
[10 11]]
布尔掩码:
[[False False False False]
[False True True True]
[ True True True True]]
大于5的元素: [ 6 7 8 9 10 11 12]
选择特定行: [[ 1 2 3 4]
[ 9 10 11 12]]
选择特定列: [[ 2 4]
[ 6 8]
[10 12]]
2.2 形状操作
import numpy as np
arr = np.arange(12)
print("一维数组:", arr)
# 改变形状
reshaped = arr.reshape(3, 4)
print("重塑为3x4:\n", reshaped)
# 展平数组
flattened = reshaped.flatten()
print("展平:", flattened)
# 转置
transposed = reshaped.T
print("转置:\n", transposed)
# 增加/减少维度
expanded = np.expand_dims(arr, axis=0)
print("增加维度:", expanded.shape)
squeezed = np.squeeze(expanded)
print("压缩维度:", squeezed.shape)
输出结果:
维数组: [ 0 1 2 3 4 5 6 7 8 9 10 11]
重塑为3x4:
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
展平: [ 0 1 2 3 4 5 6 7 8 9 10 11]
转置:
[[ 0 4 8]
[ 1 5 9]
[ 2 6 10]
[ 3 7 11]]
增加维度: (1, 12)
压缩维度: (12,)
3. 数组运算
3.1 数学运算
import numpy as np
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
# 算术运算
print("加法:", a + b)
print("减法:", a - b)
print("乘法:", a * b) # 元素乘法,不是矩阵乘法
print("除法:", a / b)
print("幂运算:", a ** 2)
# 比较运算
print("相等:", a == b)
print("大于:", a > 2)
# 聚合函数
print("总和:", np.sum(a))
print("均值:", np.mean(a))
print("标准差:", np.std(a))
print("最大值:", np.max(a))
print("最小值:", np.min(a))
# 三角函数
angles = np.array([0, np.pi/2, np.pi])
print("正弦值:", np.sin(angles))
输出结果:
法: [5 7 9]
减法: [-3 -3 -3]
乘法: [ 4 10 18]
除法: [0.25 0.4 0.5 ]
幂运算: [1 4 9]
相等: [False False False]
大于: [False False True]
总和: 6
均值: 2.0
标准差: 0.816496580927726
最大值: 3
最小值: 1
正弦值: [0.0000000e+00 1.0000000e+00 1.2246468e-16]
3.2 广播机制
import numpy as np
# 标量与数组
arr = np.array([[1, 2, 3], [4, 5, 6]])
print("数组 + 标量:\n", arr + 10)
# 不同形状数组
vector = np.array([10, 20, 30])
print("数组 + 向量:\n", arr + vector)
# 广播规则示例
a = np.array([[1], [2], [3]]) # 形状 (3, 1)
b = np.array([10, 20, 30]) # 形状 (3,)
print("广播加法:\n", a + b)
输出结果:
数组 + 标量:
[[11 12 13]
[14 15 16]]
数组 + 向量:
[[11 22 33]
[14 25 36]]
广播加法:
[[11 21 31]
[12 22 32]
[13 23 33]]
4. 实用功能
4.1 文件操作
import numpy as np
# 创建示例数据
data = np.random.rand(5, 3)
# 保存到文件
np.savetxt('data.txt', data, delimiter=',')
# 从文件加载
loaded_data = np.loadtxt('data.txt', delimiter=',')
print("从文件加载的数据:\n", loaded_data)
# 二进制格式(更高效)
np.save('data.npy', data)
binary_loaded = np.load('data.npy')
print("二进制加载的数据:\n", binary_loaded)
输出结果:
从文件加载的数据:
[[0.4553778 0.49906932 0.97523208]
[0.35906304 0.55012335 0.38348623]
[0.29169913 0.90129311 0.22774977]
[0.97325678 0.32749914 0.85698094]
[0.82758157 0.36911462 0.42524159]]
二进制加载的数据:
[[0.4553778 0.49906932 0.97523208]
[0.35906304 0.55012335 0.38348623]
[0.29169913 0.90129311 0.22774977]
[0.97325678 0.32749914 0.85698094]
[0.82758157 0.36911462 0.42524159]]
4.2 随机数生成
import numpy as np
# 设置随机种子(确保可重复性)
np.random.seed(42)
# 生成随机数
uniform = np.random.rand(5) # [0,1)均匀分布
normal = np.random.randn(5) # 标准正态分布
integers = np.random.randint(0, 10, 5) # 随机整数
print("均匀分布:", uniform)
print("正态分布:", normal)
print("随机整数:", integers)
# 随机抽样
choices = np.random.choice(['A', 'B', 'C'], size=10, p=[0.5, 0.3, 0.2])
print("随机选择:", choices)
输出结果:
匀分布: [0.37454012 0.95071431 0.73199394 0.59865848 0.15601864]
正态分布: [ 0.27904129 1.01051528 -0.58087813 -0.52516981 -0.57138017]
随机整数: [5 8 0 9 2]
随机选择: ['A' 'C' 'A' 'C' 'B' 'A' 'A' 'C' 'B' 'A']
5. 基础应用示例
5.1 数据处理
import numpy as np
# 创建示例数据集
data = np.array([
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]
])
# 数据筛选
filtered = data[data > 5]
print("大于5的值:", filtered)
# 条件修改
data[data % 2 == 0] = -1 # 将所有偶数改为-1
print("修改后的数据:\n", data)
# 数据统计
print("每列均值:", np.mean(data, axis=0))
print("每行最大值:", np.max(data, axis=1))
输出结果:
大于5的值: [ 6 7 8 9 10 11 12]
修改后的数据:
[[ 1 -1 3 -1]
[ 5 -1 7 -1]
[ 9 -1 11 -1]]
每列均值: [ 5. -1. 7. -1.]
每行最大值: [ 3 7 11]
5.2 简单图形处理
import numpy as np
# 模拟一个灰度图像(10x10像素)
image = np.random.randint(0, 256, (10, 10))
print("原始图像数据:\n", image)
# 图像处理操作
brightened = np.clip(image + 50, 0, 255) # 增加亮度
inverted = 255 - image # 颜色反转
threshold = (image > 128).astype(int) # 二值化
print("亮度增强:\n", brightened)
print("颜色反转:\n", inverted)
print("二值化:\n", threshold)
输出结果:
始图像数据:
[[ 23 218 156 90 149 255 116 104 8 60]
[ 70 42 74 180 227 17 189 92 11 90]
[207 61 110 223 21 153 47 136 20 225]
[ 8 93 153 81 81 14 112 107 249 50]
[160 149 95 60 214 233 149 247 127 7]
[140 183 32 82 247 128 174 16 44 212]
[ 34 203 210 89 98 59 48 200 181 111]
[111 23 99 101 108 130 65 215 123 205]
[170 93 210 84 230 213 82 126 87 247]
[187 250 8 157 78 54 54 227 89 164]]
亮度增强:
[[ 73 255 206 140 199 255 166 154 58 110]
[120 92 124 230 255 67 239 142 61 140]
[255 111 160 255 71 203 97 186 70 255]
[ 58 143 203 131 131 64 162 157 255 100]
[210 199 145 110 255 255 199 255 177 57]
[190 233 82 132 255 178 224 66 94 255]
[ 84 253 255 139 148 109 98 250 231 161]
[161 73 149 151 158 180 115 255 173 255]
[220 143 255 134 255 255 132 176 137 255]
[237 255 58 207 128 104 104 255 139 214]]
颜色反转:
[[232 37 99 165 106 0 139 151 247 195]
[185 213 181 75 28 238 66 163 244 165]
[ 48 194 145 32 234 102 208 119 235 30]
[247 162 102 174 174 241 143 148 6 205]
[ 95 106 160 195 41 22 106 8 128 248]
[115 72 223 173 8 127 81 239 211 43]
[221 52 45 166 157 196 207 55 74 144]
[144 232 156 154 147 125 190 40 132 50]
[ 85 162 45 171 25 42 173 129 168 8]
[ 68 5 247 98 177 201 201 28 166 91]]
二值化:
[[0 1 1 0 1 1 0 0 0 0]
[0 0 0 1 1 0 1 0 0 0]
[1 0 0 1 0 1 0 1 0 1]
[0 0 1 0 0 0 0 0 1 0]
[1 1 0 0 1 1 1 1 0 0]
[1 1 0 0 1 0 1 0 0 1]
[0 1 1 0 0 0 0 1 1 0]
[0 0 0 0 0 1 0 1 0 1]
[1 0 1 0 1 1 0 0 0 1]
[1 1 0 1 0 0 0 1 0 1]]
6. 性能调优和错误处理
6.1 性能优化
import numpy as np
# 不好的做法:使用Python循环
def slow_sum(arr):
result = 0
for x in arr:
result += x
return result
# 好的做法:使用NumPy向量化操作
def fast_sum(arr):
return np.sum(arr)
# 测试性能
large_arr = np.random.rand(1000000)
import time
start = time.time()
slow_result = slow_sum(large_arr)
end = time.time()
print(f"循环求和: {end - start:.6f}秒")
start = time.time()
fast_result = fast_sum(large_arr)
end = time.time()
print(f"向量化求和: {end - start:.6f}秒")
输出结果:
循环求和: 0.471191秒
向量化求和: 0.003989秒
6.2 常见错误
import numpy as np
# 错误:视图 vs 副本
arr = np.array([1, 2, 3, 4])
view = arr[1:3] # 这是视图,修改会影响原数组
copy = arr[1:3].copy() # 这是副本,独立于原数组
view[0] = 999
print("原数组被修改:", arr) # [1, 999, 3, 4]
copy[0] = 888
print("原数组不变:", arr) # [1, 999, 3, 4]
# 正确使用数据类型
float_arr = np.array([1, 2, 3], dtype=np.float64)
int_arr = np.array([1.5, 2.7, 3.1], dtype=np.int32) # 注意会截断
print("浮点数组:", float_arr)
print("整数数组:", int_arr) # [1, 2, 3]
输出结果:
原数组被修改: [ 1 999 3 4]
原数组不变: [ 1 999 3 4]
浮点数组: [1. 2. 3.]
整数数组: [1 2 3]
6.3 小结
-
数组创建:
np.array()
,np.zeros()
,np.ones()
,np.arange()
,np.linspace()
-
数组属性:
.shape
,.dtype
,.ndim
-
索引和切片:基本索引、布尔索引、花式索引
-
数组操作:
reshape()
,concatenate()
,stack()
-
数学运算:向量化运算、广播机制
-
通用函数:
np.sin()
,np.exp()
,np.sqrt()
等 -
线性代数:
np.dot()
,np.linalg.norm()
等
三、趣味示例
1. 井字棋游戏 - 理解二维数组
import numpy as np
# 创建游戏棋盘
def create_board():
"""
创建一个3x3的井字棋棋盘,初始状态为全0
返回值: 3x3的NumPy数组,数据类型为整数
0表示空位,1表示玩家X,2表示玩家O
"""
# np.zeros((3, 3), dtype=int) 创建一个3行3列的全0数组
return np.zeros((3, 3), dtype=int)
# 打印棋盘(美化输出)
def print_board(board):
"""
将数字表示的棋盘转换为可视化的字符格式并打印
参数:
board: 3x3的NumPy数组,表示当前棋盘状态
"""
# 定义数字到符号的映射字典
# 0 -> 空格, 1 -> 'X', 2 -> 'O'
symbols = {0: ' ', 1: 'X', 2: 'O'}
# 遍历每一行
for i in range(3):
# 将当前行的数字转换为对应的符号
row = [symbols[board[i, j]] for j in range(3)]
# 使用' | '连接符号并打印,前面加空格使输出居中
print(' ' + ' | '.join(row))
# 如果不是最后一行,打印分隔线
if i < 2:
print('-----------')
print() # 打印空行使输出更清晰
# 检查胜利条件
def check_win(board, player):
"""
检查指定玩家是否获胜
参数:
board: 3x3的NumPy数组,表示当前棋盘状态
player: 整数,1表示玩家X,2表示玩家O
返回值:
布尔值,True表示该玩家获胜,False表示未获胜
"""
# 检查所有行是否有获胜情况
# np.all() 检查给定轴上的所有元素是否为True
for i in range(3):
# 检查第i行是否全部由当前玩家的棋子占据
if np.all(board[i, :] == player):
return True
# 检查所有列是否有获胜情况
for j in range(3):
# 检查第j列是否全部由当前玩家的棋子占据
if np.all(board[:, j] == player):
return True
# 检查两条对角线是否有获胜情况
# np.diag() 获取矩阵的主对角线元素
# np.fliplr() 左右翻转矩阵,然后获取对角线即为反对角线
if np.all(np.diag(board) == player) or np.all(np.diag(np.fliplr(board)) == player):
return True
# 如果以上条件都不满足,则该玩家未获胜
return False
# 游戏演示
# 创建初始棋盘
board = create_board()
print("初始棋盘:")
print_board(board)
# 模拟几步游戏
# 玩家X在左上角(0,0)位置落子
board[0, 0] = 1 # X 在左上角
# 玩家O在中心(1,1)位置落子
board[1, 1] = 2 # O 在中心
# 玩家X在上中(0,1)位置落子
board[0, 1] = 1 # X 在上中
print("游戏进行中:")
print_board(board)
# 检查玩家X是否获胜
print("X 是否获胜?", check_win(board, 1))
输出结果:
初始棋盘:
| |
-----------
| |
-----------
| |
游戏进行中:
X | X |
-----------
| O |
-----------
| |
X 是否获胜? False
体现的知识点
-
np.zeros()
创建数组 -
二维数组索引
board[i, j]
-
np.all()
检查所有元素是否满足条件 -
np.diag()
获取对角线元素 -
np.fliplr()
左右翻转数组
2. 蒙特卡洛方法估算π的动态可视化
import numpy as np
import matplotlib.pyplot as plt
import time
from matplotlib.animation import FuncAnimation
def animated_monte_carlo(n_samples=10000, frame_interval=100):
"""
创建蒙特卡洛方法估算π的动态可视化
参数:
n_samples: 总采样点数,默认10000个点
frame_interval: 每帧更新的点数,默认每100个点更新一次动画
返回:
matplotlib.animation.FuncAnimation对象,用于控制动画
"""
# 生成所有随机点:在[-1, 1] x [-1, 1]的正方形区域内均匀采样
# points是一个n_samples x 2的数组,每行代表一个点的(x,y)坐标
points = np.random.uniform(-1, 1, size=(n_samples, 2))
# 计算每个点到原点(0,0)的距离
# 使用欧几里得距离公式:sqrt(x² + y²)
distances = np.sqrt(points[:, 0]**2 + points[:, 1]**2)
# 创建图形窗口,包含左右两个子图
# 左图显示随机点和单位圆,右图显示π估计值的收敛过程
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5))
# 初始化左图的散点图(开始时没有点)
scatter = ax1.scatter([], [], s=1, alpha=0.5) # 小点,半透明
ax1.set_xlim(-1, 1) # 设置x轴范围
ax1.set_ylim(-1, 1) # 设置y轴范围
ax1.set_aspect('equal') # 保持纵横比相等,确保圆看起来是圆形
# 在左图绘制单位圆(半径为1的圆)
# 这个圆用于可视化:圆内的点表示落在单位圆内
circle = plt.Circle((0, 0), 1, color='green', fill=False, linewidth=2)
ax1.add_patch(circle) # 将圆添加到图中
# 初始化右图的收敛曲线(开始时没有数据)
line, = ax2.plot([], [], linewidth=1)
# 在右图添加一条水平线表示π的真实值
ax2.axhline(y=np.pi, color='r', linestyle='--', label='True π')
ax2.set_xlim(0, n_samples) # 设置x轴范围(点数)
ax2.set_ylim(2.8, 3.4) # 设置y轴范围(π的估计值范围)
ax2.set_xlabel('Number of points') # x轴标签
ax2.set_ylabel('Estimate of π') # y轴标签
ax2.set_title('Convergence of π Estimate') # 右图标题
ax2.legend() # 显示图例
ax2.grid(True) # 显示网格
# 在左图添加文本框,显示当前点数、π估计值和误差
text = ax1.text(0.05, 0.95, '', transform=ax1.transAxes, fontsize=12,
verticalalignment='top', bbox=dict(boxstyle='round', facecolor='wheat', alpha=0.8))
# 动画更新函数 - 每一帧调用一次
def update(frame):
# 计算当前帧应该显示的点数
# 每frame_interval个点更新一次,但不超过总点数n_samples
n = min((frame + 1) * frame_interval, n_samples)
# 更新左图的散点图
# 确定哪些点落在单位圆内(距离<=1)
inside = distances[:n] <= 1
# 落在圆外的点
outside = ~inside # 逻辑非操作
# 创建颜色数组:圆内的点为蓝色,圆外的点为红色
colors = np.empty(n, dtype=object) # 创建空对象数组
colors[inside] = 'blue' # 圆内的点设为蓝色
colors[outside] = 'red' # 圆外的点设为红色
# 更新散点图的位置和颜色
scatter.set_offsets(points[:n]) # 设置点的位置
scatter.set_color(colors) # 设置点的颜色
# 更新右图的收敛曲线
# 计算累积落在圆内的点数(使用累积和)
cumulative_inside = np.cumsum(inside)
# 计算π的估计值:4 * (圆内点数 / 总点数)
# 根据公式:π ≈ 4 * (圆内点数 / 总点数)
cumulative_estimate = 4 * cumulative_inside / (np.arange(n) + 1)
# 更新收敛曲线的数据
line.set_data(np.arange(n), cumulative_estimate)
# 更新文本信息
# 获取当前的π估计值(最后一个值)
current_estimate = cumulative_estimate[-1] if n > 0 else 0
# 设置文本内容:点数、估计值和误差
text.set_text(f'Points: {n:,}\nEstimate: {current_estimate:.6f}\nError: {abs(current_estimate - np.pi):.6f}')
# 返回需要更新的图形元素
return scatter, line, text
# 计算总帧数:总点数除以每帧更新的点数
frames = n_samples // frame_interval
# 创建动画对象
# fig: 动画所在的图形
# update: 更新函数
# frames: 总帧数
# interval: 帧间隔时间(毫秒)
# blit: 使用blitting技术优化动画(只重绘变化的部分)
ani = FuncAnimation(fig, update, frames=frames, interval=50, blit=True)
# 调整子图布局,避免重叠
plt.tight_layout()
# 显示动画
plt.show()
# 返回动画对象,以便后续控制(如保存为视频)
return ani
# 运行动画
# 使用10000个点,每100个点更新一次动画
ani = animated_monte_carlo(n_samples=10000, frame_interval=100)
-
基本原理
这段代码实现了使用蒙特卡洛方法估算π值的动态可视化,其数学原理如下:
-
基本思想:在一个边长为2的正方形内随机撒点,统计落在内切圆(半径为1)内的点数
-
面积关系:
-
正方形面积 = 2 × 2 = 4
-
圆形面积 = π × 1² = π
-
-
概率估计:点在圆内的概率 = 圆形面积 / 正方形面积 = π/4
-
π估算公式:π ≈ 4 × (圆内点数 / 总点数)
-
代码亮点解析
-
向量化计算:使用NumPy一次性生成所有随机点并计算距离,避免了低效的Python循环
-
动态可视化:使用Matplotlib的动画功能实时展示估算过程
-
双视图设计:左侧显示随机点分布,右侧显示π估计值的收敛过程
-
性能优化:
-
使用
blit=True
只重绘变化的部分,提高动画性能 -
分批更新点(每100个点更新一次),平衡视觉效果和性能
-
-
信息丰富:实时显示当前点数、π估计值和误差,方便观察收敛过程
3. 图像滤镜 - 理解数组运算
import numpy as np
from PIL import Image
import matplotlib.pyplot as plt
# 设置中文字体支持
# 确保图表中的中文标题能够正确显示
plt.rcParams['font.sans-serif'] = ['SimHei'] # 使用黑体显示中文
plt.rcParams['axes.unicode_minus'] = False # 解决负号显示问题
# 创建简单的测试图像
def create_test_image():
"""
创建一个自定义的渐变测试图像,用于演示图像处理效果
返回:
一个100x100像素的RGB图像,数据类型为uint8(0-255范围)
"""
# 创建从0到1的等间距数组,用于x和y坐标
x = np.linspace(0, 1, 100) # 100个点,从0到1
y = np.linspace(0, 1, 100) # 100个点,从0到1
# 创建网格坐标矩阵
# xx的每一行都是x的复制,yy的每一列都是y的复制
xx, yy = np.meshgrid(x, y)
# 创建RGB三个通道的渐变效果
red = xx # 红色通道:水平渐变(从左到右增强)
green = yy # 绿色通道:垂直渐变(从下到上增强)
blue = (xx + yy) / 2 # 蓝色通道:对角线渐变(从右下到左上增强)
# 将三个通道堆叠成三维数组(高度×宽度×通道)
# 然后缩放到0-255范围并转换为8位无符号整数
image = np.stack([red, green, blue], axis=2)
return (image * 255).astype(np.uint8)
# 定义彩虹色条纹的颜色值(RGB格式,范围0-1)
colors = [
[1, 0, 0], # 红色
[1, 0.5, 0], # 橙色
[1, 1, 0], # 黄色
[0, 1, 0], # 绿色
[0, 0, 1], # 蓝色
[0.5, 0, 0.5],# 紫色
]
# 创建彩虹色条纹图像
def create_rainbow_stripes(size=100):
"""
创建一个彩虹色条纹图像
参数:
size: 图像的大小(将创建size×size的正方形图像)
返回:
一个包含彩虹色条纹的RGB图像
"""
# 创建一个全零的三维数组(高度×宽度×3个颜色通道)
image = np.zeros((size, size, 3))
# 计算每个条纹的宽度(将图像水平分成6等份)
stripe_width = size // len(colors)
# 填充彩虹色条纹
for i, color in enumerate(colors):
# 计算当前条纹的起始和结束列索引
start = i * stripe_width
end = (i + 1) * stripe_width if i < len(colors) - 1 else size
# 将当前条纹的所有行和指定列范围设置为当前颜色
image[:, start:end] = color
return image
# 应用各种滤镜
def apply_filters(image):
"""
对输入图像应用多种滤镜效果
参数:
image: 输入图像(NumPy数组,uint8类型,0-255范围)
返回:
四个处理后的图像:灰度图、边缘检测图、颜色反转图、sepia色调图
"""
# 将图像转换为浮点数并归一化到0-1范围,便于数学运算
img_float = image.astype(float) / 255.0
# 1. 灰度化:将彩色图像转换为灰度图像
# 方法:取RGB三个通道的平均值
gray = np.mean(img_float, axis=2)
# 2. 边缘检测(使用简化的Sobel算子)
# 定义Sobel算子的x方向和y方向卷积核
sobel_x = np.array([[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]]) # 水平边缘检测
sobel_y = np.array([[-1, -2, -1], [0, 0, 0], [1, 2, 1]]) # 垂直边缘检测
# 初始化边缘强度图
edges = np.zeros_like(gray)
# 对每个颜色通道分别应用Sobel算子
for i in range(3):
# 获取当前颜色通道
channel = img_float[:, :, i]
# 使用卷积计算x方向和y方向的梯度
# 注意:这里使用了一维卷积,需要先将二维数组展平
gx = np.abs(np.convolve(channel.flatten(), sobel_x.flatten(), mode='same').reshape(channel.shape))
gy = np.abs(np.convolve(channel.flatten(), sobel_y.flatten(), mode='same').reshape(channel.shape))
# 计算梯度幅值并累加到边缘图中
edges += np.sqrt(gx**2 + gy**2)
# 对三个通道的结果取平均
edges = edges / 3.0
# 3. 颜色反转:用1减去每个像素值,实现颜色反转效果
inverted = 1.0 - img_float
# 4. Sepia色调(棕褐色调效果)
# 定义Sepia色调的转换矩阵
sepia_matrix = np.array([[0.393, 0.769, 0.189], # 红色分量转换系数
[0.349, 0.686, 0.168], # 绿色分量转换系数
[0.272, 0.534, 0.131]]) # 蓝色分量转换系数
# 应用矩阵乘法实现颜色转换
sepia = np.dot(img_float, sepia_matrix.T)
# 确保值在0-1范围内(使用np.clip防止溢出)
sepia = np.clip(sepia, 0, 1)
return gray, edges, inverted, sepia
# 创建并处理图像
# 生成测试图像
image = create_test_image()
# 应用各种滤镜
gray, edges, inverted, sepia = apply_filters(image)
# 创建彩虹色条纹图像
rainbow_image = create_rainbow_stripes()
# 显示结果
# 创建2行3列的子图布局
fig, axes = plt.subplots(2, 3, figsize=(15, 10))
# 第一行第一列:原始图像
axes[0, 0].imshow(image)
axes[0, 0].set_title('原始图像')
axes[0, 0].axis('off') # 隐藏坐标轴
# 第一行第二列:灰度化效果
axes[0, 1].imshow(gray, cmap='gray') # 使用灰度颜色映射
axes[0, 1].set_title('灰度化')
axes[0, 1].axis('off')
# 第一行第三列:边缘检测效果
axes[0, 2].imshow(edges, cmap='hot') # 使用热力图颜色映射
axes[0, 2].set_title('边缘检测')
axes[0, 2].axis('off')
# 第二行第一列:颜色反转效果
axes[1, 0].imshow(inverted)
axes[1, 0].set_title('颜色反转')
axes[1, 0].axis('off')
# 第二行第二列:Sepia色调效果
axes[1, 1].imshow(sepia)
axes[1, 1].set_title('Sepia色调')
axes[1, 1].axis('off')
# 第二行第三列:彩虹条纹图像
axes[1, 2].imshow(rainbow_image)
axes[1, 2].set_title('彩虹条纹')
axes[1, 2].axis('off')
# 自动调整子图参数,使之填充整个图像区域
plt.tight_layout()
# 显示图形
plt.show()
体现的知识点
-
数组创建和形状操作
-
数学运算和广播
-
数组索引和切片
-
np.convolve()
卷积操作 -
np.clip()
限制数值范围 -
np.dot()
矩阵乘法
代码技术亮点
-
数组操作: 使用
np.linspace
,np.meshgrid
创建坐标网格 -
广播机制: 在创建渐变图像时利用了NumPy的广播功能
-
矩阵运算: 在Sepia滤镜中使用矩阵乘法实现颜色转换
-
卷积操作: 在边缘检测中使用卷积计算图像梯度
-
数组索引和切片: 在创建彩虹条纹时使用切片操作高效赋值
四、总结
操作 |
代码示例 |
说明 |
创建数组 |
np.array([1,2,3]) |
从列表创建 |
全零数组 |
np.zeros((3,3)) |
创建全0数组 |
全一数组 |
np.ones((2,2)) |
创建全1数组 |
范围数组 |
np.arange(0,10,2) |
创建等差数组 |
随机数组 |
np.random.rand(3,3) |
创建随机数组 |
改变形状 |
arr.reshape(2,3) |
改变数组形状 |
数组拼接 |
np.concatenate([a,b]) |
连接数组 |
数学运算 |
arr + 1, |
向量化运算 |
统计函数 |
np.mean(arr), |
统计计算 |

为武汉地区的开发者提供学习、交流和合作的平台。社区聚集了众多技术爱好者和专业人士,涵盖了多个领域,包括人工智能、大数据、云计算、区块链等。社区定期举办技术分享、培训和活动,为开发者提供更多的学习和交流机会。
更多推荐
所有评论(0)