1

This is my second question post on StackOverflow relating to coding in Python/Numpy.

I feel like there is definitely some sort of function which does the pseudocode:

np.random.choice([a[i-1,j],a[i+1,j],a[i,j-1],a[i,j+1]])==0 = 9

Essentially, I would like the random function to select a cell adjacent to mine (up, down, left, right) with the value 0, and replace said cell with a 9

Unforunately, I know why the code I typed is illegal. The first half of the statement returns a True/False boolean as I have used a comparison/checking operator. I can't set this into a value 9.

If I split the code-load into two codes and used an if statement with the random.choice (looking at an adjacent element that equalled zero), then following this, I would need some sort of function or definition to recall which cell (up down left or right) did the random generator originally select, to which I can then set it to 9.

Kind Regards,

EDIT: I may as well attach a sample code, so you can simply just run this (I am including my error)

a = np.empty((6,6,))
a[:] = 0
a[2,3]=a[3,3]=a[2,4] = 1

for (i,j), value in np.ndenumerate(a):
     if a[i,j]==1:
          np.random.choice([a[i-1,j],a[i+1,j],a[i,j-1],a[i,j+1]])==0 = 9
kevqnp
  • 153
  • 10

2 Answers2

1

You could select from a range of directions (up, down, left, right) that map to specific coordinate movements in the 2D array, like this:

# generate a dataset
a = np.zeros((6,6))
a[2,3]=a[3,3]=a[2,4] = 1

# map directions to coordinate movements
nesw_map = {'left': [-1, 0], 'top': [0, 1], 'right': [1,0], 'bottom': [0,-1]}
directions = nesw_map.keys()

# select only those places where a == 1
for col_ind, row_ind in zip(*np.where(a == 1)):  # more efficient than iterating over the entire array
    x = np.random.choice(directions)
    elm_coords = col_ind + nesw_map[x][0], row_ind + nesw_map[x][1]
    if a[elm_coords] == 0:
        a[elm_coords] = 9

Note that this does not do any type of bounds checking (so if a 1 appears at the edge, you might select an item "off the grid" which will result in an error).

Oliver W.
  • 13,169
  • 3
  • 37
  • 50
  • Thanks for your input again! I wasn't too familiar with dictionaries and keys, but your example has really cleared it up! I have a few questions: I'm not understanding how the zip(*np.where()) function operates. Why isn't the where function sufficient alone? – kevqnp Mar 21 '15 at 01:07
  • @kevenren, `np.where` will return as many lists as there are dimensions in the array. So `where` on the 2D array `a==1` will return *2* lists, each list containing the indices along the first & second axis respectively where the condition `a==1` was true. Let's call those lists `row_indices` and `col_indices`. Now, in the loop you simply want to look at each *pair* of elements from those 2 arrays. So you want to combine those 2 arrays, similar to the way a zipper works. Try building up that line in an interactive session to see what happens; a visual example explains it better in my opinion. – Oliver W. Mar 21 '15 at 01:27
  • Thanks. This is extremely useful. The asterisk separates the individual arrays into tuples and the zip pairs them up. – kevqnp Mar 21 '15 at 02:00
0

This is the most "basic" way of getting what you need (Adding a try/except statement provides error checking, so you can prevent any unwanted errors):

import random,numpy
a = numpy.empty((6,6,))
a[:] = 0
a[2,3]=a[3,3]=a[5,5] = 1

for (i,j), value in numpy.ndenumerate(a):
    var = 0
    if a[i,j]==1:
        while var==0:
            x=random.randrange(0,4)                   #Generate a random number
            try:      
                if x==0 and a[i-1,j]==0:              
                    a[i-1,j] =9                       #Do this if x = 0
                elif x==1 and a[i+1,j]==0:            
                    a[i+1,j] =9                       #Do this if x = 1
                elif x==2 and a[i,j-1]==0:             
                    a[i,j-1] =9                       #Do this if x = 2
                elif x==3 and a[i,j+1]==0:            
                    a[i,j+1] =9                       #Do this if x = 3
                var=1
            except:
                var=0

print a
logic
  • 1,739
  • 3
  • 16
  • 22
  • Thanks for your input! I was wondering what is the function of the "try" is. Would this be equivalent to having an if statement and a larger else? – kevqnp Mar 21 '15 at 01:10
  • @kevenren, it's there to be prevent an IndexError, when the algorithm finds e.g. a `1` at the edge of the matrix, for example the right edge. In that case, if the random choice would've been 3, `a[i, j+1]` would've generated an error because you're trying to access an element "outside" of the matrix. It *could* be replaced by an if-else statement, but in this case it's better to use try-except, because generally the number of edge elements is much smaller than the total number of elements. Have a look at the [EAFP principle](http://stackoverflow.com/q/11360858/2476444). – Oliver W. Mar 21 '15 at 01:34