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))
I have tested it and the following code works:
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]):
print np.array([i,j], dtype=np.int64)
I used a list comprehension but there may be a better way
A = [(i,j) for i in range(1,data.shape[0]-1) for j in range(1,data.shape[1]-1) if all((data[i-1:i+2,j-1:j+2]==data[i,j]).flatten())]
EDIT:
If you want the form array([i,j],dtype=int64) then you just need to modify the first part:
A= [np.array([i,j], dtype=np.int64) for i in range(1,data.shape[0]-1) for j in range(1,data.shape[1]-1) if all((data[i-1:i+2,j-1:j+2]==data[i,j]).flatten())]
displacements = [[-1, -1], [-1, 0], [-1, 1], [0, -1], [0, 1], [1, -1], [1, 0], [1, 1]]
for x in range(1, data.shape[0] - 1):
for y in range(1, data.shape[1] - 1):
if all((data[x][y] == data[x + a][y + b]) for a, b in displacements):
print np.array([x, y], dtype=np.int64)
Not as succint as the other answers, but it's clear and prints the correct output. I think it's also a little easier to change/add displacement values.
Whoops, didn't realize you wanted all 8 neighbors. Easy fix though. :)