Here's a generic vectorized approach using scipy.spatial.distance.cdist
-
import scipy
# Stack lon and lat arrays as columns to form a Nx2 array, where is N is grid**2
lonlat = np.column_stack((lon.ravel(),lat.ravel()))
# Get the distances and get the argmin across the entire N length
idx = scipy.spatial.distance.cdist(lonlat,points).argmin(0)
# Get the indices corresponding to grid's shape as the final output
ind = np.column_stack((np.unravel_index(idx,lon.shape))).tolist()
Sample run -
In [161]: lon
Out[161]:
array([[-11. , -7.82 , -4.52 , -1.18 , 2.19 ],
[-12. , -8.65 , -5.21 , -1.71 , 1.81 ],
[-13. , -9.53 , -5.94 , -2.29 , 1.41 ],
[-14.1 , -0.04 , -6.74 , -2.91 , 0.976]])
In [162]: lat
Out[162]:
array([[-11.2 , -7.82 , -4.51 , -1.18 , 2.19 ],
[-12. , -8.63 , -5.27 , -1.71 , 1.81 ],
[-13.2 , -9.52 , -5.96 , -2.29 , 1.41 ],
[-14.3 , -0.06 , -6.75 , -2.91 , 0.973]])
In [163]: lonlat = np.column_stack((lon.ravel(),lat.ravel()))
In [164]: idx = scipy.spatial.distance.cdist(lonlat,points).argmin(0)
In [165]: np.column_stack((np.unravel_index(idx,lon.shape))).tolist()
Out[165]: [[0, 4], [0, 4], [0, 4], [0, 4], [0, 4], [0, 4], [3, 3]]
Runtime tests -
Define functions:
def find_indices(lon,lat,x,y):
lonlat = np.dstack([lon,lat])
delta = np.abs(lonlat-[x,y])
ij_1d = np.linalg.norm(delta,axis=2).argmin()
i,j = np.unravel_index(ij_1d,lon.shape)
return i,j
def loopy_app(lon,lat,pts):
return [find_indices(lon,lat,pts[i,0],pts[i,1]) for i in range(pts.shape[0])]
def vectorized_app(lon,lat,points):
lonlat = np.column_stack((lon.ravel(),lat.ravel()))
idx = scipy.spatial.distance.cdist(lonlat,points).argmin(0)
return np.column_stack((np.unravel_index(idx,lon.shape))).tolist()
Timings:
In [179]: lon = np.random.rand(100,100)
In [180]: lat = np.random.rand(100,100)
In [181]: points = np.random.rand(50,2)
In [182]: %timeit loopy_app(lon,lat,points)
10 loops, best of 3: 47 ms per loop
In [183]: %timeit vectorized_app(lon,lat,points)
10 loops, best of 3: 16.6 ms per loop
For squeezing out more performance, np.concatenate
could be used in place of np.column_stack
.