
本教程旨在介绍如何利用numpy的`np.lib.stride_tricks.as_strided`功能,结合查找表机制,高效地对2d数组进行2x2块级别的修改。通过创建数组视图而非复制数据,并利用高级索引进行矢量化操作,此方法显著优于传统的python循环,极大提升了处理大型数据集的性能。
在处理大型NumPy数组时,如果需要对数组中的局部小块(例如2x2的子矩阵)进行模式识别和替换操作,传统的Python循环(如使用itertools.product遍历坐标)会因为解释器开销和逐元素操作而效率低下。NumPy的核心优势在于其矢量化操作,能够将循环操作推送到C语言层面执行,从而获得显著的性能提升。本教程的目标是展示如何采用NumPy原生的、矢量化的方式来高效地实现2D数组的2x2块级修改。
实现高效块级操作的关键在于np.lib.stride_tricks.as_strided。这个函数允许我们创建一个现有NumPy数组的“视图”,而无需复制底层数据。通过巧妙地定义视图的shape(形状)和strides(步幅),我们可以将一个2D数组解释为一个由多个2x2块组成的更高维数组。对这个视图的修改会直接反映在原始数组上,从而避免了昂贵的数据复制操作。
shape: 新视图的形状。对于一个 (ny, nx) 的原始2D数组 A,如果我们要以 (2, 2) 的块进行操作,那么新的视图的形状将是 (ny//2, nx//2, 2, 2)。这表示视图包含 ny//2 行和 nx//2 列,每个“元素”本身又是一个 (2, 2) 的子数组。
strides: 定义了在内存中沿着某个维度移动一个单位所需的字节数。
import numpy as np
# 示例数据:一个10x10的二进制数组
A = np.random.randint(0, 2, (10, 10))
print("原始数组 A:")
print(A)
# 计算视图的形状和步幅
ny, nx = A.shape
block_rows = ny // 2
block_cols = nx // 2
# 创建2x2块的视图
Av = np.lib.stride_tricks.as_strided(A,
shape=(block_rows, block_cols, 2, 2),
strides=(A.strides[0] * 2, A.strides[1] * 2,
A.strides[0], A.strides[1]))
print("\n通过as_strided创建的2x2块视图 Av 的形状:", Av.shape)
print("Av[0,0] (原始数组A的第一个2x2块):\n", Av[0,0])
# 验证视图是否指向原始数据 (修改视图会影响原始数组)
# Av[0,0,0,0] = 99 # 修改视图的第一个元素
# print("\n修改Av[0,0,0,0]后,原始数组A的左上角:\n", A[:2,:2]) # 原始数组A也会随之改变当每个2x2块的替换规则依赖于其内部的四个元素时,我们可以构建一个多维查找表(Lookup Table, LUT)。对于由布尔值(0或1)组成的2x2块,共有 2^4 = 16 种可能的状态。查找表将以前四个元素的值作为索引,存储对应的2x2替换块。
查找表的形状将是 (2, 2, 2, 2, 2, 2),其中前四个 2 对应输入2x2块的四个元素 (0,0), (0,1), (1,0), (1,1) 的值,后两个 2 对应替换后的2x2块的形状。
# 示例查找表 (lut[val00, val01, val10, val11] = replacement_block)
# lut的形状为 (2,2,2,2, 2,2)
# 前四个2代表2x2块的四个元素 (0,0), (0,1), (1,0), (1,1) 的值
# 后两个2代表替换后的2x2块的形状
lut = np.zeros((2, 2, 2, 2, 2, 2), dtype=A.dtype)
# 填充一些转换规则。例如:
# 1. 原始块 [[0,0],[0,0]] 替换为 [[1,1],[1,1]]
lut[0, 0, 0, 0] = [[1, 1], [1, 1]]
# 2. 原始块 [[0,0],[0,1]] 替换为 [[1,1],[1,0]]
lut[0, 0, 0, 1] = [[1, 1], [1, 0]]
# 3. 原始块 [[0,1],[0,0]] 替换为 [[1,1],[0,1]]
lut[0, 1, 0, 0] = [[1, 1], [0, 1]]
# 4. 原始块 [[1,1],[0,0]] 替换为 [[1,1],[1,1]]
lut[1, 1, 0, 0] = [[1, 1], [1, 1]]
# ... 可以根据需要填充所有16种情况
# 应用查找表进行块替换
# 使用高级索引,将Av中的每个2x2块的四个元素作为lut的索引
Av[:] = lut[Av[..., 0, 0], Av[..., 0, 1], Av[..., 1, 0], Av[..., 1, 1]]
print("\n方法一:使用多维查找表后的数组 A:")
print(A)为了简化查找表的索引,我们可以将每个2x2块的四个布尔值转换为一个单一的整数索引(0-15)。这可以通过为每个位置分配一个权重并求和来实现。这种方法可以使查找表更紧凑,并可能简化索引逻辑。
我们可以定义一个权重矩阵,例如:
[[8, 4], [2, 1]]
对于一个2x2块 [[v00, v01], [v10, v11]],其对应的单一索引将是 v00*8 + v01*4 + v10*2 + v11*1。这个索引的范围是0到15。
# 重新初始化数组A进行演示
A = np.random.randint(0, 2, (10, 10))
ny, nx = A.shape
block_rows = ny // 2
block_cols = nx // 2
Av = np.lib.stride_tricks.as_strided(A,
shape=(block_rows, block_cols, 2, 2),
strides=(A.strides[0] * 2, A.strides[1] * 2,
A.strides[0], A.strides[1]))
print("\n重新初始化后的原始数组 A:")
print(A)
# 示例一维查找表 (lut2[index] = replacement_block)
lut2 = np.zeros((16, 2, 2), dtype=A.dtype)
# 填充一些转换规则。例如:
# 索引0 (对应原始块 [[0,0],[0,0]]) 替换为 [[1,1],[1,1]]
lut2[0] = [[1, 1], [1, 1]]
# 索引1 (对应原始块 [[0,0],[0,1]]) 替换为 [[1,1以上就是NumPy 2D数组高效块级修改:基于视图与查找表的策略的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号