For the given 2d array of data, how to retrieve the position (index) of 7 and 11 in the bold. Because only they are the elements surrounded by same value in the neighbours>
Using scipy, you could characterize such points as those which are both the maximum and the minimum of its neighborhood:
import numpy as np
import scipy.ndimage.filters as filters
def using_filters(data):
return np.where(np.logical_and.reduce(
[data == f(data, footprint=np.ones((3,3)), mode='constant', cval=np.inf)
for f in (filters.maximum_filter, filters.minimum_filter)]))
using_filters(data)
# (array([2, 3]), array([5, 9]))
Using only numpy, you could compare data
with 8 shifted slices of itself to find the points which are equal:
def using_eight_shifts(data):
h, w = data.shape
data2 = np.empty((h+2, w+2))
data2[(0,-1),:] = np.nan
data2[:,(0,-1)] = np.nan
data2[1:1+h,1:1+w] = data
result = np.where(np.logical_and.reduce([
(data2[i:i+h,j:j+w] == data)
for i in range(3)
for j in range(3)
if not (i==1 and j==1)]))
return result
As you can see above, this strategy makes an expanded array which has a border of NaNs around the data. This allows the shifted slices to be expressed as data2[i:i+h,j:j+w]
.
If you know that you are going to be comparing against neighbors, it might behoove you to define data
with a border of NaNs from the very beginning so you don't have to make a second array as done above.
Using eight shifts (and comparisons) is much faster than looping over each cell in data
and comparing it against its neighbors:
def using_quadratic_loop(data):
return np.array([[i,j]
for i in range(1,np.shape(data)[0]-1)
for j in range(1,np.shape(data)[1]-1)
if np.all(data[i-1:i+2,j-1:j+2]==data[i,j])]).T
Here is a benchmark:
using_filters : 0.130
using_eight_shifts : 0.340
using_quadratic_loop : 18.794
Here is the code used to produce the benchmark:
import timeit
import operator
import numpy as np
import scipy.ndimage.filters as filters
import matplotlib.pyplot as plt
data = np.array([
[0,1,2,3,4,7,6,7,8,9,10],
[3,3,3,4,7,7,7,8,11,12,11],
[3,3,3,5,7,7,7,9,11,11,11],
[3,4,3,6,7,7,7,10,11,11,11],
[4,5,6,7,7,9,10,11,11,11,11]
])
data = np.tile(data, (50,50))
def using_filters(data):
return np.where(np.logical_and.reduce(
[data == f(data, footprint=np.ones((3,3)), mode='constant', cval=np.inf)
for f in (filters.maximum_filter, filters.minimum_filter)]))
def using_eight_shifts(data):
h, w = data.shape
data2 = np.empty((h+2, w+2))
data2[(0,-1),:] = np.nan
data2[:,(0,-1)] = np.nan
data2[1:1+h,1:1+w] = data
result = np.where(np.logical_and.reduce([
(data2[i:i+h,j:j+w] == data)
for i in range(3)
for j in range(3)
if not (i==1 and j==1)]))
return result
def using_quadratic_loop(data):
return np.array([[i,j]
for i in range(1,np.shape(data)[0]-1)
for j in range(1,np.shape(data)[1]-1)
if np.all(data[i-1:i+2,j-1:j+2]==data[i,j])]).T
np.testing.assert_equal(using_quadratic_loop(data), using_filters(data))
np.testing.assert_equal(using_eight_shifts(data), using_filters(data))
timing = dict()
for f in ('using_filters', 'using_eight_shifts', 'using_quadratic_loop'):
timing[f] = timeit.timeit('{f}(data)'.format(f=f),
'from __main__ import data, {f}'.format(f=f),
number=10)
for f, t in sorted(timing.items(), key=operator.itemgetter(1)):
print('{f:25}: {t:.3f}'.format(f=f, t=t))