Reputation: 1406
there are functions in matlab for take the average value of the n nearest entries to each entry.
x=np.array([[0.1,0.8,.2],
[0.5,0.2,np.nan],
[0.7,0.2,0.9],
[0.4,0.7,1],
[np.nan,0.14,1]])
creates average filter in matlab:
x=[[0.1,0.8,.2],
[0.5,0.2,nan],
[0.7,0.2,0.9],
[0.4,0.7,1],
[nan,0.14,1]]
fspecial('average',[3,3])
filter2(ave1,x)
[[ 0.17777778 nan nan]
[ 0.27777778 nan nan]
[ 0.3 nan nan]
[ nan nan 0.43777778]
[ nan nan 0.31555556]]
I want to convert this to python. I have found this: uniform filter and: skimage.filters.rank.mean
but the result is not same as matlab. uniform filter:
x=np.array([[0.1,0.8,.2],
[0.5,0.2,np.nan],
[0.7,0.2,0.9],
[0.4,0.7,1],
[np.nan,0.14,1]])
print(uniform_filter(x, size=3, mode='constant'))
[[ 0.17777778 nan nan]
[ 0.27777778 nan nan]
[ 0.3 nan nan]
[ nan nan nan]
[ nan nan nan]]
skimage filter:
from skimage.filters.rank import mean
from skimage.morphology import square
from skimage import img_as_float
x=np.array([[0.1,0.8,.2],
[0.5,0.2,np.nan],
[0.7,0.2,0.9],
[0.4,0.7,1],
[np.nan,0.14,1]])
print(mean(x, square(3)))
[[102 76 76]
[106 102 97]
[114 130 127]
[ 90 142 167]
[ 79 137 181]]
print(img_as_float(mean(x, square(3))))
[[ 0.4 0.29803922 0.29803922]
[ 0.41568627 0.4 0.38039216]
[ 0.44705882 0.50980392 0.49803922]
[ 0.35294118 0.55686275 0.65490196]
[ 0.30980392 0.5372549 0.70980392]]
finally I have to it myself but is not mature in performance:
x=np.array([[0.1,0.8,.2],
[0.5,0.2,np.nan],
[0.7,0.2,0.9],
[0.4,0.7,1],
[np.nan,0.14,1]])
Winsize=3
adder=int(Winsize/2)
result=np.zeros_like(x)
nan_window_index=np.array([])
for i in range(x.shape[0]):
for j in range(x.shape[1]):
top_left_r= int(i-adder)
top_left_c= int(j-adder)
bottom_right_r=int(i+adder)
bottom_right_c=int(j+adder)
sum_list=np.array([])
for r_counter in range(top_left_r, bottom_right_r+1):
if r_counter<0 or r_counter > x.shape[0]-1:
continue
for c_counter in range(top_left_c, bottom_right_c+1):
if c_counter<0 or c_counter > x.shape[1]-1:
continue
if not np.isnan(x[r_counter, c_counter]):
sum_list=np.append(sum_list, x[r_counter, c_counter])
else:
nan_window_index=np.append(nan_window_index, [[r_counter, c_counter]])
result[i,j]= np.sum(sum_list)/(Winsize*Winsize)
nan_window_index=np.unique(nan_window_index.reshape(int(len(nan_window_index)/2),2), axis=0)
for i,j in nan_window_index:
top_left_r= int(i-adder)
top_left_c= int(j-adder)
bottom_right_r=int(i+adder)
bottom_right_c=int(j+adder)
for r_counter in range(top_left_r, bottom_right_r+1):
if r_counter<0 or r_counter > x.shape[0]-1:
continue
for c_counter in range(top_left_c, bottom_right_c+1):
if c_counter<0 or c_counter > x.shape[1]-1:
continue
result[r_counter, c_counter]=np.nan
print(result)
and the result is same as matlab:
[[ 0.17777778 nan nan]
[ 0.27777778 nan nan]
[ 0.3 nan nan]
[ nan nan 0.43777778]
[ nan nan 0.31555556]]
any suggestion for better performance?
Upvotes: 2
Views: 882
Reputation: 6891
You can use scipy.signal.convolve
(or perhaps scipy.signal.convolve2d
as it might be quicker) as:
import numpy as np
# from scipy.signal import convolve
from scipy.signal import convolve2d
x=np.array([[0.1,0.8,.2],
[0.5,0.2,np.nan],
[0.7,0.2,0.9],
[0.4,0.7,1],
[np.nan,0.14,1]])
core = np.full((3,3),1/3**2)
# convolve(x, core, mode='same')
convolve2d(x, core, mode='same')
Convolution with uniform values is the same as a uniform filter. Note that this will automatically "assume" zeros outside your matrix, but that is in line with what you are asking for, so it will work in your current setup.
Upvotes: 4