Add legend at the top of a plot - r

My legend is cropped in my plot. To solve this, I know I have to deal with margins but I do not know how. And the use of xpd=TRUE does not seems to work.
My code has this structure:
plot(x,y1)
par(new=TRUE)
plot(x,y2)
par(new=TRUE)
plot(x,y3)
...
par(xpd=TRUE)
legend(...)
The entire code:
time<-c(18,19, 21, 25, 26)
layer_0<-c(0.73,0.78,0.95,0.83,0.77)
layer_0_sd<-c(0.04,0.04,0.03,0.13,0.19)
layer_1<-c(0.89,0.9,0.61,0.28,0.08)
layer_1_sd<-c(0.03,0.01,0.14,0.14,0.09)
layer_2<-c(0.66,0.6,0.21,0.01,0)
layer_2_sd<-c(0.06,0,0.12,0.01,0)
layer_3<-c(0.23,0.13,0.05,0,0)
layer_3_sd<-c(0.07,0,0.03,0,0)
layer_4<-c(0.03,0.01,0.01,0,0)
layer_4_sd<-c(0.01,0,0.01,0,0)
layer_5<-c(0,0,0,0,0)
layer_5_sd<-c(0,0,0,0,0)
epsilon=0.02
plot(time, layer_0, ylim=c(0,1), type="o", lty=1, lwd=2,ylab="Longitudinal fCOVER", xlab="Days after seeding", cex.lab=1.5, cex.axis=1.5, cex.main=1.5, cex.sub=1.5)
segments(time, layer_0-layer_0_sd,time, layer_0+layer_0_sd, lwd=2)
segments(time-epsilon,layer_0-layer_0_sd,time+epsilon,layer_0-layer_0_sd, lwd=2)
segments(time-epsilon,layer_0+layer_0_sd,time+epsilon,layer_0+layer_0_sd, lwd=2)
for (i in c(1:5)){
par(new=TRUE)
eval(parse(text=paste("plot(time, layer_",i,", ylim=c(0,1), type='o', xlab='', ylab='', xaxt='n', yaxt='n',lwd=2, lty=",i+1,", col=",i+1,")+
segments(time, layer_",i,"-layer_",i,"_sd,time, layer_",i,"+layer_",i,"_sd, col=",i+1,",lwd=2)+
segments(time-epsilon,layer_",i,"-layer_",i,"_sd,time+epsilon,layer_",i,"-layer_",i,"_sd, col=",i+1,",lwd=2)+
segments(time-epsilon,layer_",i,"+layer_",i,"_sd,time+epsilon,layer_",i,"+layer_",i,"_sd, col=",i+1,",lwd=2)", sep="")))
}
par(xpd=TRUE)
legend(x=17.65, y=1.3, c("0-5 cm","5-10 cm", "10-15 cm", "15-20 cm", "20-25 cm","25-30 cm"), lty=c(1,2,3,4,5,6), col=c(1,2,3,4,5,6),ncol=3, lwd=2, cex=1.5)

Just as an alternative, the use of the parameter inset makes this very easy. A similar duscussion can be found here.
for your example:
par(mar=c(5, 5, 6, 3), xpd=TRUE)
plot(time, layer_0, ylim=c(0,1), type="o", lty=1, lwd=2,ylab="Longitudinal fCOVER", xlab="Days after seeding", cex.lab=1.5, cex.axis=1.5, cex.main=1.5, cex.sub=1.5)
segments(time, layer_0-layer_0_sd,time, layer_0+layer_0_sd, lwd=2)
segments(time-epsilon,layer_0-layer_0_sd,time+epsilon,layer_0-layer_0_sd, lwd=2)
segments(time-epsilon,layer_0+layer_0_sd,time+epsilon,layer_0+layer_0_sd, lwd=2)
for (i in c(1:5)){
par(new=TRUE)
eval(parse(text=paste("plot(time, layer_",i,", ylim=c(0,1), type='o', xlab='', ylab='', xaxt='n', yaxt='n',lwd=2, lty=",i+1,", col=",i+1,")+
segments(time, layer_",i,"-layer_",i,"_sd,time, layer_",i,"+layer_",i,"_sd, col=",i+1,",lwd=2)+
segments(time-epsilon,layer_",i,"-layer_",i,"_sd,time+epsilon,layer_",i,"-layer_",i,"_sd, col=",i+1,",lwd=2)+
segments(time-epsilon,layer_",i,"+layer_",i,"_sd,time+epsilon,layer_",i,"+layer_",i,"_sd, col=",i+1,",lwd=2)", sep="")))
}
legend("top", inset = c(0, -0.25), legend=c("0-5 cm","5-10 cm", "10-15 cm", "15-20 cm", "20-25 cm","25-30 cm"), lty=c(1,2,3,4,5,6), col=c(1,2,3,4,5,6),ncol=3, lwd=2, cex=1.5)

If I have to place a legend outside a plot I generally do it with a separate panel, as I find it much easier to control positions and sizes then.
layout(1:2, heights=c(1, 5))
# Legend panel
par(mar=rep(0,4))
plot(0, 0, type="n", ann=FALSE, axes=FALSE)
legend("center", c("5-10 cm", "15-20 cm", "25-30 cm"), horiz=TRUE,
lty=2:4, col=1:3)
# Plot panel
par(mar=c(5,4,0,2))
plot(1:20, cumsum(rnorm(20)))
Or if you want to follow the theme you started using par(new=TRUE) you could do this
par(mar=c(5,4,5,2))
plot(1:20, cumsum(rnorm(20)))
par(new=TRUE, mar=c(0,0,1,0))
plot(0, 0, type="n", ann=FALSE, axes=FALSE)
legend("top", c("5-10 cm", "15-20 cm", "25-30 cm"), horiz=TRUE,
lty=2:4, col=1:3)
Both give the result below.

Related

Why do I loose parts of the plot when exporting it as metafile?

I'm working with GLMs and therefore do some plots with the "visreg" package, and I also add usual plots to them. My script looks like this:
library(visreg)
visreg(model01, scale = "response", type="conditional",line=list(col="green"), ylim=c(0,1), xlim=c(0,400), rug=F, axes=F, ann=F)
par(new=T)
plot(data$x ~ data$y, ylim=c(0,1), xlim=c(0,400), col=rgb(0,100,50,50, maxColorValue=255), pch=19, cex=2, axes=F, ann=F)
par(new=T)
visreg(model02, scale = "response", type="conditional",line=list(col="red"), ylim=c(0,1), xlim=c(0,400), rug=F, axes=F, ann=F )
par(new=T)
plot(data$x ~ data$z, ylim=c(0,1), xlim=c(0,400), col=rgb(0,50,100,50, maxColorValue=255), pch=19, cex=2, las=1, xlab="label01", ylab="label02")
axis(1)
axis(2, las=1)
box()
col1 <- c(rgb(0,100,50, maxColorValue=155), rgb(0,50,100, maxColorValue=155))
legend (315, 1.05, legend=c("bla01", "bla02"), col=c("green","red"), y.intersp=1.3, cex=0.9, lty=1, lwd=2, bty="n")
legend (309, 1.06, legend=c("",""), col=col1, pch=19, y.intersp=1.2, bty="n")
The visreg() parts plot the regression model while the plot() parts plot the corresponding data. The whole plot looks the way I want it in RStudio, but when I export it as a metafile I loose the plot() parts of it (except the axis labels, they are still there). Does anybody know why that is and what I can do to export it all as a metafile?
Cheers,
Alex

Modifying aspect ratio of a plot in R

Here is my first plot
par(bg="white")
image(m, main = paste("generation: ",generation), ylab="", col=heat.colors(100), xaxt="n", yaxt="n", xlab="")
It is a square, and I'd like it to be a rectangle. So I did
par(bg="white", mar=c(16,1,16,1))
image(m, main = paste("generation: ",generation), ylab="", col=heat.colors(100), xaxt="n", yaxt="n", xlab="")
But then the title (main) is really far away from the plot. So I did
par(bg="white", mar=c(16,1,16,1))
image(m, ylab="", col=heat.colors(100), xaxt="n", yaxt="n", xlab="")
legend(0.32, 3.5, paste("Generation: ", IntFormat(generation, 4)), border="white", xpd=TRUE, box.col="white", cex=1.5)
Well.. I wouldn't say it is splendid, but I was satisfied. So let's put that into a .png
png(paste0(folder.images, "pg_",IntFormat(generation,4),".png"))
par(bg="white", mar=c(16,1,16,1))
image(m, ylab="", col=heat.colors(100), xaxt="n", yaxt="n", xlab="")
legend(0.32, 3.5, paste("Generation: ", IntFormat(generation, 4)), border="white", xpd=TRUE, box.col="white", cex=1.5)
dev.off()
and here is what the .png looks like!
Is there a better solution to make a rectangle out of my square than using the parameter mar that force me to add a title with legend and to search by trial and error where the center to the picture is for the title.
Why is the .png different from what is shown in the R window?
I could achieve this:
png("test.png", res = 150, width = 8, height = 1, units = "in")
par(mar = c(1,1,1,1))
image(matrix(1:10, ncol=1), ylab="", col=heat.colors(100), xaxt="n",
yaxt="n", xlab="", main="Generation: 0001")
dev.off()

How to plot horizontal y label

I tried to plot a horizontal y label using the following code:
require(grDevices)
par(mfrow=c(2,1), mar=c(0,3,0,0.5)+0.1, oma=c(3,0,0.5,0)+0.1,
mgp=c(2,1,0), cex=tcex)
par(las=1)
#pl-d001
plot(1, type="n", yaxt='n', xaxt='n', xaxs="i", yaxs="i", xlab="xlabel",
ylab=expression("axis "*italic(r[infinity])), xlim=c(0, 0.4), ylim=c(0, 1))
But, I only get a normal (vertical) Y label. What is wrong here?
As #rawr said in the comments, your best chance is with mtext:
plot(1, type="n", yaxt='n', xaxt='n', xaxs="i", yaxs="i",
xlab="xlabel", ylab="", xlim=c(0, 0.4), ylim=c(0, 1))
mtext(expression("axis "*italic(r[infinity])),side=2,las=1,line=1)
The reason why you can't do that directly from plot is that, internally, plot uses title to draw those labels and title (for some reason) doesn't take las or srt as arguments.

R plot multiple line plots (3) against single bar plot

I'm trying to plot multiple line plots against a single bar plot (in this example it's grouped but doesn't have to be) but I'm running into some issues with left y-axis resizing in strange ways. It seems ok-ish when there are two lines, but then extends the axis out when there are more so the scale is wrong and doesn't line up with the bars.
I need to end up with a situation where I can customise the labels on both the left and right y-axes, hence the use of axis at the bottom of the code. In the real implementation I'll be dynamically using 'M' for million or 'K' for thousand on the left, and '%' on the right.
Any help would be very much appreciated.
x = c("a","b","c","d")
y= cbind(c(50,40,30,20), c(40,30,20,10))
y2 = c(0.80,0.65,0.75,0.50)
y3 = c(0.30,0.20,0.50,0.60)
y4 = c(0.20,0.30,0.40,0.45)
#
par(mar=c(5,8,3,5))
bp <- barplot(t(y), beside=TRUE, las=1, names.arg=x, axes=TRUE, ylim=c(0,max(y)+10), labels=c("0","10 M","20 M","30 M","40 M","50 M","60 M"))
xlim <- c(floor(min(bp)), ceiling(max(bp)))
#
par(new=T)
plot(colMeans(bp), y2, type="o", col="black", lwd=3, lty=1, xaxt="n", yaxt="n", xlab="", ylab="", xlim = xlim)
points(colMeans(bp), y2, pch=20)
par(new=T)
plot(colMeans(bp), y3, type="o", col="red", lwd=3, lty=1, xaxt="n", yaxt="n", xlab="", ylab="", xlim = xlim)
points(colMeans(bp), y3, pch=20)
par(new=T)
plot(colMeans(bp), y4, type="o", col="red", lwd=3, lty=1, xaxt="n", yaxt="n", xlab="", ylab="", xlim = xlim)
points(colMeans(bp), y4, pch=20)
axis(side=2, at=c(0,0.10,0.20,0.30,0.40,0.50,0.60), labels=c("0","10 M","20 M","30 M","40 M","50 M","60 M"), las=1, pos=0.555)
axis(side=4, pretty(c(0,max(y2))), ylim=c(0,max(y2)), col='black', ylab='', las=1 )
You can use the lines() argument for the second and third line, the everything works fine. Here is an example for the third Line:
#par(new=T)
lines(colMeans(bp), y4, type="o", col="red", lwd=3, lty=1)
points(colMeans(bp), y4, pch=20)

How to avoid wired ylab error when plotting in R

I need a two y-axes figure. hrbrmstr suggested to use simple plots. But when adapting the graph to my setting I observed I cannot add the ylab on the right hand side, getting a wired error:
Error in axis(4, ylim = c(0, 1), col = "black", col.axis = "black", las = 1, :
'labels' is supplied and not 'at'
Is this avoidable?
look at the code the bottom line fpr SOURCE OF ERROR
featPerf <- data.frame( expS=c("1", "2", "3", "4"),
exp1=c(1000, 0, 0, 0),
exp2=c(1000, 5000, 0, 0),
exp3=c(1000, 5000, 10000, 0),
exp4=c(1000, 5000, 10000,20000),
accuracy=c(0.4, 0.5, 0.65, 0.9) )
# make room for both axes ; adjust as necessary
par(mar=c(5, 5, 5, 7) + 0.2)
# plot the bars first with no annotations and specify limits for y
#barplot(as.matrix(featPerf[,2:5]), axes=FALSE, xlab="", ylab="", ylim=c(0, max(colSums(featPerf[2:5]))))
barplot(as.matrix(featPerf[,2:5]), axes=FALSE, xlab="", ylab="", beside=TRUE)
# make the bounding box (or not...it might not make sense for your plot)
#box()
# now make the left axis
axis(2, ylim=c(0, max(colSums(featPerf[2:5]))), col="black", las=1)
# start a new plot
par(new=TRUE)
# plot the line; adjust lwd as necessary
plot(x=1:4, y=featPerf[,6], xlab="Experiments", ylab="Abs. # of Features", axes=FALSE, type="l", ylim=c(0,1), lwd=5)
# annotate the second axis -- SOURCE OF ERROR -> VVVVVVVVVVVVVVVVVV
axis(4, ylim=c(0,1), col="black", col.axis="black", las=1, labels="Accuracy")
Like this?
par(mar=c(4,4,1,4) + 0.2)
barplot(as.matrix(featPerf[,2:5]), axes=FALSE, xlab="", ylab="", beside=TRUE)
axis(2, ylim=c(0, max(colSums(featPerf[2:5]))), col="black", las=1)
par(new=TRUE)
plot(x=1:4, y=featPerf[,6], xlab="Experiments", ylab="Abs. # of Features", axes=FALSE, type="l", ylim=c(0,1), lwd=5, col="blue")
axis(4, ylim=c(0,1), col="blue", col.axis="blue", las=1)
mtext("Accuracy",4,line=2, col="blue")
For the record, it is never a good idea to stack plots on top of each other this way (with two axes). I've made the line and the axis the same color in an attempt to draw attention to what you are doing, but this is still a very bad idea.
First of all it is not advisable to use two Y-axes in a same plot.
If you add at argument to the axis call, you get the name "Accuracy" on the right hand side of the plot.
axis(4, ylim=c(0,1), col="black", col.axis="black", las=1, labels="Accuracy",
at = .5)

Resources