
在数据分析和机器学习任务中,将一维数组分割成固定大小的、互不重叠的子数组(即非重叠滑动窗口)是一项常见操作,尤其是在时间序列分析或异常检测等领域。numpy库提供了多种灵活且高效的方式来实现这一目标。本文将详细介绍三种主要方法:简单的reshape、结合sliding_window_view进行切片,以及更底层的as_strided。
当原始数组的长度能够被窗口大小完美整除时,reshape方法是最直接、最简洁的解决方案。它将一维数组直接重塑为二维数组,其中每个行代表一个非重叠的窗口。
示例代码:
import numpy as np
# 原始一维数组
x = np.arange(1, 7)
print(f"原始数组: {x}")
# 窗口大小为3,数组长度可被3整除
window_size = 3
if len(x) % window_size == 0:
result_reshape = x.reshape(-1, window_size)
print(f"使用 reshape 创建的非重叠窗口:\n{result_reshape}")
else:
print("数组长度无法被窗口大小整除,reshape不适用。")输出:
原始数组: [1 2 3 4 5 6] 使用 reshape 创建的非重叠窗口: [[1 2 3] [4 5 6]]
注意事项:reshape方法要求数组的元素总数必须与新形状的元素总数相匹配。如果无法完美整除,此方法将报错。
np.lib.stride_tricks.sliding_window_view 是NumPy提供的一个强大工具,用于创建滑动窗口视图。默认情况下,它会生成所有可能的、高度重叠的滑动窗口。为了获得非重叠窗口,我们可以在其输出结果上进行步长切片。
示例代码:
import numpy as np
x = np.arange(1, 7)
window_size = 3
# 1. 生成所有重叠的滑动窗口
all_windows = np.lib.stride_tricks.sliding_window_view(x, window_size)
print(f"使用 sliding_window_view 生成的重叠窗口:\n{all_windows}")
# 2. 从重叠窗口中切片出非重叠部分
# 每隔 window_size 个窗口取一个,即可实现非重叠
non_overlapping_windows = all_windows[::window_size]
print(f"通过切片获得的非重叠窗口:\n{non_overlapping_windows}")输出:
使用 sliding_window_view 生成的重叠窗口: [[1 2 3] [2 3 4] [3 4 5] [4 5 6]] 通过切片获得的非重叠窗口: [[1 2 3] [4 5 6]]
优点:
np.lib.stride_tricks.as_strided 是NumPy中实现视图操作的底层函数。它允许用户直接指定新数组的形状(shape)和步幅(strides),从而创建任意复杂的视图。理解strides是使用as_strided的关键。
理解 strides:strides是一个元组,表示在每个维度上移动一个元素需要跳过的字节数。
为了创建非重叠窗口,我们需要计算正确的shape和strides。
计算 shape 和 strides: 假设我们有一个一维数组 x,元素类型为 dtype,窗口大小为 window_size。
新数组的行数 (窗口数量): num_windows = len(x) // window_size
新数组的列数 (窗口大小): window_size
因此,新数组的 shape 为: (num_windows, window_size)
原始数组的元素字节大小: itemsize = x.itemsize (例如,np.int32 为 4 字节)
新数组第一个维度(行)的步幅: 要从一个窗口的起始位置跳到下一个非重叠窗口的起始位置,需要跳过 window_size 个原始数组元素。所以步幅是 window_size * itemsize。
新数组第二个维度(列)的步幅: 在一个窗口内部,要从一个元素跳到下一个元素,只需跳过一个原始数组元素。所以步幅是 itemsize。
因此,新数组的 strides 为: (window_size * itemsize, itemsize)
示例代码:
import numpy as np
x = np.arange(1, 7)
window_size = 3
# 获取原始数组的元素字节大小
item_size = x.itemsize
print(f"原始数组元素字节大小: {item_size}") # 例如,int32是4字节
# 计算新数组的 shape
num_windows = len(x) // window_size
new_shape = (num_windows, window_size)
# 计算新数组的 strides
# 行步幅: 从一个窗口的开头到下一个窗口的开头,跳过 window_size 个元素
row_stride = window_size * item_size
# 列步幅: 在一个窗口内,从一个元素到下一个元素,跳过 1 个元素
col_stride = item_size
new_strides = (row_stride, col_stride)
print(f"计算出的新 shape: {new_shape}")
print(f"计算出的新 strides: {new_strides}")
# 使用 as_strided 创建非重叠窗口视图
non_overlapping_as_strided = np.lib.stride_tricks.as_strided(x, shape=new_shape, strides=new_strides)
print(f"使用 as_strided 创建的非重叠窗口:\n{non_overlapping_as_strided}")
# 比较 as_strided 和 sliding_window_view 默认行为的 strides
# full_overlap_strides = np.lib.stride_tricks.as_strided(x, shape=(len(x) - window_size + 1, window_size), strides=(item_size, item_size))
# print(f"等价于 sliding_window_view 默认行为的 as_strided (重叠):\n{full_overlap_strides}")输出:
原始数组元素字节大小: 4 计算出的新 shape: (2, 3) 计算出的新 strides: (12, 4) 使用 as_strided 创建的非重叠窗口: [[1 2 3] [4 5 6]]
注意事项:
下表总结了三种方法的特点:
| 方法 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| reshape | 最简洁、高效 | 严格要求数组长度能被窗口大小整除 | 数组长度可完美整除,追求代码简洁 |
| sliding_window_view + 切片 | 灵活,封装了步幅计算,易于理解 | 需额外切片步骤,可能先生成大量中间窗口 | 数组长度不确定,或需要更灵活的窗口处理 |
| as_strided | 极致的灵活性和性能,底层控制 | 复杂,易出错,需手动计算步幅,存在内存安全风险 | 对性能有极高要求,或需实现非常规的视图操作 |
在大多数实际应用中,推荐优先考虑使用 reshape(如果适用)或 sliding_window_view 结合切片的方法。as_strided 虽功能强大,但其复杂性和潜在风险使其更适合对NumPy内部机制有深入理解的高级用户,或在性能成为瓶颈且其他方法无法满足需求时使用。
以上就是NumPy数组非重叠滑动窗口的高效构建方法的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号