我有一个2D数组2000x4000,对于该数组中的每个单元,我必须比较该单元的值与由10个相邻单元(在+/- X和+/-中,是)。
例如,这就是我现在正在做的事情:
import numpy as np
from astropy.stats import sigma_clipped_stats
BPmap=[]
N=10
a=np.random.random((2000,4000))
for row in range(N,a.shape[0]-N):
BPmap_row=[]
for column in range(N,a.shape[1]-N):
Bpmap_data=np.array(a[row-N:row+N,column-N:column+N].ravel())
mean, median, std = sigma_clipped_stats(Bpmap_data, sigma=3,iters=5)
BPmap_Nsigma=float(a[row][column]-median)/std
BPmap_row.append(BPmap_Nsigma)
BPmap.append(BPmap_row)
这有一个明显的问题,我正在制作2000x4000 = 8000000循环,这花费了很长时间。我需要找到一种非常有效的方法来执行这些操作,但我不知道如何。
答案 0 :(得分:8)
我们通常避免对numpy使用double-for循环;它很慢,而且有了智能索引(array [:, i-N] ...),我们可以在一个循环中做很多事情。
但是对于您的卷积问题,这可能是最简单的~~(并且只有?)~~方式来完成您想要的事情。 (编辑:不是。请参见下面@Masoud的回答)。
Bpmap_data=np.array(a[row-N:row+N,column-N:column+N].ravel())
在每个循环中创建一个新数组。
在不创建新数组(即直接使用numpy视图)的情况下计算中值和标准差会更快。
要获得快400倍的算法,请查看将scipy过滤器用于2D阵列的@Masoud answer。
import numpy as np
from astropy.stats import sigma_clipped_stats
N=10
a=np.random.random((80,40))
def f():
"""Your original code"""
BPmap=[]
for row in range(N,a.shape[0]-N):
BPmap_row=[]
for column in range(N,a.shape[1]-N):
Bpmap_data=np.array(a[row-N:row+N,column-N:column+N].ravel())
mean, median, std = sigma_clipped_stats(Bpmap_data, sigma=3,iters=5)
BPmap_Nsigma=float(a[row][column]-median)/std
BPmap_row.append(BPmap_Nsigma)
BPmap.append(BPmap_row)
return BPmap
def f2():
"""this little guy is improving a lot your work"""
BPmap=[]
for row in range(N,a.shape[0]-N):
BPmap_row=[]
for column in range(N,a.shape[1]-N):
# the next 3 lines do not need any more memory
view = a[row-N:row+N,column-N:column+N]
std_without_outliers = view[view - view.mean() < 3*view.std()].std()
median = np.median(view)
# back to your workflow
BPmap_Nsigma=float(a[row][column]-median)/std_without_outliers
BPmap_row.append(BPmap_Nsigma)
BPmap.append(BPmap_row)
return BPmap
%time _ = f()
%time _ = f2()
f() == f2()
>>>CPU times: user 39.7 s, sys: 14.2 ms, total: 39.7 s
Wall time: 39.7 s
CPU times: user 969 ms, sys: 2.99 ms, total: 972 ms
Wall time: 976 ms
True
修改
实际上,sigma_clipped_stats(a[row-N:row+N,column-N:column+N])
确实会使循环变慢。我怀疑sigma_clipped_stats
正在创建其参数的副本。
在消除3 sigma剪切中的异常值后立即执行std
我在这里展示了一种使用纯numpy做到这一点的方法;这确实比您以前使用的功能要快。
最后,f()= f2()那么为什么还要使用此astropy
函数呢?
答案 1 :(得分:5)
代码中存在一些降低性能的问题:
您可以使用Scipy.ndimage
和opencv
图书馆员代替for循环来进行卷积。尽管这些库用于图像处理,但它们对于处理任何2D数组非常有效。这是一段代码,其执行的任务与使用Scipy.ndimage
工具所需的任务相同,但是速度提高了1000倍(对于200X400阵列,则为23ms vs 27s)。我使用here提供的算法来计算标准差:
import numpy as np
from scipy.ndimage.filters import uniform_filter, median_filter
a=np.random.random((200,400))
c1 = uniform_filter(a, size = (10,10))
c2 = uniform_filter(a*a, size = (10,10))
std = ((c2 - c1*c1)**.5)
med = median_filter(a, size=(10, 10))
BPmap = (a - med)/std