In this notebook, we'll explore one of the most common types of environment - a grid. In this case, we'll be creating a two-dimension (2D) square grid. It's important to remember that grids can take other forms, including:
triangular
2D grids are the simple to implement and understand, and they are a very common choice for real-world spatial models. Let's begin by importing some libraries and exploring how a grid works.
### Imports
The imports below provide us with:
In addition, the %matplotlib inline
statement ensures that our plots are visible in ipython.
In [5]:
%matplotlib inline
# Imports
import numpy
import matplotlib.pyplot as plt
import seaborn; seaborn.set()
Before we begin, it's important to agree our terminology and notation down. Below are some grid terms that we'll be using:
Now that we know how to refer to elements of our grid, let's see the most common way to store and display grids with numerical values. These are grids whose cells store a numerical value, e.g., :
decimal quantity; e.g., mass of vegetation per square meter
To store this information, we can use numpy.array
objects. To brush up on arrays, you might review the following links:
To start, let's see how to create an empty array. We can do this in a number of ways, including:
zero: create an array whose cells are filled with zeros
Depending on the interpretation of cell values, your appropriate choice may vary.
In [11]:
# Set the grid size
grid_size = 5
# Create the grid
space = numpy.full((grid_size, grid_size), numpy.nan)
print(space)
In [16]:
# Set the grid size
grid_size = 5
# Create the grid
space = numpy.zeros((grid_size, grid_size))
print(space)
Our next step should be to visualize the grid in a more scalable fashion than reviewing the full array of cell values. Instead, we can visualize the grid in a form that allows our visual cortex to provide context and characterization.
Let's start by visualizing our "zero-d" space using the matplotlib.pyplot
method pcolor
.
In [17]:
# Now show the space
plt.figure()
plt.pcolor(space, snap=True)
plt.colorbar()
Out[17]:
Of course, since this is an "empty" grid with no values, we don't see much. Let's manually set one of the cells from 0 to 1 and plot again.
In [18]:
# Change cell (1,1)'s value to 1
space[1, 1] = 1
# Now show the space
plt.figure()
plt.pcolor(space, snap=True)
plt.colorbar()
Out[18]:
There are two things to note:
(1,1)
corresponds to the second row and second column in the grid because we are using zero-indexing(0, 0)
is in the lower left corner for this visualization; not all visualizations may choose this orientation! For example, many follow a convention like words on a page, with (0, 0) in the upper left cornerGrids are commonly setup with initial conditions, including:
agent positions
Let's walk through creating a grid that represents the binary presence of a resource. Our parameters are as follows:
grid_size
: "dimension" of the grid, i.e., number of cells in each row or column
num_cells
: number of cells to sample for resource endowmentIn order to determine which cells to endow, we'll need to sample from a random distribution. If you haven't already, now would be a great time to review the Basic Distributions notebook in the basic-random
folder. We'll make the following assertions for our spatial distribution of resource:
In [20]:
# Set the grid and cell parameters
grid_size = 25
num_cells = 10
# Create the space and activate random cells
space = numpy.zeros((grid_size, grid_size))
# Now sample the agents.
for cell_id in range(num_cells):
# Sample random position
row = numpy.random.randint(0, grid_size)
col = numpy.random.randint(0, grid_size)
# "Endow" the cell with the resource by setting its value to 1.
space[row, col] = 1
# Output some info about the agent.
print("Endowing cell ({0}, {1}).".format(row, col))
# Now show the space
plt.figure()
plt.pcolor(space, snap=True)
plt.colorbar()
Out[20]:
In [21]:
# Set the grid and agent parameters
grid_size = 25
num_cells = 10
spread = 1
# Create the space and activate random cells
space = numpy.zeros((grid_size, grid_size))
# Now sample the agents.
for cell_id in range(num_cells):
# Sample random position
row = int(numpy.random.normal((grid_size) / 2.0, spread))
col = int(numpy.random.normal((grid_size) / 2.0, spread))
# "Activate" the cell by setting its value to 1.
space[row, col] += 1
# Output some info about the agent.
print("Endowing cell ({0}, {1}).".format(row, col))
# Now show the space
f = plt.figure()
plt.pcolor(space, snap=True)
plt.colorbar()
Out[21]:
You should now see a small grid with blank background and a number of endowed cells. Unlike the previous example, the intensity of these cells should vary with the quantity of resource that has been placed.
spread
parameter come into play?Next, let's create an interactive widget that helps us understand how the spread parameter affects the grid's initial conditions. In the code below, we wrap our example above in a method labeled gaussian_initial_grid
, which we then call via the interact
method.
When you run the code, perform the following visual "experiments":
num_cells
and spread
, vary grid_size
. Does the qualitative character of the initial conditions change?grid_size
and spread
, vary num_cells
. Does the qualitative character of the initial conditions change?num_cells
and grid_size
, vary spread
. Does the qualitative character of the initial conditions change?
In [34]:
# First, we'll wrap our experiments above in a method that takes as input our grid parameters.
def gaussian_initial_grid(grid_size=25, num_cells=10, spread=1):
"""
Create an initial 2D grid with Gaussian/normal-distributed cells.
"""
# Create the space and activate random cells
space = numpy.zeros((grid_size, grid_size))
# Now sample the agents.
for cell_id in range(num_cells):
# Sample random position
row = int(numpy.random.normal((grid_size) / 2.0, spread))
col = int(numpy.random.normal((grid_size) / 2.0, spread))
# "Activate" the cell by setting its value to 1.
space[row, col] += 1
# Now show the space
f = plt.figure()
plt.pcolor(space, snap=True)
plt.colorbar()
# Import widget methods
from IPython.html.widgets import interact, fixed
interact(gaussian_initial_grid, grid_size=(5, 100),
num_cells = (1, 500),
spread=(1, 100))
Out[34]:
If all went as planned, in your third experiment with spread
, you should have generated an error. This particular error is called an IndexError, because it occured when we tried to access or index a cell that did not exist in our space.
For example, if we have a (10, 10) grid, there are no cells with the label (27, 4) or (-15, 8). However, based on our method for sampling the resource positions, there is some non-zero probability that we will try these cells.
This is an example of boundary in space. There are a number of approaches to addressing it:
-4
as a column, place the resource instead in column 0wrap cells around the boundary, similar to a torus or sphere; for example, if we drew -1
as a column, we would wrap all the way around to the last column and place the resource there instead.
In the example below, we'll go with the wrapping
or torus approach. To obtain the desired effect, we'll use the modulo operator %
.
In [42]:
# First, we'll wrap our experiments above in a method that takes as input our grid parameters.
def gaussian_initial_grid(grid_size=10, num_cells=100, spread=4):
"""
Create an initial 2D grid with Gaussian/normal-distributed cells with wrapping.
"""
# Create the space and activate random cells
space = numpy.zeros((grid_size, grid_size))
# Now sample the agents.
for cell_id in range(num_cells):
# Sample random position
row = int(numpy.random.normal((grid_size) / 2.0, spread)) % grid_size
col = int(numpy.random.normal((grid_size) / 2.0, spread)) % grid_size
# "Activate" the cell by setting its value to 1.
space[row, col] += 1
# Now show the space
f = plt.figure()
plt.pcolor(space, snap=True)
plt.colorbar()
# Import widget methods
from IPython.html.widgets import interact, fixed
interact(gaussian_initial_grid, grid_size=(5, 100),
num_cells = (1, 500),
spread=(1, 100))
Out[42]: