A very useful function: eval parse text paste

A very useful function: eval parse text paste

When creating evolutions models and simulations in R, code can quickly become messy and slow. Functions that require fewer lines of code can speed up your program, and make it easier to follow what is happening. There are also often variables, such as number of alleles or loci, that need to be manipulated easily, without the requirement of changing multiple lines of code. This is where eval parse text paste becomes useful. The four stages of this can be understood by considering each of them individually, in reverse order.

paste simply converts something into text, and can combine several pieces of text together. For example. In the code below, we can paste the generation number of a program i next to the word generation. ,sep="" means there is no gap between the two pieces we are pasting together. We could make this to something else such as an underscore or a space if we wished.

i <- 1
paste("Generation ",i,sep="")
## [1] "Generation 1"

text takes this pasted expression as the input for parse, which converts it into an expression. For example take the code below. We are making a vector Output to store something, and creating an expression that can multiply the result for our value of i (generation) and multiply it by 50

i <- 1
Output <- c()
Output[i] <- i+1
parse(text=(paste("Output[",i,"]*50",sep="")))
## expression(Output[1] * 50)

eval then evaluates this expression, meaning it calculates the result of the expression, for that value of i

eval(parse(text=(paste("Output[",i,"]*50",sep=""))))
## [1] 100

We could use this to give us a Result for each generation i using the Output. The basic format is eval(parse(text=(paste("something",i,"something else",sep="")))).

Output <- c()
Results <- c()
for(i in 1:10){
  Output[i] <- i+1
  Results[i] <- eval(parse(text=(paste("Output[",i,"]*i",sep=""))))
}

This is a basic application that could be completed with other methods, but there are much more useful features of eval parse text paste.

At this stage I find it helpful to create a function to contain eval parse text paste. The code at the start of each line using this expression is quite long, making for some long lines of code that can be hard to read. Making functions in R is really easy. I won’t go into detail, but here is a good link for the basics of making your own simple fuctions

This function here is called belch3 because it is an eval expression with 3 pieces.

belch3 <- function(x, y, z) {
   eval(parse(text=(paste(x, y, z,sep=""))))
 }

You can make more functions for arguments with 4 or 5 pieces, which are sometimes necessary

belch4 <- function(x, y, z, a) {
  eval(parse(text=(paste(x, y, z, a, sep=""))))
}

belch5 <- function(x, y, z, a, b) {
  eval(parse(text=(paste(x, y, z, a, b, sep=""))))
}

We can now start to explore a useful application of these functions, in creating the right number of variables for a scenario. Imagine you are creating an evolutionary model and need a vector for each locus in your model, but each time you run the model you will use a different number of loci. Manually creating the right number of variables is tedious, and unneccessary with this function.

number.loci <- 3
for(i in 1:number.loci){
belch3("loci",i,"<<-c(i/i)")
}

We now have the correct number of variables produced each time, simply by changing the variable number.loci. Note that when creating variables we use <<- instead of just <-. This is to ensure that the variable is available to us, rather than just contained within the function. See this link if you want to find out more.

An example of using one of our longer functions is if we want to make another variable that uses information from one that we have just made. For example, here we are making a matrix for each loci, using information from that locus and random numbers (for no meaningful reason, simply to show the kinds of things that could be done)

for(i in 1:number.loci){
  belch5("loci.output",i,"<<-matrix(rep(loci",i,"+runif(50),25),nrow=5,ncol=5)")
}

This function is extremely useful for generating and storing variables where you are varying something such as allele number or loci number, where each of these variants require their own vectors/matrices that would normally have to be generated manually, with many more lines of code. If you don’t want to use the function, you can just use the full eval argument on each line

for(i in 1:5){
eval(parse(text=(paste("loci.output",i,"<-matrix(rep(loci",i,"+runif(2),25),nrow=5,ncol=5)",sep=""))))
}

but this is quite a bit longer, and can be more difficult to read, so I prefer to use functions.

There are many other uses for this function, with another key one being for plotting graphs.

Here, we are making a seperate graph for each locus, plotting the loci.output matrix. We don’t have to make each graph individually. This function will plot the correct number of graphs from number.loci

palette(rainbow(number.loci))  
for(i in 1:number.loci){
  belch5("plot(seq(1:25),loci.output",i,", col = i, type='l', xlab = 'Generation', ylab = 'Loci Output', main='Locus ",i,"')")
}

We could also plot more than one line on each of these graphs, for example for different alleles at each loci. For this purpose I am going to use the eval function to generate a matrix for each allele, with a column in that allele for each loci and a row for each generation.

number.allele <- 4
for(i in 1:number.allele){
  belch3("allele",i,"<<-matrix(runif(25,1,2),25,5)")
}

We can now plot the lines for each allele onto the graph for each loci. All in a very short section of code, that is fully flexible to changes in allele number and loci number

palette(rainbow(number.loci))  
for(i in 1:number.loci){
  belch5("plot(seq(1:25),loci.output",i,", col = i, type='l',lwd=3,ylim=c(1,2), xlab = 'Generation', ylab = 'Loci Output', main='Locus ",i,"')")
  for(j in 1:number.allele){
    belch3("points(seq(1:25),allele",j,"[,i], col=j, type='l')")
}
  }