2016-07-06 100 views
5

我有一個ndarray,我想用相鄰元素的平均值替換數組中的每個值。下面的代碼可以完成這項工作,但是當我有700個具有形狀(7000,7000)的陣列時,它會超級慢,所以我想知道是否有更好的方法來完成它。謝謝!Numpy:使用相鄰元素的平均值替換陣列中的每個值

a = np.array(([1,2,3,4,5,6,7,8,9],[4,5,6,7,8,9,10,11,12],[3,4,5,6,7,8,9,10,11])) 
row,col = a.shape 
new_arr = np.ndarray(a.shape) 
for x in xrange(row): 
    for y in xrange(col): 
     min_x = max(0, x-1) 
     min_y = max(0, y-1) 
     new_arr[x][y] = a[min_x:(x+2),min_y:(y+2)].mean() 
print new_arr 
+0

它對我來說運行得不是很慢,也沒有看起來應該慢慢運行。 –

+0

@EliSadoff它將如果我有700陣列形狀(7000,7000)... – Chiefscreation

+0

我不知道如何在python中做到這一點,但你有沒有考慮過多線程或並行處理?我知道在C中可以這樣做來加速大數據處理。 – Michael

回答

9

嗯,這是一個smoothing operation in image processing,可與2D卷積來實現。你對邊界元素的工作方式有點不同。因此,如果邊界元素都不放過精密,你可以使用scipy's convolve2d像這樣 -

from scipy.signal import convolve2d as conv2 

out = (conv2(a,np.ones((3,3)),'same')/9.0 

這具體操作是內置在OpenCV的模塊cv2.blur,是它非常有效。該名稱基本上描述了其模糊輸入數組表示圖像的操作。我相信效率來自於這樣一個事實,即它在內部完全用C來實現,因爲它使用一個很薄的Python封裝來處理NumPy數組。

因此,輸出可以或者用它來計算,像這樣 -

import cv2 # Import OpenCV module 

out = cv2.blur(a.astype(float),(3,3)) 

這裏有一個關於時機的快速攤牌一個體面的大圖像/陣列上 -

In [93]: a = np.random.randint(0,255,(5000,5000)) # Input array 

In [94]: %timeit conv2(a,np.ones((3,3)),'same')/9.0 
1 loops, best of 3: 2.74 s per loop 

In [95]: %timeit cv2.blur(a.astype(float),(3,3)) 
1 loops, best of 3: 627 ms per loop 
+0

很好地完成了。我正要寫這樣的東西。 +1。 – rayryeng

+0

@rayryeng很高興見到你與NumPy標籤! ;) – Divakar

+0

@Divakar這是在我的飼料:)我最近沒有回答問題,雖然...很多東西在我的最後。 – rayryeng

4

繼與@Divakar討論,發現在scipy中存在的不同卷積方法的比較:

import numpy as np 
from scipy import signal, ndimage 

def conv2(A, size): 
    return signal.convolve2d(A, np.ones((size, size)), mode='same')/float(size**2) 

def fftconv(A, size): 
    return signal.fftconvolve(A, np.ones((size, size)), mode='same')/float(size**2) 

def uniform(A, size): 
    return ndimage.uniform_filter(A, size, mode='constant') 

所有3種方法的返回值完全相同。但是請注意,uniform_filter的參數mode='constant'表示濾波器的邊界條件,constant == 0與傅里葉域(在其他兩種方法中)的執行方式相同。對於不同的用例,您可以更改邊界條件。

現在一些測試矩陣:

A = np.random.randn(1000, 1000) 

和一些計時:

%timeit conv2(A, 3)  # 33.8 ms per loop 
%timeit fftconv(A, 3) # 84.1 ms per loop 
%timeit uniform(A, 3) # 17.1 ms per loop 

%timeit conv2(A, 5)  # 68.7 ms per loop 
%timeit fftconv(A, 5) # 92.8 ms per loop 
%timeit uniform(A, 5) # 17.1 ms per loop 

%timeit conv2(A, 10)  # 210 ms per loop 
%timeit fftconv(A, 10) # 86 ms per loop 
%timeit uniform(A, 10) # 16.4 ms per loop 

%timeit conv2(A, 30)  # 1.75 s per loop 
%timeit fftconv(A, 30) # 102 ms per loop 
%timeit uniform(A, 30) # 16.5 ms per loop 

因此,在短期,uniform_filter似乎更快,這是因爲兩個convolution is separable 1D convolutons(類似於gaussian_filter這是也可分離)。

使用signal模塊(@ Divakar中的模塊)解決方案時,具有不同內核的其他不可分離濾波器更有可能更快。

兩個fftconvolveuniform_filter的速度仍然是不同的內核尺寸不變,而convolve2d得稍微慢一些。

+0

好的發現!現在消化所有這些東西。 – Divakar

+0

@Divakar剛剛發現一些非常有趣的事情,針對不同的* small *內核大小,最後2個方法保持* constant *執行時間。 –

+0

我想我通過兩個通道來理解uniform_filter的實現過程。但是'fftconvolve'的內部實現看起來很亂。再次感謝您提出這些有用的發現! – Divakar

0

我最近有一個類似的問題,必須找到不同的解決方案,因爲我不能使用SciPy的。

import numpy as np 

a = np.random.randint(100, size=(7000,7000)) #Array of 7000 x 7000 
row,col = a.shape 

column_totals = a.sum(axis=0) #Dump the sum of all columns into a single array 

new_array = np.zeros([row,col]) #Create an receiving array 

for i in range(row): 
    #Resulting row = the value of all rows minus the orignal row, divided by the row number minus one. 
    new_array[i] = (column_totals - a[i])/(row - 1) 

print(new_array)