5

I have a function within a Python (2.7) class that should retrieve the values of the 'cells' around it in a 2 dimensional numpy array. If the index is out of range, I would the value should be set as None.

I'm struggling to find a way to do this without writing 8 try/catch statements, or without using multiple if x else None statements as is in my code below. While they would both work, they don't seem very well structured, and I'm thinking there must be a simpler way to do this - I'm probably caught thinking about this in entirely the wrong way. Any help would be much appreciated.

# This will return a dictionary with the values of the surrounding points
def get_next_values(self, column, row):
    if not (column < self.COLUMNS and row < self.ROWS):
        print "Invalid row/column."
        return False

    nextHorizIsIndex = True if column < self.COLUMNS - 2 else False
    nextVertIsIndex = True if row < self.ROWS - 2 else False

    n = self.board[column, row-1] if column > 0 else None
    ne = self.board[column+1, row-1] if nextHorizIsIndex else None
    e = self.board[column+1, row] if nextHorizIsIndex else None
    se = self.board[column+1, row+1] if nextHorizIsIndex and nextVertIsIndex else None
    s = self.board[column, row+1] if nextVertIsIndex else None
    sw = self.board[column-1, row+1] if nextVertIsIndex else None
    w = self.board[column-1, row] if row > 0 else None
    nw = self.board[column-1, row-1] if 0 not in [row, column] else None

    # debug
    print n, ne, e, se, s, sw, w, nw
2
  • 1
    Watch out for the negative index wrap-around ... ! Commented Feb 4, 2013 at 2:30
  • It hadn't occurred to me, thanks! Commented Feb 4, 2013 at 2:46

1 Answer 1

6

Here is a standard trick: Create your board with an edge padded with the value None. Then you can access any inner 3x3 square and fill in the appropriate values with

nw, n, ne, w, _, e, sw, s, se = (self.board[column-1:column+2, row-1:row+2]).ravel()

For example,

import numpy as np

board = np.empty((10,10), dtype = 'object')
board[:,:] = None
board[1:9, 1:9] = np.arange(64).reshape(8,8)
print(board)
# [[None None None None None None None None None None]
#  [None 0 1 2 3 4 5 6 7 None]
#  [None 8 9 10 11 12 13 14 15 None]
#  [None 16 17 18 19 20 21 22 23 None]
#  [None 24 25 26 27 28 29 30 31 None]
#  [None 32 33 34 35 36 37 38 39 None]
#  [None 40 41 42 43 44 45 46 47 None]
#  [None 48 49 50 51 52 53 54 55 None]
#  [None 56 57 58 59 60 61 62 63 None]
#  [None None None None None None None None None None]]

column = 1
row = 1
nw, n, ne, w, _, e, sw, s, se = (board[column-1:column+2, row-1:row+2]).ravel()
print(nw, n, ne, w, _, e, sw, s, se)
# (None, None, None, None, 0, 1, None, 8, 9)

Note that

  • when you define the board this way, the first non-None index is now 1, not 0.
  • I think it is more typical to think of the first index as the row, and the second index as the column, because when you print(board) that is the way the values are formatted. So perhaps you want board[row-1:row+2, column-1:column+2] instead. Of course, you could define your own print_board function, and then be free to use whatever convention you like.
Sign up to request clarification or add additional context in comments.

4 Comments

A good trick, but they may have trouble to use None in an array with numeric dtype. May have to use dtype object, which isn't very "numpythonic", or use nan instead of None.
True, nan might be a better choice if the dtype is float.
Thank you! This is really helpful - I wasn't aware of the board[:,:] = None syntax either. My dtype is int, so I'll give it a try padded with nan.
nan is compatible with float dtypes, but not int. You'll need to choose an integer value for int. Would a negative number like -1 be okay?

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.