Beh*_*ali 3 python performance numpy
我有两个数据集如下:
ds1:一个 DEM(数字高程模型)文件作为 2d numpy 数组,
ds2:显示有一些多余水分的区域(像素)。
我有一个 while 循环,负责根据其 8 个相邻像素的高程及其自身的高度来扩展(和更改)每个像素中的多余体积,直到每个像素中的多余体积小于某个值 d = 0.05。因此,在每次迭代中,我需要在 ds2 中找到多余体积大于 0.05 的像素索引,如果没有像素剩余,则退出 while 循环:
exit_code == "No"
while exit_code == "No":
index_of_pixels_with_excess_volume = numpy.argwhere(ds2> 0.05) # find location of pixels where excess volume is greater than 0.05
if not index_of_pixels_with_excess_volume.size:
exit_code = "Yes"
else:
for pixel in index_of_pixels_with_excess_volume:
# spread those excess volumes to the neighbours and
# change the values of ds2
Run Code Online (Sandbox Code Playgroud)
问题是 numpy.argwhere(ds2> 0.05) 非常慢。我正在寻找更快的替代解决方案。
制作一个示例二维数组:
In [584]: arr = np.random.rand(1000,1000)
Run Code Online (Sandbox Code Playgroud)
找出其中的一小部分:
In [587]: np.where(arr>.999)
Out[587]:
(array([ 1, 1, 1, ..., 997, 999, 999], dtype=int32),
array([273, 471, 584, ..., 745, 310, 679], dtype=int32))
In [588]: _[0].shape
Out[588]: (1034,)
Run Code Online (Sandbox Code Playgroud)
时间各件argwhere:
In [589]: timeit arr>.999
2.65 ms ± 116 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
In [590]: timeit np.count_nonzero(arr>.999)
2.79 ms ± 26 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
In [591]: timeit np.nonzero(arr>.999)
6 ms ± 10 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
In [592]: timeit np.argwhere(arr>.999)
6.06 ms ± 58.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Run Code Online (Sandbox Code Playgroud)
所以大约 1/3 的时间花在做>测试上,其余的时间花在寻找True元素上。将where元组转换为 2 列数组很快。
现在,如果目标只是找到第一个>值,argmax则很快。
In [593]: np.argmax(arr>.999)
Out[593]: 1273 # can unravel this to (1,273)
In [594]: timeit np.argmax(arr>.999)
2.76 ms ± 143 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Run Code Online (Sandbox Code Playgroud)
argmax 短路,因此当它找到第一个值时,实际运行时间会有所不同。
flatnonzero快于where:
In [595]: np.flatnonzero(arr>.999)
Out[595]: array([ 1273, 1471, 1584, ..., 997745, 999310, 999679], dtype=int32)
In [596]: timeit np.flatnonzero(arr>.999)
3.05 ms ± 26.6 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
In [599]: np.unravel_index(np.flatnonzero(arr>.999),arr.shape)
Out[599]:
(array([ 1, 1, 1, ..., 997, 999, 999], dtype=int32),
array([273, 471, 584, ..., 745, 310, 679], dtype=int32))
In [600]: timeit np.unravel_index(np.flatnonzero(arr>.999),arr.shape)
3.05 ms ± 3.58 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
In [601]: timeit np.transpose(np.unravel_index(np.flatnonzero(arr>.999),arr.shap
...: e))
3.1 ms ± 5.86 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Run Code Online (Sandbox Code Playgroud)
这与np.argwhere(arr>.999).
有趣的是,这种flatnonzero方法将时间缩短了一半!没想到有这么大的进步。
比较迭代速度:
从 2d 数组迭代argwhere:
In [607]: pixels = np.argwhere(arr>.999)
In [608]: timeit [pixel for pixel in pixels]
347 µs ± 5.29 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
Run Code Online (Sandbox Code Playgroud)
where使用zip(*)转置迭代元组:
In [609]: idx = np.where(arr>.999)
In [610]: timeit [pixel for pixel in zip(*idx)]
256 µs ± 147 ns per loop (mean ± std. dev. of 7 runs, 1000 loops each)
Run Code Online (Sandbox Code Playgroud)
迭代数组通常比迭代列表慢一点,或者在这种情况下是压缩数组。
In [611]: [pixel for pixel in pixels][:5]
Out[611]:
[array([ 1, 273], dtype=int32),
array([ 1, 471], dtype=int32),
array([ 1, 584], dtype=int32),
array([ 1, 826], dtype=int32),
array([ 2, 169], dtype=int32)]
In [612]: [pixel for pixel in zip(*idx)][:5]
Out[612]: [(1, 273), (1, 471), (1, 584), (1, 826), (2, 169)]
Run Code Online (Sandbox Code Playgroud)
一个是数组列表,另一个是元组列表。但是将这些元组(单独)转换为数组很慢:
In [614]: timeit [np.array(pixel) for pixel in zip(*idx)]
2.26 ms ± 4.94 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Run Code Online (Sandbox Code Playgroud)
迭代平面非零数组更快
In [617]: fdx = np.flatnonzero(arr>.999)
In [618]: fdx[:5]
Out[618]: array([1273, 1471, 1584, 1826, 2169], dtype=int32)
In [619]: timeit [i for i in fdx]
112 µs ± 23.5 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
Run Code Online (Sandbox Code Playgroud)
但是unravel单独应用这些值需要时间。
def foo(idx): # a simplified unravel
return idx//1000, idx%1000
In [628]: timeit [foo(i) for i in fdx]
1.12 ms ± 1.02 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
Run Code Online (Sandbox Code Playgroud)
将此 1 ms 添加到 3 ms 以生成fdx,这flatnonzero可能仍会提前。但在最好的情况下,我们谈论的是 2 倍的速度提升。
| 归档时间: |
|
| 查看次数: |
3749 次 |
| 最近记录: |