Created
May 29, 2017 15:44
-
-
Save bfbraum/2587ea2b602f0bda32942b6abc515eea to your computer and use it in GitHub Desktop.
A template for running and plotting a very simple agent-based model in R
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Template for running and plotting a very simple agent-based model in R | |
# Professor Bear Braumoeller, Department of Political Science, Ohio State | |
# This code creates a 20x20 grid of 0s and 1s, which represent values of some | |
# variable held by agents in those cells. It then chooses two adjacent cells, | |
# the first at random and the second at random from among the first cell's | |
# neighbors, and applies a simple rule -- the first cell takes on the value | |
# of the second. It iterates this cell selection and rule application 1,000 | |
# times, displays the result, and tracks the fraction of 1s in the matrix | |
# over time. | |
# This is not meant to represent a meaningful social process. It's just meant | |
# to be a template for students and colleagues to use to create more interesting | |
# agent-based models. | |
library(spam) | |
dimension <- 20 # Set the dimensions of the world you want to create | |
# Create grid and populate it with random 0s and 1s | |
test.mat <- matrix(sample(c(0,1), dimension*dimension, replace=TRUE), nrow=dimension, ncol=dimension) | |
# Start tracking the value of interest to you -- here, the fraction of 1s in the matrix | |
thing.to.track <- sum(test.mat)/(dimension*dimension) | |
# Open a plot window with two panes to visualize results | |
getOption("device")(width=10, height=5) | |
par(mfrow=c(1,2)) | |
pars <- c('plt','usr') | |
# Set the model in motion. Choose a cell, apply a rule, and plot the result. | |
for(iteration in 1:1000){ | |
# Select two cells to interact, using two random numbers between 1 and dimension | |
first.cell.row <- round(runif(1)*(dimension)+0.5) | |
first.cell.column <- round(runif(1)*(dimension)+0.5) | |
second.cell.row <- first.cell.row | |
second.cell.column <- first.cell.column | |
while((second.cell.row == first.cell.row) & (second.cell.column == first.cell.column)){ | |
second.cell.row <- first.cell.row + sample(c(-1, 0, 1), 1) | |
second.cell.column <- first.cell.column + sample(c(-1, 0, 1), 1) | |
} | |
# Make the world "wrap around" by adjusting location of second cell, if necessary | |
second.cell.row[second.cell.row==0] <- dimension | |
second.cell.row[second.cell.row==dimension+1] <- 1 | |
second.cell.column[second.cell.column==0] <- dimension | |
second.cell.column[second.cell.column==dimension+1] <- 1 | |
# Make the first cell take on the value of the second. | |
# A stupid rule, but these agents aren't very bright. | |
test.mat[first.cell.row,first.cell.column] <- test.mat[second.cell.row,second.cell.column] | |
# Track how applying this rule changes the statistic of interest | |
thing.to.track <- c(thing.to.track, sum(test.mat)/(dimension*dimension)) | |
# Now plot the result. The tricky part here is getting R to switch back | |
# and forth between the panes and update the plots correctly. It'd be | |
# possible just to draw a series of new plots, but they'd flicker a lot. | |
if(iteration==1){ | |
image(t(test.mat), col=c("white", "black"), axes=FALSE) | |
par1 <- c(list(mfg=c(1,1,1,2)), par(pars)) | |
plot(-100, -100, xlim=c(1,1000), ylim=c(0,1), ylab="Fraction of black squares", xlab="Iteration", type="n", cex.axis=0.8) | |
rect(par("usr")[1], par("usr")[3], par("usr")[2], par("usr")[4], col="#E6E6E6") | |
abline(h=c(0,0.25,0.5,0.75,1), col="white", lwd=0.5) | |
abline(v=c(0,200,400,600,800,1000), col="white", lwd=0.5) | |
par2 <- c(list(mfg=c(1,2,1,2)), par(pars)) | |
} else { | |
par(par1) | |
image(t(test.mat), col=c("white", "black"), axes=FALSE) | |
par(par2) | |
segments(iteration-1, thing.to.track[iteration-1], iteration, thing.to.track[iteration], col="black", lwd=1) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hm. Now that I've taken a first cut at this, I realize that it might generalize more readily if one were to create a series of functions and then execute those functions. The actual code once the functions have been written could then just look something like
setup.matrix(20, my.matrix)
setup.thing.to.track(thing.to.track)
for(iterate in 1:1000){
select.cell(my.matrix, first.cell)
select.neighbor(first.cell)
copy.values(first.cell, second.cell)
add.to.tracked.series(thing.to.track, current.state(my.matrix))
display.result(my.matrix)
}
That'd make it a lot easier to see, and change, what's going on at each step.