You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何无需显式循环,基于多偏移量从二维数组索引多个子块

问题描述

假设有一个10×10的二维数组A:

# A
array([[ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9],
       [10, 11, 12, 13, 14, 15, 16, 17, 18, 19],
       [20, 21, 22, 23, 24, 25, 26, 27, 28, 29],
       [30, 31, 32, 33, 34, 35, 36, 37, 38, 39],
       [40, 41, 42, 43, 44, 45, 46, 47, 48, 49],
       [50, 51, 52, 53, 54, 55, 56, 57, 58, 59],
       [60, 61, 62, 63, 64, 65, 66, 67, 68, 69],
       [70, 71, 72, 73, 74, 75, 76, 77, 78, 79],
       [80, 81, 82, 83, 84, 85, 86, 87, 88, 89],
       [90, 91, 92, 93, 94, 95, 96, 97, 98, 99]])

同时有一个3×2的偏移量数组offsets,以及block_size=3:

# offsets
array([[0, 0],
       [2, 3],
       [4, 5]])

我需要从A中按offsets指定的起始位置,提取尺寸为block_size×block_size的子块,目前用显式for循环可以实现需求:

block_size = 3
results = []
for x, y in offsets:
    block = A[x:x+block_size, y:y+block_size]
    results.append(block)
results = np.array(results)

# results.shape = (3, 3, 3)
array([[[ 0,  1,  2],
        [10, 11, 12],
        [20, 21, 22]],

       [[23, 24, 25],
        [33, 34, 35],
        [43, 44, 45]],

       [[45, 46, 47],
        [55, 56, 57],
        [65, 66, 67]]])

请问有没有不需要显式for循环的实现方式?比如类似下面这种(但写法无效)的方法:

# 该写法无效
results = A[offsets[:,0]:offsets[:,0]+block_size, offsets[:,1]:offsets[:,1]+block_size]
解决方案

可以用NumPy的向量化操作实现,这里提供几种高效的无循环方案:

方法1:广播生成索引矩阵

通过广播生成所有子块的行、列索引,再直接索引数组A:

import numpy as np

block_size = 3
# 生成块内的偏移量:(block_size, block_size)
row_offs = np.arange(block_size)[:, None]
col_offs = np.arange(block_size)
# 扩展offsets维度,与块内偏移量广播:(3, block_size, block_size)
rows = offsets[:, 0, None, None] + row_offs
cols = offsets[:, 1, None, None] + col_offs
# 索引得到结果
results = A[rows, cols]

得到的results形状为(3, 3, 3),和循环结果完全一致。

方法2:使用np.lib.stride_tricks.as_strided(内存高效)

如果数组A是连续内存的,可以用as_strided直接生成视图,避免数据复制:

from numpy.lib.stride_tricks import as_strided

block_size = 3
# 获取原数组的内存步长
strides = A.strides
# 生成所有可能的block视图,再按offsets提取目标块
all_blocks = as_strided(A, 
                        shape=(A.shape[0]-block_size+1, A.shape[1]-block_size+1, block_size, block_size),
                        strides=(strides[0], strides[1], strides[0], strides[1]))
results = all_blocks[offsets[:,0], offsets[:,1]]

注意:这种方法生成的是原数组的视图,修改results会直接影响原数组A,若需要独立数组可添加.copy()。

方法3:使用np.take_along_axis

结合广播索引和take_along_axis分步提取:

block_size = 3
# 生成每个子块的行索引范围:(3, block_size)
row_indices = offsets[:,0, None] + np.arange(block_size)
# 生成每个子块的列索引范围:(3, block_size)
col_indices = offsets[:,1, None] + np.arange(block_size)
# 先按行提取对应行,再按列提取对应列
temp = np.take_along_axis(A, row_indices[:, :, None], axis=0)
results = np.take_along_axis(temp, col_indices[:, None, :], axis=1)

这种方法逻辑直观,同样能得到符合要求的结果。

内容的提问来源于stack exchange,提问作者Qimin Chen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 08:20:27