I am using R for agent-based historical simulation and the code works but slowly. It loops through timesteps updating a dataframe of attributes of agents, and another with summary of overall state after each timestep (a generation). Looping above that are a few runs of each different parameter setting. Though it begins with 100 agents, under extreme settings (high S, low A) after e.g. five generations the population can grow above a thousand. I read that updating a matrix is faster than dataframe so I converted summary to a matrix. But I also hear that vectorisation is best so before I change agents to matrix I wonder if anyone please could suggest a way to make it more vectorised? Here is the code:
NextGeneration <- function(agent, N, S, A) {
# N is number of agents.
# S is probability that an agent with traditional fertility will have 2 sons surviving to the age of inheritance.
# A is probability that an heir experiencing division of estate changes his fertility preference from traditional to planned.
# find number of surviving heirs for each agent
excess <- runif(N) # get random numbers
heir <- rep(1, N) # everyone has at least 1 surviving heir
# if agent has traditional fertility 2 heirs may survive to inherit
heir[agent$fertility == "Trad" & excess < S] <- 2
# next generation more numerous if spare heirs survive
# new agents have vertical inheritance but also guided variation.
# first append to build a vector, then combine into new agent dataframe
nextgen.fertility <- NULL
nextgen.lineage <- NULL
for (i in 1:N) {
if (heir[i]==2) {
# two agents inherit from one parent.
for (j in 1:2) {
# A is probability of inheritance division event affecting fertility preference in new generation.
if (A > runif(1)) {
nextgen.fertility <- c(nextgen.fertility, "Plan")
} else {
nextgen.fertility <- c(nextgen.fertility, agent$fertility[i])
}
nextgen.lineage <- c(nextgen.lineage, agent$lineage[i])
}
} else {
nextgen.fertility <- c(nextgen.fertility, agent$fertility[i])
nextgen.lineage <- c(nextgen.lineage, agent$lineage[i])
}
}
# assemble new agent frame
nextgen.agent <- data.frame(nextgen.fertility, nextgen.lineage, stringsAsFactors = FALSE)
names(nextgen.agent) <- c("fertility", "lineage")
nextgen.agent
}
So the agents begin like this (Trad = traditional):
ID fertility lineage,
1 Trad 1
2 Trad 2
3 Trad 3
4 Trad 4
5 Trad 5
and after a few timesteps (generations) of random changes end up something like this:
ID fertility lineage
1 Plan 1
2 Plan 1
3 Trad 2
4 Plan 3
5 Trad 3
6 Trad 4
7 Plan 4
8 Plan 4
9 Plan 4
10 Plan 5
11 Trad 5