Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Adjusting base graphics legend label width

Tags:

r

legend

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.

R code:

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
like image 919
Head and toes Avatar asked Dec 30 '25 08:12

Head and toes


1 Answers

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

enter image description here

I tried to change as little as possible to all other normal legend options should work (hopefully).

like image 188
MrFlick Avatar answered Jan 01 '26 23:01

MrFlick