Goals

After working through handout you should:

Flow control

You may often need to control the flow of code execution such that you only want to execute certain lines if specific conditions are fulfilled. This can be done with if and else operators in R.

if and else

if evaluates a logical statement and only executes embedded code if the statement is TRUE. For example:

#Print statements depending on the value of y
y <- 3
if (y > 0){
  print("y is larger than zero")
}
## [1] "y is larger than zero"
if (y < 0){
  print("y is smaller than zero")
}
#Let's use a for loop to draw from a uniform, then an if statement 
#to keep values > 0.5
for(i in 1:20){
  p <- runif(1,0,1)
  if (p > 0.5){
    print(p)
  }
}
## [1] 0.7413317
## [1] 0.950984
## [1] 0.8635665
## [1] 0.9304076
## [1] 0.9403199
## [1] 0.9122959
## [1] 0.8869252
## [1] 0.7883813
## [1] 0.6306414

We can take things further by using else alongside if to branch the code into one of multiple possible directions. else is only considered if the if statement is FALSE. Let’s try it.

y <- 3
if (y > 0){
  print("y is greater than zero")
} else {
  print("y is smaller than zero")
}
## [1] "y is greater than zero"
y <- -2
if (y > 0) {
print("y is > 0")
} else {
print("y is < 0")
}
## [1] "y is < 0"
#Let's try again with more possibilities
for (i in 1:20) {
  p <- runif(1, 0, 1)
  if (p > 0.5) {
    print("p > .5")
  } else if (p > 0.2) {
    print("p > .2 and p < .5")
  } else {
  ## only enters if both if statements FALSE
    print("p < .2")
  }
}
## [1] "p > .2 and p < .5"
## [1] "p < .2"
## [1] "p > .5"
## [1] "p < .2"
## [1] "p > .2 and p < .5"
## [1] "p > .2 and p < .5"
## [1] "p > .2 and p < .5"
## [1] "p > .5"
## [1] "p > .5"
## [1] "p > .5"
## [1] "p > .2 and p < .5"
## [1] "p > .5"
## [1] "p > .5"
## [1] "p > .5"
## [1] "p < .2"
## [1] "p > .2 and p < .5"
## [1] "p > .5"
## [1] "p > .2 and p < .5"
## [1] "p > .5"
## [1] "p < .2"

The else statement must occur on the same line as the closing curly bracket of the if clause.

Custom functions

We have been working with functions, e.g. mean(), rnorm(), etc. Functions let us do things in R. R has many predefined functions, and R packages (which we’ll revisit later) greatly extend the set of functions available to you. Still, sometimes it is convenient to define your own functions. Let’s try this, defining a function to run one Wright-Fisher simulation of evolution by genetic drift.

#Define the function, use the function() function followed by 
#your default arguments. If no value is given, the default is nothing
wf <- function(p0 = 0.5, Ne = 100, t = 100){
  p <- rep(NA, t)  #Vector of allele frequencies
  p[1] <- p0 #First value is set to p0
  for(i in 2:t) {
    p[i] <- rbinom(1,size = Ne, p[i-1])/Ne
  }
  return(p)
}

#Now let's run our function using the default values 
oneP <- wf()
plot(1:100, oneP, type = "l", ylim = c(0, 1), xlab = "Time", ylab = "Allele frequency")

#Now let's try it with different conditions (non-default arguments)
oneP <- wf(p0 = 0.1, Ne = 200, t = 100)
plot(1:100, oneP, type = "l", ylim = c(0, 1), xlab = "Time", ylab = "Allele frequency")

You can set default values for function arguments when you create the function. If no other value is given for this argument when the function is called, the defaults are used. Objects defined within the function are not available outside the function. For example, p0 will not show up if I enter it into the console, having only defined it within my function. Use return() to return the output you want from a function, like we did with p in the above example. We will cover functions more as we go.

\[ \] \[ \] \[ \] \[ \]