Move inside a numpy array

In this notebook we’ll apply a simple rule to a »mover« that walks through a numpy array and shifts values on its way. Move one step forward - turn the cell into a 1, then one step to the right - turn the cell into a 1, then forward, right, forward, right, …

Done by hand

First some cells that perform the action for one starting position (0, 0) manually. This helps to see what we have to do.

import numpy as np
# create an array filled with zeros (integers) and reshape it into a 3x3 matrix
a = np.zeros(9).astype(int).reshape(3, 3)
print(a)
[[0 0 0]
 [0 0 0]
 [0 0 0]]
x, y = (0, 0)  # starting position
# set the starting position to 1
# as the first axis is vertical in numpy,
# x and y are shifted to meet our logic of flat
a[y][x] = 1 
print(a)
[[1 0 0]
 [0 0 0]
 [0 0 0]]
# forward = x +1
x +=1
a[y][x] = 1
print(a)
[[1 1 0]
 [0 0 0]
 [0 0 0]]
# right = y + 1
y += 1
a[y][x] = 1
print(a)
[[1 1 0]
 [0 1 0]
 [0 0 0]]
# forward = x +1
x +=1
a[y][x] = 1
print(a)
[[1 1 0]
 [0 1 1]
 [0 0 0]]
# right = y + 1
y += 1
a[y][x] = 1
print(a)
[[1 1 0]
 [0 1 1]
 [0 0 1]]
# forward = x +1
x +=1
a[y][x] = 1
print(a)
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
Cell In[40], line 3
      1 # forward = x +1
      2 x +=1
----> 3 a[y][x] = 1
      4 print(a)

IndexError: index 3 is out of bounds for axis 0 with size 3

The IndexError is a common error when dealing with lists and arrays. x is 3, but the array has only the indices [0, 1, 2]. Thus we have to figure out when x or y are larger than the last index of the axis.

Done with a while loop

x, y = (0, 0) # starting position
x_size, y_size = (3, 3)

a = np.zeros(y_size*x_size).astype(int).reshape(y_size, x_size)
# set starting position to 1
a[y][x] = 1

while True:
    # move forward
    x += 1
    if x >= x_size:
        break
    else:
        a[y][x] = 1

    # move right
    y += 1
    if y >= y_size:
        break
    else:
        a[y][x] = 1

print(a)
[[1 1 0]
 [0 1 1]
 [0 0 1]]

The resulting matrix is the same we’ve got with our manual method before the IndexError occured.

Next we have to put this while loop into a nested for-loop to create a maxtrix/symbol/shape for every possible starting position.

As axes and the shifting of axes may be confusing, it’s a good idea to test the loop before we insert more code into it:

x_size = 3
y_size = 2

for col in range(y_size):
    for row in range(x_size):
        x, y = row, col # shifting x and y axes for np
        a = np.zeros(y_size*x_size).astype(int).reshape(y_size, x_size)
        a[y][x] = 1
        print(a)
[[1 0 0]
 [0 0 0]]
[[0 1 0]
 [0 0 0]]
[[0 0 1]
 [0 0 0]]
[[0 0 0]
 [1 0 0]]
[[0 0 0]
 [0 1 0]]
[[0 0 0]
 [0 0 1]]

The loops work as wished, we’re moving from top left to bottom right col by col and row by row.

Everything together

import numpy as np

res = [] # store resulting arrays
x_size, y_size = (5, 5)

for col in range(y_size):
    for row in range(x_size):
        x, y = row, col # shifting x and y axes for np
        a = np.zeros(y_size*x_size).astype(int).reshape(y_size, x_size)
        # set initial position to 1
        a[y][x] = 1

        while True:
            # move forward
            x += 1
            if x >= x_size:
                break
            else:
                a[y][x] = 1

            # move right
            y += 1
            if y >= y_size:
                break
            else:
                a[y][x] = 1

        res.append(a)
from matplotlib import pyplot as plt
from matplotlib import colors

cols, rows = x_size, y_size
scale = 3

fig=plt.figure(figsize=(cols*scale, rows*scale))

cmap = colors.ListedColormap(['white','black'])

for i, data in enumerate(res):
    fig.add_subplot(rows, cols, i+1)
    plt.xticks(ticks=[])
    plt.yticks(ticks=[])
    plt.imshow(data, cmap=cmap)
plt.show()
_images/np_move_17_0.png

Create your own rule-based mover

Modify the code below so that it produces signs according to your own rules.

import numpy as np
from matplotlib import pyplot as plt
from matplotlib import colors

res = [] # store resulting arrays
x_size, y_size = (5, 5)

for col in range(y_size):
    for row in range(x_size):
        x, y = row, col # shifting x and y axes for np
        a = np.zeros(y_size*x_size).astype(int).reshape(y_size, x_size)
        # set initial position to 1
        a[y][x] = 1

        while True:
            # move forward
            x += 1
            if x >= x_size:
                break
            else:
                a[y][x] = 1

            # move right
            y += 1
            if y >= y_size:
                break
            else:
                a[y][x] = 1

        res.append(a)
        
        
cols, rows = x_size, y_size
scale = 3

fig=plt.figure(figsize=(cols*scale, rows*scale))

cmap = colors.ListedColormap(['white','black'])

for i, data in enumerate(res):
    fig.add_subplot(rows, cols, i+1)
    plt.xticks(ticks=[])
    plt.yticks(ticks=[])
    plt.imshow(data, cmap=cmap)
plt.show()
_images/np_move_19_0.png