在向量化NumPy数组上进行移动窗口操作

简介: 在向量化NumPy数组上进行移动窗口操作

今天很有可能你已经做了一些使用滑动窗口(也称为移动窗口)的事情,而你甚至不知道它。例如:许多编辑算法都是基于移动窗口的。在GIS中做地形分析的大多数地形栅格度量(坡度、坡向、山坡阴影等)都基于滑动窗口。很多情况下,对格式化为二维数组的数据进行分析时,都很有可能涉及到滑动窗口。

滑动窗口操作非常普遍,非常有用。它们也很容易在Python中实现。学习如何实现移动窗口将把你的数据分析和争论技能提升到一个新的水平。

什么是滑动窗?

下面的例子显示了一个3×3(3×3)滑动窗口。用红色标注的数组元素是目标元素。这是滑动窗口将计算的新度量的数组位置。例如,在下面的图像中,我们可以计算灰色窗口中9个元素的平均值(平均值也是8),并将其分配给目标元素,用红色标出。你可以计算最小值(0)、最大值(16)或其他一些指标,而不是平均值。对数组中的每个元素都这样做。

就是这样。这就是滑动窗口的基本原理。当然,事情可能变得更加复杂。有限差分方法可以用于时间和空间数据。逻辑可以实现。可以使用更大的窗口大小或非正方形窗口。你懂的。但在其核心,移动窗口分析可以简单地总结为邻居元素的平均值。

需要注意的是,必须为边缘元素设置特殊的调整,因为它们没有9个相邻元素。因此,许多分析都排除了边缘元素。为简单起见,我们将在本文中排除边缘元素。

640.png

样例数组

640.png

3x3的滑动窗口

创建一个NumPy数组

为了实现一些简单的示例,让我们创建上面所示的数组。首先,导入numpy。

importnumpyasnp

然后使用arange创建一个7×7的数组,值范围从1到48。另外,创建另一个包含无数据值的数组,该数组的形状和数据类型与初始数组相同。在本例中,我使用-1作为无数据值。

a=np.arange(49).reshape((7, 7))
b=np.full(a.shape, -1.0)

我们将使用这些数组来开发下面的滑动窗口示例。

通过循环实现滑动窗口

毫无疑问,你已经听说过Python中的循环很慢,应该尽可能避免。特别是在使用大型NumPy数组时。这是完全正确。尽管如此,我们将首先看一个使用循环的示例,因为这是一种简单的方法来概念化在移动窗口操作中发生的事情。在你通过循环示例掌握了概念之后,我们将继续使用更有效的向量化方法。

要实现移动窗口,只需循环遍历所有内部数组元素,识别所有相邻元素的值,并在特定的计算中使用这些值。

通过行和列偏移量可以很容易地识别相邻值。3×3窗口的偏移量如下所示。

640.png

行偏移

640.png

列偏移

循环中NumPy移动窗口的Python代码

我们可以用三行代码实现一个移动窗口。这个例子在滑动窗口内计算平均值。首先,循环遍历数组的内部行。其次,循环遍历数组的内部列。第三,在滑动窗口内计算平均值,并将值赋给输出数组中相应的数组元素。

foriinrange(1, a.shape[0]-1):
forjinrange(1, a.shape[1]-1):
b[i, j] = (a[i-1, j-1] +a[i-1, j] +a[i-1, j+1] +a[i, j-1] +a[i, j] +a[i, j+1] +a[i+1, j-1] +a[i+1, j] +a[i+1, j+1]) /9.0

循环后结果

你将注意到结果与输入数组具有相同的值,但是外部元素没有被分配数据值,因为它们不包含9个相邻元素。

[[-1.-1.-1.-1.-1.-1.-1.]
  [-1.8.9.10.11.12.-1.]
  [-1.15.16.17.18.19.-1.]
  [-1.22.23.24.25.26.-1.]
  [-1.29.30.31.32.33.-1.]
  [-1.36.37.38.39.40.-1.]
  [-1.-1.-1.-1.-1.-1.-1.]]

向量化滑动窗口

Python中的数组循环通常计算效率低下。通过对通常在循环中执行的操作进行向量化,可以提高效率。移动窗口矢量化可以通过同时抵消数组内部的所有元素来实现。

如下图所示。每个图像都有相应的索引。你将注意到最后一张图像索引了所有内部元素,并且对应的图像索引了每个相邻元素的偏移量。

640.png

640.png

640.png

从左到右的偏移索引:[1:-1,:-2],[1:-1,2:],[2 :, 2:]

640.png

640.png

640.png

从左到右的偏移索引:[2 :,:-2],[2 :, 1:-1],[:-2,1:-1]

640.png

640.png

640.png

从左到右的偏移索引:[:-2,2:],[:-2,:-2],[1:-1、1:-1]

Numpy数组上的向量化移动窗口的Python代码

有了上述偏移量,我们现在可以轻松地在一行代码中实现滑动窗口。只需将输出数组的所有内部元素设置为根据相邻元素计算所需输出的函数。

b[1:-1, 1:-1] = (a[1:-1, 1:-1] +a[:-2, 1:-1] +a[2:, 1:-1] +a[1:-1, :-2] +a[1:-1, 2:] +a[2:, 2:] +a[:-2, :-2] +a[2:, :-2] +a[:-2, 2:]) /9.0

矢量化滑动窗口结果

如你所见,这将得到与循环相同的结果。

[[-1.-1.-1.-1.-1.-1.-1.]
  [-1.8.9.10.11.12.-1.]
  [-1.15.16.17.18.19.-1.]
  [-1.22.23.24.25.26.-1.]
  [-1.29.30.31.32.33.-1.]
  [-1.36.37.38.39.40.-1.]
  [-1.-1.-1.-1.-1.-1.-1.]]

速度比较

上述两种方法产生相同的结果,但哪一种更有效?我计算了从5行到100列的数组的每种方法的速度。每种方法对每个测试100次。下面是每种方法的平均时间。

640.png

很明显,向量化的方法更加有效。随着数组大小的增加,循环的效率呈指数级下降。另外,需要注意的是,一个包含10,000个元素(100行和100列)的数组非常小。

总结

移动窗口计算在许多数据分析工作流程中非常常见。这些计算是非常有用的,非常容易实现。然而,使用循环来实现滑动窗口操作是非常低效的。向量化的移动窗口实现不仅更高效,而且使用更少的代码行。一旦掌握了实现滑动窗口的向量化方法,就可以轻松有效地提高工作流程的速度。

目录
相关文章
|
1月前
|
索引 Python
NumPy 教程 之 Numpy 数组操作 28
NumPy 提供多种数组操作功能,包括修改形状、翻转、连接和分割等。本教程重点介绍元素的添加与删除,如使用 `resize`、`append`、`insert` 和 `delete` 函数。其中 `numpy.insert` 可在指定索引前插入值,支持标量或数组插入。示例展示了不同情况下 `insert` 的使用方法,包括不指定轴时的数组扁平化插入,以及沿特定轴进行广播插入。
31 2
|
1月前
|
数据处理 索引 Python
NumPy 数组操作:和普通操作相较,到底蕴含着怎样令人费解的独特魅力?
【8月更文挑战第19天】NumPy是Python科学计算核心库,提供高效数组操作。不同于Python列表直接列举创建,NumPy用`np.array()`创建数组。两者都支持索引和切片,但NumPy性能更优。数学运算方面,NumPy支持简洁的向量化操作,如`my_array * 2`,无需循环。NumPy还简化了数组形状变换,如使用`reshape()`方法。此外,NumPy数组要求元素类型一致,提高了内存使用效率和计算速度。这些特点使NumPy在科学计算和数据分析中不可或缺。
28 0
|
30天前
|
存储 缓存 C语言
|
29天前
|
索引 Python
|
29天前
|
索引 Python
NumPy 快速入门:数组操作基础
【8月更文第30天】NumPy 是 Python 中一个非常重要的科学计算库,它提供了高性能的多维数组对象以及用于操作这些数组的工具。NumPy 数组(也称为 `ndarray`)是 NumPy 库的核心,它比 Python 内置的列表类型更高效,特别是在处理大型数据集时。本文将介绍 NumPy 数组的基本概念、创建方法以及一些常用的数组操作。
18 2
|
1月前
|
Python
NumPy 教程 之 Numpy 数组操作 29
NumPy 提供多种数组操作函数,如修改形状、翻转和连接等。本教程重点介绍元素的添加与删除,包括 `resize`、`append`、`insert` 和 `delete` 函数。`numpy.delete` 用于从数组中删除指定元素,参数包括 `arr`(输入数组)、`obj`(待删元素)和 `axis`(删除轴)。示例展示了如何使用 `numpy.delete` 删除一维和二维数组中的元素。
32 4
|
1月前
|
数据挖掘 索引 Python
NumPy 教程 之 Numpy 数组操作 30
展示了如何使用`numpy.unique`去除数组中的重复值,并获取额外信息如索引和计数。示例中,数组`a`的重复值被去除,打印出唯一值及其在原数组中的首次出现索引、对应原值的索引以及各唯一元素的出现次数。这有助于数据分析时简化数据集。
28 2
|
1月前
|
Python
NumPy 教程 之 Numpy 数组操作 24
本教程介绍Numpy中分割数组的方法,特别是`vsplit`函数,它能将数组沿垂直轴分为多个子数组。示例代码展示了如何使用`vsplit`将一个4x4的数组分为两个2x4的子数组,适用于需要按行分割数据的场景。
13 1
|
1月前
|
Python
NumPy 教程 之 Numpy 数组操作 22
本教程介绍NumPy中分割数组的方法。`numpy.split`可沿指定轴将数组分为子数组,参数包括待分割数组、切分数或位置及轴向。示例展示了如何使用`numpy.split`和`numpy.hsplit`沿不同轴向分割一个4x4数组。`numpy.split`在默认轴0方向分割,而通过设定`axis=1`实现水平分割;`numpy.hsplit`则专门用于水平分割。
27 2
|
1月前
|
Python
NumPy 教程 之 Numpy 数组操作 19
本教程介绍Numpy数组操作,涵盖数组形状修改、翻转、维度调整、连接与分割以及元素增删等关键技能。重点讲解`numpy.vstack`函数,该函数可将多个数组沿垂直方向堆叠。示例展示了如何使用`vstack`将两个2D数组堆叠成一个更大的数组,适用于数据整合场景。
25 2