I have plotted six graphs using R. They are arranged into 2 rows and 3 columns. At the bottom of all these graphs, I want to create a legend which contains four lines with a name next to each line, i.e.
**------- aaaaaaa -------bbbbbbbbbbbbbbbbb -------cccccc --------dddddd **
However, when I used the text.width function in legend, I couldn't figure out how I can work out values for text.width. As you can see above, some legend names are longer (e.g.bbbbbbbbbbbbbbbbb) therefore would need more space.
par(mfrow=c(2,3),mar=c(4.5,4,1,1),oma=c(2,0,0,0)) #Setting the margins
plot(1,1,xlim=c(5,10.7),ylim=c(15,25))
plot(1,1,xlim=c(5,10.7),ylim=c(45,70))
plot(1,1,xlim=c(5,10.7),ylim=c(20,33))
plot(1,1,xlim=c(5,10.7),ylim=c(15,25))
plot(1,1,xlim=c(5,10.7),ylim=c(45,70))
plot(1,1,xlim=c(5,10.7),ylim=c(20,33)) # plot six graphs
labels<-c("aaaaa","bbbbbbbbbbbbbbbbbbbbbbb","ccccccccccc", "ddddd") #legend labels
colour<-c("red","black","dark green","blue")#legend line colours
par(fig = c(0, 1, 0, 1), oma = c(0, 0, 0, 0), mar = c(0, 0, 0, 0), new = TRUE)
plot(0, 0, type = "n", bty = "n", xaxt = "n", yaxt = "n")## create an empty plot
legend("bottom", lty=1,lwd=1.5,ncol=4,labels,cex=1,
col=colour,bg="white",pt.cex=1,bty="n",text.width=c(0.2,0.2,0.2,0.2)) ## creating legend
So this is kind of tricky because while legend accepts a vector of different lengths for w0, it doesn't actually like it. It expects text.width to be a single value. So I tried to do a minimal amount of hacking to get it to work. This is kind of messy and only tested in R version 3.0.2. But there are two lines we need to change
body(legend)[[c(38,4,6)]]
# w <- ncol * w0 + 0.5 * xchar
body(legend)[[40]]
# xt <- left + xchar + xextra + (w0 * rep.int(0:(ncol - 1),
# rep.int(n.legpercol, ncol)))[1L:n.leg]
I would make sure you get the same results when running those commands, otherwise this hack won't work and may change other important code. Anyway, let's create our own version of legend with our changes
legend2 <- legend
body(legend2)[[c(38,4,6)]] <- quote({
cidx <- col(matrix(1, ncol=ncol, nrow=n.legpercol))[1:n.leg]
wc <- tapply(rep_len(w0,n.leg), cidx, max)
w <- sum(wc) + 0.5 * xchar
w0 <- rep(cumsum(c(0,wc[-length(wc)])), each=n.legpercol)[1:n.leg]
})
body(legend2)[[40]] <- quote(xt <- left + xchar + xextra + w0)
So a lot of this just reshaping the values to make sense if each label has a different length. Extra work was done to make sure columns of a legend are still all the same width (the width of the longest element). You should be able to swap out your legend() call with this new version, but be sure to add the text.width as follows
legend2("bottom", lty=1,lwd=1.5,ncol=4,labels,cex=1,
col=colour,bg="white",pt.cex=1,bty="n",
text.width=strwidth(labels, units="user"))
With the test data above, this should plot

I tried to change as little as possible to all other normal legend options should work (hopefully).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With