1

I am not sure what is right terminology here. Please correct. I have a grid (2D array) that is looped. By this I mean first row is the next after last row. Same for columns.

I want to slice subset of big grid having this looped rule in mind. So, having grid:

[[ 0  1  2  3  4  5  6  7  8  9]
 [10 11 12 13 14 15 16 17 18 19]
 [20 21 22 23 24 25 26 27 28 29]
 [30 31 32 33 34 35 36 37 38 39]
 [40 41 42 43 44 45 46 47 48 49]
 [50 51 52 53 54 55 56 57 58 59]
 [60 61 62 63 64 65 66 67 68 69]
 [70 71 72 73 74 75 76 77 78 79]
 [80 81 82 83 84 85 86 87 88 89]
 [90 91 92 93 94 95 96 97 98 99]]

And I want subset with size 3 by 3 centered in the middle (5,5), I would get:

[[44 45 46]
 [54 55 56]
 [64 65 66]]

But if I want it to be centered in (0,0) I would get:

[[99 90 91]
 [ 9  0  1]
 [19 10 11]]

In my current solution I've combined np.roll with slicing. It's working, but I am looking for more performant solution.

My current solution:

def get_centered_section(arr, center, side_size):
    if side_size % 2 is 0:
        raise "size shuold be odd number"
    half_side_size = int((side_size - 1) / 2)
    w, h = arr.shape
    x, y = center

    ystart = y - half_side_size
    if ystart < 0:
        arr = np.roll(arr, abs(ystart), 0)
        ystart = 0
    elif ystart + side_size >= h:
        overflow = ystart + side_size - h
        ystart -= overflow
        arr = np.roll(arr, -overflow, 0)

    xstart = x - half_side_size
    if xstart < 0:
        arr = np.roll(arr, abs(xstart), 1)
        xstart = 0
    elif xstart + side_size >= w:
        overflow = xstart + side_size - w
        xstart -= overflow
        arr = np.roll(arr, -overflow, 1)

    return arr[ystart:ystart+side_size,xstart:xstart+side_size]

test_a1 = np.reshape(np.arange(10*10), (10, 10))
get_centered_section(test_a1, (0, 0), 3)

Maybe there is a way to cache my way out. My specific usage will require going through each cell getting this kinda slice.

1
  • 1
    The right terminology for what you call looped is wrapping around. Searching for 'numpy wrap around', you arrive at this question, the answer with numpy.take seems a good solution. This and this also seem very closely related. I suggest to close this question as a duplicate of one of these ... Commented Dec 3, 2016 at 11:25

1 Answer 1

2

One approach would involve padding with wrap around using np.pad and then slicing, like so -

def get_centered_section(a, center, side_size):
    ext_size = (side_size[0]-1)/2, (side_size[1]-1)//2
    a_pad = np.lib.pad(a, ([ext_size[0]],[ext_size[1]]), 'wrap')
    return a_pad[center[0]:center[0]+side_size[0], \
                 center[1]:center[1]+side_size[1]]

Few sample runs -

In [94]: a
Out[94]: 
array([[ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9],
       [10, 11, 12, 13, 14, 15, 16, 17, 18, 19],
       [20, 21, 22, 23, 24, 25, 26, 27, 28, 29],
       [30, 31, 32, 33, 34, 35, 36, 37, 38, 39],
       [40, 41, 42, 43, 44, 45, 46, 47, 48, 49],
       [50, 51, 52, 53, 54, 55, 56, 57, 58, 59],
       [60, 61, 62, 63, 64, 65, 66, 67, 68, 69],
       [70, 71, 72, 73, 74, 75, 76, 77, 78, 79],
       [80, 81, 82, 83, 84, 85, 86, 87, 88, 89],
       [90, 91, 92, 93, 94, 95, 96, 97, 98, 99]])

In [95]: get_centered_section(a, center = (0,0), side_size = (3,3))
Out[95]: 
array([[99, 90, 91],
       [ 9,  0,  1],
       [19, 10, 11]])

In [97]: get_centered_section(a, center = (5,5), side_size = (5,5))
Out[97]: 
array([[33, 34, 35, 36, 37],
       [43, 44, 45, 46, 47],
       [53, 54, 55, 56, 57],
       [63, 64, 65, 66, 67],
       [73, 74, 75, 76, 77]])

In [98]: get_centered_section(a, center = (7,2), side_size = (3,5))
Out[98]: 
array([[60, 61, 62, 63, 64],
       [70, 71, 72, 73, 74],
       [80, 81, 82, 83, 84]])
Sign up to request clarification or add additional context in comments.

5 Comments

That's funny. I thought of similar solution second before refreshing and seeing it =) I thought that if I pad once with maximum I need and then slice normally in for-loops using modified, padded grid. Big difference is that I didn't know how to do this padding.
This solution many times slower then mine. With 10x10 grid and 3x3 kernel my takes ~ 0.05ms and this one ~0.3ms
But maybe when I iterate it will take less time. Let me check.
@AleksandrMotsjonov If you have to iterate, it would make sense to store the padded version and slice on the padded version iteratively, rather than creating the padded version again and again.
Of cause. That's what I was thinking in the first comment. And that's what I just did. Seems like it's 15 times faster then mine! Thx.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.