Loops allow us to execute functions repeatedly under a set of conditions. Sometimes it is possible to vectorize code or to use the apply() function to avoid this, but other times loops are necessary. We’re going to focus on for loops today.
For loops are used when you know how many times you want to repeat (loop over) the code. The for loop takes two arguments. One is the iterator variable (most commonly i), and the other is the vector of values for the variable to loop over. The code you want to repeat goes in curly brackets, and can be any code that works in R (mostly). The loop will then be repeated with the iterator variable taking on a new value of the vector for each run of the loop, one at a time. Here’s a basic example that uses the print() function to display the value of the iterator value for each run of the loop.
for(i in 1:10){
print(i)
}
## [1] 1
## [1] 2
## [1] 3
## [1] 4
## [1] 5
## [1] 6
## [1] 7
## [1] 8
## [1] 9
## [1] 10
While the above example is great, we usually want to save the output of our loop to an object. To do this, we need to specify the output object (in this case a vector) prior to running the loop. Then, we use indexing to assign the output of that iteration to the current value of the iterator (in the case when your iterator is an integer vector starting at 1, this is not always true).
Let’s try storing ten draws of size ten (i.e. ten coin flips) from a binomial probability distribution with prob = 0.2 in a vector.
sims <- rep(NA,10)
for(i in 1:10){
sims[i] <- rbinom(1,size = 10, prob = 0.2)
}
sims
## [1] 1 1 4 3 1 1 5 1 2 3
#Here's the same thing, vectorized.
sims <- rbinom(n=10,size=10,prob=0.2)
sims
## [1] 1 2 3 3 1 3 0 3 4 3
Clearly we could have just done this with vectorization. Let’s make things a bit more complicated, and assume that the probability of success p increases by 0.1 with each sample.
p <- seq(from = 0.1, to = 1, by = 0.1)
sims <- numeric(10)
for(i in 1:10){
sims[i] <- rbinom(n=1,size=10,prob=p[i])
}
sims
## [1] 2 1 2 2 6 5 6 7 8 10
#Again, same thing vectorized
sims <- rbinom(n=10,size=10,prob=p)
sims
## [1] 3 1 1 5 7 2 7 9 10 10
While we once again did not need a for loop, there are times when they are necessary. For example, simulating a random walk where the next value depends on the previous one. We’ll start at some value then add a random deviate at each new step. Let’s try it and plot the results.
val <- rep(NA,1000)
val[1] <- 0
for(i in 2:1000){
#Start at the second value, since we assigned the first one
val[i] <- val[i - 1] + rnorm(1, mean=0, sd=1)
}
plot(x =1:1000, y = val, type = "l", xlab = "Timestep", ylab = "Value")
Let’s try something fun now. We’re going to simulate 10 random walks and plot each line on the same graph. This will involve one loop to assign each simulation to a row, then another loop within that to conduct each simulation. The results therefore will be stored in a matrix.
sims <- matrix(NA,nrow = 10, ncol = 1000)
sims[,1] <- 0 #Ensuring each sim starts at 0
#Note each for loop needs a unique indexer (k, then i)
for(k in 1:10){
for(i in 2:1000){
sims[k,i] <- sims[k, i - 1] + rnorm(1, mean=0, sd=1)
}
}
#Now let's plot everything. Make a plot for the first timeseries
#and make sure the y axis limits are big enough for all simulations
lb <- min(as.vector(sims))
ub <- max(as.vector(sims))
plot(x = 1:1000,y = sims[1,],xlab = "Timestep", ylab = "Value", ylim = c(lb,ub),type = "l")
#Now the next nine. Notice how we use the "lines" function
#instead of "plot" so we don't overwrite the first one
for(k in 2:10){
lines(1:1000,sims[k,])
}
As a final note, any vector can be used for a for loop. Here are some examples.
#While this works, remember this may not
#be a good example for indexing
for(i in seq(from = 0, to = 5, by = 0.3)){
print(i)
}
## [1] 0
## [1] 0.3
## [1] 0.6
## [1] 0.9
## [1] 1.2
## [1] 1.5
## [1] 1.8
## [1] 2.1
## [1] 2.4
## [1] 2.7
## [1] 3
## [1] 3.3
## [1] 3.6
## [1] 3.9
## [1] 4.2
## [1] 4.5
## [1] 4.8
#This one is useful if you don't know the full size of your vector,
#or if the vector length changes with each iteration of a higher loop
#Try running this a few times to see how the output changes
vec <- numeric(sample(x=10:20,size=1))
for(i in 1:length(vec)){
print(i)
}
## [1] 1
## [1] 2
## [1] 3
## [1] 4
## [1] 5
## [1] 6
## [1] 7
## [1] 8
## [1] 9
## [1] 10
## [1] 11
## [1] 12
## [1] 13
## [1] 14
#Predefined character vector
pets <- c("dog","cat","bird","alligator")
for(k in pets){
print(paste("My",k,"ran away",sep = " "))
}
## [1] "My dog ran away"
## [1] "My cat ran away"
## [1] "My bird ran away"
## [1] "My alligator ran away"
\[ \] \[ \] \[ \] \[ \]