I recently had mathematical biology coursework requiring quite particular plotting needs. Sketching a graph and putting it in wouldn’t look brilliant and I thought that R would probably be able to plot what we wanted, even though it wasn’t very standard.
After going through the usual google syntax and function searches, I eventually produced what we wanted, and it showed me in the process just how flexible R plots can be.
Full Code
par(mar=c(5,4,4,1)+0.1) y = x^4/(x^4+7)+0.2 x = 0:300/10 plot(x, y ,type = 'l' ,xlim=c(0,3) ,ylim =c(0,1.2) ,ylab="" ,xlab = "" ,xaxt = 'n' ,yaxt = 'n' ,xaxs='i' ,yaxs='i' ,col="blue" ,lwd = 2 ,mgp=c(1.10,0.7,0.7) ) abline(a=0, b = 0.4, col = "red", lwd = 2, lty = 1) x0 = 0.527 y1 = 0.2109 segments(x0=x0,y0=0,x1=x0, y1=y1, lty= 3, lwd = 2) mtext(expression(bold("P"[1]^"*")), side=1, line=0.65, at = x0) mtext(">", side=1, line=-0.28, at = x0-0.2,cex=1.8, font = 0) mtext("<", side=1, line=-0.28, at = x0+0.2,cex=1.8, font = 0) x0 = 1.422 y1 = 0.5689 segments(x0=x0,y0=0,x1=x0, y1=y1, lty= 3, lwd = 2) mtext(expression(bold("P"[2]^"*")), side=1, line=0.65, at = x0) mtext("<", side=1, line=-0.28, at = x0-0.2,cex=1.8, font = 0) mtext(">", side=1, line=-0.28, at = x0+0.2,cex=1.8, font = 0) x0 = 2.7144 y1 = 1.0858 segments(x0=x0,y0=0,x1=x0, y1=y1, lty= 3, lwd = 2) mtext(expression(bold("P"[3]^"*")), side=1, line=0.65, at = x0) mtext(">", side=1, line=-0.28, at = x0-0.2,cex=1.8, font = 0) mtext("<", side=1, line=-0.28, at = x0+0.2,cex=1.8, font = 0)
First things first…
par(mar=c(5,4,4,1)+0.1) y = x^4/(x^4+7)+0.2 x = 0:300/10
par: The first thing I used was the par function to set the margins of the plot. These margins are the margins for the ENTIRE plot i.e on my RStudio window.
Par is short for parameters and is a way of globally storing any plotting options you want.
y: The y function is the function of the blue line. If anyone is interested, this function crops up in biological models that experience hysteresis (sudden shifts in stable state of the system that cannot be reversed in the same manner). It took the general form r (x^p / (x^p + c^p)), but as we only needed it qualitatively, I I basically just fiddled round with the constants until it looked nice on the plot.
x: x is equally spaced points with which the y function can be plotted against. Could have used the function curve() instead of plot(), but plot was more flexible.
The main plot
plot(x, y ,type = 'l' ,xlim=c(0,3) ,ylim =c(0,1.2) ,ylab="" ,xlab = "" ,xaxt = 'n' ,yaxt = 'n' ,xaxs='i' ,yaxs='i' ,col="blue" ,lwd = 2 ,mgp=c(1.10,0.7,0.7) )
The main plot needed a fair few parameters to change from default.
type: This takes a string to determine what type of plot is wanted. This is where R can be pretty flexible, e.g l is for lines, p is for points, b is for both.
xlim, ylim: The limits of where to show the plot on the x, y axis. Again, the numbers were pretty much determined by me fiddling with it until it looked OK.
xlab, ylab: These determines the labels for the x,y axes. Even though I hide them anyway, I include this line in case we wanted to show and label them.
xaxt, yaxt: Setting to “n” means that the axes ticks and numbers are hidden.
xaxs, yaxs: Setting to “i” means that there are no gaps between the plotting area and the edge of the box. We required it to be clear that the straight line started at the origin, so without axes, allowing the borders of the plot to be the axes seemed like the right thing.
col: Fairly self explanatory, sets the color of the line. Can either pass in a string like I did here (use colors() to see all the names, my favorites being tomato, papayawhip and seashell), a hexadecimal, an index or an RBG value.
lwd: Line width
mgp: Margin gap. I played around with the idea of including labels / axes, and if I did, I wanted them to be closer to the box. The mgp parameter allows this.
The red line
Adding extra lines to a plot is very easy. Especially when they are straight lines, which we luckily wanted to include here.
abline(a=0, b = 0.4, col = "red", lwd = 2, lty = 1)
ABline is just a straight line where you can specify a (intercept) and b (gradient).
lty: Line type (e.g dotted, dashed, solid)
The dotted lines
I was very surprised at how easy it was to add these.
x0 = 0.527 y1 = 0.2109 segments(x0=x0,y0=0,x1=x0, y1=y1, lty= 3, lwd = 2)
Segments gives complete freedom to just draw lines onto a plot. (x0, y0) is the starting point, (x1, y1) is the end point. The exact numbers to put into y1, x0 I found by using Wolfram Alfa to work out the simultaneous equation for the blue and red line.
Finally, the x-axis labels and arrows
mtext(expression(bold("P"[3]^"*")), side=1, line=0.65, at = x0) mtext(">", side=1, line=-0.28, at = x0-0.2,cex=1.8, font = 0) mtext("<", side=1, line=-0.28, at = x0+0.2,cex=1.8, font = 0)
mtext stands for margin text, and can be used to label features on any 4 sides of the plot. The first line does the P* label for the dotted line, whilst the next two put the arrows onto the x axis.
expression(): It’s not possible to write sub/super script in plain text, so you can use the expression() function along with ^ or [] for super/sub scripts respectively.
side: Integer for which side of the plot to put the text
line: How far away from the “margin” of the plot should the text be. To align the arrows onto the x axis itself I had to make this negative.
at: how far along on the x axis to put the text. It’s not in reference to the plot image, it will actually align to the values on the axis.
cex: Size of the text
font: Integer suggesting e.g bold, italic, none
Closing thoughts
It’s nice to know that R (even without any extra packages) is so flexible.