新闻中心

使用NumPy高效修改二维数组:2x2块操作的Stride Tricks技巧

2025-11-06
浏览次数:
返回列表

使用NumPy高效修改二维数组:2x2块操作的Stride Tricks技巧

本教程详细介绍了如何利用numpy的`np.lib.stride_tricks.as_strided`函数高效地对二维数组进行2x2块的修改。文章通过创建数组的“块视图”并结合查找表(lut)机制,避免了传统python循环的性能瓶颈。内容涵盖了多维索引和扁平化索引两种lut构建方法,并提供了详细的代码示例与注意事项,旨在帮助读者掌握numpy高级技巧,优化大规模数组的块级操作性能。

引言:高效处理NumPy二维数组的块操作

在数据处理和科学计算中,我们经常需要对二维数组的局部区域,特别是固定大小的块(例如2x2)进行遍历和修改。传统的Python循环虽然直观,但在处理大型NumPy数组时效率低下,因为它无法充分利用NumPy底层C语言实现的优化。为了克服这一性能瓶颈,NumPy提供了一系列高级工具,其中np.lib.stride_tricks.as_strided是一个强大且灵活的函数,能够让我们以非传统的方式“查看”数组,从而实现高效的块级操作。本教程将深入探讨如何结合as_strided和查找表(Lookup Table, LUT)来高效地修改NumPy二维数组的2x2块。

核心技术:利用np.lib.stride_tricks.as_strided创建块视图

np.lib.stride_tricks.as_strided是一个用于创建数组新视图的函数。它的强大之处在于,你可以手动指定新视图的形状(shape)和步长(strides),而无需复制原始数据。这意味着对视图的修改会直接反映在原始数组上,极大地提高了内存效率和操作速度。

要将一个二维数组A(例如ny行nx列)转换为一个由2x2块组成的视图,我们需要理解shape和strides的含义:

  • shape: 新视图的形状。如果原始数组是ny行nx列,我们想将其看作(ny/2)行(nx/2)列的2x2块,那么新视图的形状将是(ny/2, nx/2, 2, 2)。前两个维度代表块的行和列索引,后两个维度代表每个块内部的行和列索引。
  • strides: 新视图中每个维度移动一个单位所需的字节数。
    • 对于块的行移动:原始数组每向下移动2行,新视图的块行索引才移动1。所以,块行的步长是原始数组行步长的两倍:A.strides[0] * 2。
    • 对于块的列移动:原始数组每向右移动2列,新视图的块列索引才移动1。所以,块列的步长是原始数组列步长的两倍:A.strides[1] * 2。
    • 对于块内部的行移动:新视图的第三个维度代表块内部的行。从块内第一行到第二行,实际上是在原始数组中向下移动了一行。所以,块内部行的步长是原始数组行步长:A.strides[0]。
    • 对于块内部的列移动:新视图的第四个维度代表块内部的列。从块内第一列到第二列,实际上是在原始数组中向右移动了一列。所以,块内部列的步长是原始数组列步长:A.strides[1]。

综合起来,strides参数将是(A.strides[0]*2, A.strides[1]*2, A.strides[0], A.strides[1])。

代码示例1:创建块视图

import numpy as np

# 假设原始数组A是一个10x10的0/1值数组
A = np.random.randint(0, 2, (10, 10))
print("原始数组 A:\n", A)

# 计算新视图的形状
# 如果A是(ny, nx),那么块视图的形状是(ny//2, nx//2, 2, 2)
block_rows = A.shape[0] // 2
block_cols = A.shape[1] // 2

# 创建块视图
# A.strides[0] 是行步长,A.strides[1] 是列步长
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块视图 Av 的形状:", Av.shape)
# 验证 Av[0,0] 是否是 A 的左上角2x2块
print("\nAv[0,0] (第一个2x2块):\n", Av[0, 0])
print("\nA[0:2, 0:2] (A的左上角2x2块):\n", A[0:2, 0:2])

# 验证修改Av会影响A
Av[0, 0] = [[9, 9], [9, 9]]
print("\n修改 Av[0,0] 后,A 的左上角2x2块:\n", A[0:2, 0:2])

使用查找表(Lookup Table, LUT)进行块转换

一旦我们有了块视图Av,就可以使用查找表来根据每个2x2块的当前值来决定其新的值。查找表的构建方式可以有多种,这里介绍两种常见且高效的方法。

假设我们的2x2块中的元素都是0或1(布尔值)。一个2x2的块共有 $2^4 = 16$ 种可能的组合。

方法一:多维索引查找表

这种方法为查找表lut创建多个维度,每个维度对应2x2块中的一个元素的值。例如,一个lut的形状可以是(2, 2, 2, 2, 2, 2),其中前四个2代表输入块的四个元素([0,0], [0,1], [1,0], [1,1])的可能值(0或1),后两个2代表输出的2x2块。

构建查找表:

# lut 的形状:(输入块[0,0], 输入块[0,1], 输入块[1,0], 输入块[1,1], 输出块行, 输出块列)
lut = np.zeros((2, 2, 2, 2, 2, 2), dtype=A.dtype)

# 填充一些转换规则 (示例,根据实际需求定义)
# 假设输入块 [[0,0],[0,0]] 转换为 [[1,1],[1,1]]
lut[0, 0, 0, 0] = [[1, 1], [1, 1]]
# 假设输入块 [[0,0],[0,1]] 转换为 [[1,1],[1,0]]
lut[0, 0, 0, 1] = [[1, 1], [1, 0]]
# 假设输入块 [[1,1],[0,0]] 转换为 [[1,1],[1,1]]
lut[1, 1, 0, 0] = [[1, 1], [1, 1]]
# 其他未定义的组合将保持为0(根据lut的初始化)

应用查找表:

易标AI 易标AI

告别低效手工,迎接AI标书新时代!3分钟智能生成,行业唯一具备查重功能,自动避雷废标项

易标AI 135 查看详情 易标AI

通过高级索引,我们可以直接将Av中每个2x2块的四个元素作为lut的索引,从而一次性完成所有块的转换。

# 重新初始化A以进行演示
A = np.random.randint(0, 2, (10, 10))
print("应用多维LUT前的 A:\n", A)

block_rows = A.shape[0] // 2
block_cols = A.shape[1] // 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]))

# 使用高级索引应用LUT
# Av[...,0,0] 获取所有块的[0,0]元素组成的数组
# Av[...,0,1] 获取所有块的[0,1]元素组成的数组,以此类推
Av[:] = lut[Av[..., 0, 0], Av[..., 0, 1], Av[..., 1, 0], Av[..., 1, 1]]

print("\n应用多维LUT后的 A:\n", A)

方法二:扁平化索引查找表

这种方法首先将每个2x2的0/1块转换成一个单一的整数索引(0-15),然后使用这个整数索引来查找一个一维的查找表。这种方式可以使查找表的定义更紧凑。

将2x2块转换为单一索引:

一个2x2的0/1块可以看作一个4位的二进制数。例如,块[[a,b],[c,d]]可以转换为索引 a*8 + b*4 + c*2 + d*1。

# 定义权重矩阵
weights = np.array([[8, 4], [2, 1]])

# 计算每个2x2块的扁平化索引
# (Av * weights) 会对每个2x2块内部进行元素级乘法
# .sum(axis=(2,3)) 会将每个2x2块内部的元素求和,得到一个 (block_rows, block_cols) 形状的索引数组
idx = (Av * weights).sum(axis=(2, 3))

构建扁平化查找表:

lut2的形状将是(16, 2, 2),其中16代表所有可能的输入块索引。

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],[1,0]]
lut2[1] = [[1, 1], [1, 0]]
# 索引12 (即块[[1,1],[0,0]]) 转换为 [[1,1],[1,1]]
lut2[12] = [[1, 1], [1, 1]]
# 其他未定义的组合将保持为0

应用查找表:

# 重新初始化A以进行演示
A = np.random.randint(0, 2, (10, 10))
print("应用扁平化LUT前的 A:\n", A)

block_rows = A.shape[0] // 2
block_cols = A.shape[1] // 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]))

# 计算扁平化索引
idx = (Av * weights).sum(axis=(2, 3))

# 使用扁平化索引应用LUT
Av[:] = lut2[idx]

print("\n应用扁平化LUT后的 A:\n", A)

局部块修改

as_strided创建的视图支持常规的NumPy切片操作。这意味着你可以只对原始数组的某个特定区域的块进行修改,而不是整个数组。

# 重新初始化A
A = np.random.randint(0, 2, (10, 10))
print("进行局部修改前的 A:\n", A)

block_rows = A.shape[0] // 2
block_cols = A.shape[1] // 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]))

# 假设我们只想修改Av中索引为 (2,2) 到 (3,3) 的块区域
# 使用扁平化LUT进行修改
weights = np.array([[8, 4], [2, 1]])
lut2 = np.zeros((16, 2, 2), dtype=A.dtype)
lut2[0] = [[1, 1], [1, 1]] # 示例规则

# 计算指定区域块的索引
idx_partial = (Av[2:4, 2:4] * weights).sum(axis=(2, 3))

# 对指定区域的块进行修改
Av[2:4, 2:4] = lut2[idx_partial]

print("\n进行局部修改后的 A:\n", A)

注意事项与最佳实践

  1. 视图特性与内存效率: as_strided创建的是一个视图,不涉及数据复制。这意味着它非常内存高效,并且对视图的任何修改都会直接作用于原始数组。然而,这也要求使用者对视图的结构有清晰的理解,避免意外修改。
  2. 数据类型兼容性: 上述查找表方法假设块中的元素是0或1(布尔值或整数)。如果你的数组包含其他类型或更大范围的值,你需要相应地调整查找表的维度和索引转换逻辑。
  3. 性能优势: 这种基于NumPy向量化操作和as_strided的方法,相比于Python的for循环和itertools.product,能够带来显著的性能提升,尤其是在处理大型数组时。
  4. as_strided的谨慎使用: as_strided是一个低级函数,使用不当可能导致访问越界或创建无效视图,从而引发难以调试的问题。务必确保shape和strides参数的计算是准确的。
  5. 块的内存非连续性: 虽然as_strided创建的视图本身是连续的(逻辑上),但视图中的每个2x2小块在原始数组的内存中可能不是完全连续的。例如,Av[i,j]是一个2x2的视图,它内部的元素在原始数组中是连续的,但Av[i,j]作为一个整体,其与Av[i,j+1]之间存在跳跃。因此,直接对Av[i,j]调用如tobytes()这类依赖内存连续性的方法,可能无法得到预期的结果。本教程中的方法通过提取块的作为索引,有效规避了这一问题。
  6. 通用性: 这种方法不仅限于2x2块,可以推广到任意大小的块(例如3x3),只需相应调整shape和strides的计算以及查找表的维度或索引转换逻辑。

总结

通过巧妙地运用np.lib.stride_tricks.as_strided创建数组的块视图,并结合查找表机制,我们可以高效、内存友好地对NumPy二维数组的固定大小块进行批量修改。这种方法将复杂的循环逻辑转化为NumPy底层的向量化操作,显著提升了处理大规模数据的性能。掌握这一高级技巧,将使你在NumPy数据处理中如虎添翼。

以上就是使用NumPy高效修改二维数组:2x2块操作的Stride Tricks技巧的详细内容,更多请关注其它相关文章!


# 这种方法  # 鹤壁网站策划推广企业  # 建设企业新资质查询网站  # 黑帽SEO反推软件  # 西安网站优化平台  # 衡水seo公司选择12火星  # 如何做seo 推广  # 中国香港企业网站建设  # 手工素材网站推广文案  # 如何做好网站线上推广  # 上虞网站优化选哪家  # 两种  # 你可以  # python  # 将是  # 是在  # 这一  # 扁平化  # 多维  # 是一个  # 转换为  # 性能瓶颈  # 工具  # 字节  # c语言 


相关栏目: 【 科技资讯46185 】 【 网络学院92790


相关推荐: 谷歌浏览器一键优化方案_谷歌浏览器直达主页极速不卡版  C++如何生成随机数_C++ random库使用方法与范围设置  蛙漫漫画官网在线入口 蛙漫全本漫画免费阅读平台  DLsite中文平台入口 DLsite官网内容在线查看  谷歌浏览器无痕模式怎么开 Chrome开启无痕浏览设置方法【教程】  蛙漫2台版漫画地址 Manwa2正版网页版链接  如何更改在 Excel 中打开超链接时的默认浏览器  163邮箱注册官网 免费申请163个人邮箱  AO3官方镜像站点汇总 AO3同人作品网页版直达链接  构建轻量级网站内部消息系统:Formspree 集成指南  动漫花园资源网使用步骤_动漫花园资源网下载流程  TikTok评论显示延迟如何处理 TikTok评论刷新优化方法  QQ邮箱网页版入口页面 QQ邮箱在线登录入口官网  QQ邮箱登录首页官网地址2026 QQ邮箱官方网页入口  如何将一个大型PHP应用拆分为多个Composer包_微服务与模块化架构的Composer实践  支付宝解绑银行卡步骤_支付宝如何解除绑定银行卡  将JSON对象数组转置为键值对列表的实用指南  yandex入口引擎手机版 yandex安卓版下载入口  MAC怎么让Dock栏只显示当前运行的应用_MAC终端命令实现极简Dock栏  如何使用Go和Martini动态服务解码后的图片  TikTok国际版官网直达_TikTok国际版官网直达进入在线观看  Golang如何优雅处理error_Golang error处理最佳实践总结  动漫共和国防屏蔽稳定域名-动漫共和国官方正版直达通道  漫蛙2(台版)官方入口地址 漫蛙2(台版)正版漫画网页端  抖音网页版企业服务中心登录入口_抖音网页版企业登录平台  在Go语言中利用后缀数组处理多字符串:实现高效文本匹配与自动补全  Sublime Text怎么显示空格和制表符_Sublime显示不可见字符设置  Lar*el头像管理:图片缩放与旧文件删除的最佳实践  微博网页版首页入口 微博电脑端官网登录链接  win11专注助手在哪 Win11免打扰模式设置与自动化规则【指南】  C++如何实现异步操作_C++11使用std::future和std::async进行异步编程  蛙漫限时开放最深处链接_蛙漫全站漫画会员同款秒开地址  格力空气能E5故障代码是什么情况_格力空气能E5代码解析与应对措施  使用 Pandas 高效处理 .dat 文件:字符清理与数据计算  UC浏览器网页版登录入口官网 电脑版网址入口  KFC早餐时段怎么领特惠代码_KFC早餐订餐优惠代码获取与使用说明  如何在CSS中使用浮动制作导航栏_float实现水平菜单  提升Kafka消费者健壮性:会话超时处理与消息处理语义  支付宝如何管理隐私设置_支付宝隐私保护的配置技巧  漫蛙漫画网页端入口 漫蛙2官方正版漫画站点  AO3网页版最新入口合集 Archive of Our Own在线访问指南  Django表单验证失败时保留用户输入数据的最佳实践  12306选座系统怎么选连座_12306选座多人连坐操作方法  如何使用spryker/configurable-bundles-products-resource-relationship模块解决复杂产品捆绑关系难题  Excel组合图表怎么做 Excel创建柱状图与折线组合图教程【图表】  J*aScript map 方法中处理循环元素为空数组的策略  TypeScript/J*aScript:高效查找数组中首个唯一ID对象  12306几点到几点不能订票? | 官方最新系统维护时间全解析  Safari自带网页翻译功能怎么用 无需插件轻松看懂外文网站【方法】  蛙漫官网漫画入口地址_蛙漫在线畅读无广告弹窗 

搜索