3

I want to slice an array of [x,y] coordinate pairs by x value in Python 3.x, in a similar way to the solution to this question but with coordinates rather than a 1d list.

For example for the (numpy) array of coordinates I want a function like:

coords = np.array([[1.5,10],[2.5,20],[3.5,30],[4.5,40],[5.5,50]])
def slice_coords_by_x(xmin, xmax, arr):
    *some function*
slice_coords_by_x(2, 4, arr)
>>>[[2.5,20],[3.5,30]]

Not overly fussy if the solution is inclusive or exclusive of xmin and xmax since i'll be using this over a large range of over 1000 or so.

Community
  • 1
  • 1
Ben Jones
  • 555
  • 6
  • 22

4 Answers4

2

Slice and create a mask with such min-max limits and thus select rows with boolean-indexing -

def slice_coords_by_x(arr, xmin, xmax):
    return arr[(arr[:,0] >= xmin) & (arr[:,0] <= xmax)] 

Sample runs -

In [43]: arr
Out[43]: 
array([[  1.5,  10. ],
       [  2.5,  20. ],
       [  3.5,  30. ],
       [  4.5,  40. ],
       [  5.5,  50. ]])

In [44]: slice_coords_by_x(arr, xmin=2, xmax=4)
Out[44]: 
array([[  2.5,  20. ],
       [  3.5,  30. ]])

In [45]: slice_coords_by_x(arr, xmin=1, xmax=5)
Out[45]: 
array([[  1.5,  10. ],
       [  2.5,  20. ],
       [  3.5,  30. ],
       [  4.5,  40. ]])
Divakar
  • 218,885
  • 19
  • 262
  • 358
  • Appreciated all the answers thanks all- chose this one purely because it doesn't depend on another package and it retains the numpy array type over Python's own array – Ben Jones Jan 16 '17 at 23:33
2

Without numpy, you could use bisect for this, to find insertion point. Note that the parameter is a list (I was adding None as second parameter as seen in here, but it's not useful).

import bisect

coords = [[1.5,10],[2.5,20],[3.5,30],[4.5,40],[5.5,50]]

def slice_coords_by_x(lower,upper,arr):
    l=bisect.bisect_left(arr,[lower])
    u=bisect.bisect_right(arr,[upper])
    return arr[l:u]

print(slice_coords_by_x(2,4,coords))

result:

[[2.5, 20], [3.5, 30]]

bisect requires that the list is sorted (which seems to be the case) or that won't work.

Community
  • 1
  • 1
Jean-François Fabre
  • 137,073
  • 23
  • 153
  • 219
2

Unordered

If the given list of points are unordered, you can use a filter, and materialize with list:

def slice_coords_by_x(xmin,xmax,arr):
    return list(filter(lambda p: xmin < p[0] < xmax,arr))

You can evidently feed your sorted list to this as well, but it will take considerably more time than the next approach.

Sorted list

Given the points are sorted by x-coordinate, you can use the bisect package:

def slice_coords_by_x(xmin,xmax,arr):
    left = bisect.bisect_left(arr,[xmin])
    right = bisect.bisect_right(arr,[xmax])
    return arr[left:right]
Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
2

Shouldn't simply

def slice_coords_by_x(xmin, xmax, arr):
    return [i for i in arr if xmin <= i[0] and i[0] <= xmax]

do the trick? It's readable, fast and accessible.

This list can be sorted or even pass an array, but the approach should be accessible enough to be changed to any needs.

Patrick Abraham
  • 208
  • 2
  • 10