I have a plot with a horizontal legend:
legend("bottomleft", inset = c(0, -0.3), bty = "n",
x.intersp=0, xjust=0,yjust=0,
legend=c("AAPL", "Information Technology",
"Technology Hardware and Equipment", "S&P 500"),
col=c("black", "red", "blue3", "olivedrab3"),
lwd=2, cex = 0.5, xpd = TRUE, ncol = 4)
The problem is that there is a huge spacing between the first item of the legend, "AAPL", and the second item "Information Technology".
I tried adjusting the spacing using txt.width()
, but it didn't work at all. Or maybe I am not using this option as directed. This is how I have introduced the txt.width
option inside legend()
:
txt.width = c(2,1,1)
I am not sure if it relevant to mention, but my x-axis is an axis of dates!
Is there an easy way of customizing the spaces between the text in the legend?
Thanks!
text.width
can give you control over the width of each column in your legend, but it's not straightforward. Basically, text.width
is a vector that will be multiplied by another vector that is as long as your vector of legend strings. The elements of that second vector are integers from 0
to length(legend)-1
. See the code to legend()
for the gory details. The important thing is that you can think of this product of text.width
and the second vector as, approximately, the x coordinates for your legend elements. Then if you know which x coordinates you want, you can calculate what needs to be passed in the text.width
argument.
legtext <- c("AAPL", "Information Technology",
"Technology Hardware and Equipment", "S&P 500")
xcoords <- c(0, 10, 30, 60)
secondvector <- (1:length(legtext))-1
textwidths <- xcoords/secondvector # this works for all but the first element
textwidths[1] <- 0 # so replace element 1 with a finite number (any will do)
And then your final code could look something like this (except that we don't know your original plotting data or parameters):
plot(x=as.Date(c("1/1/13","3/1/13","5/1/13"), "%m/%d/%y"), y=1:3, ylim=c(0,3))
legend(x="bottomleft", bty = "n", x.intersp=0, xjust=0, yjust=0,
legend=legtext,
col=c("black", "red", "blue3", "olivedrab3"),
lwd=2, cex = 0.5, xpd = TRUE, ncol = 4,
text.width=textwidths)
As Andre Silva mentioned, the values you'll want in xcoords
and textwidths
will depend on the current size of your plot, the range of values specified for your x axis, etc.
Also, secondvector
above would look different if you had more than one element per column. For example, for two columns with two legend elements apiece, secondvector == c(0,0,1,1)
.
plot(1,1,xlab="",ylab="",xlim=c(0,2),ylim=c(0,2))
legend("bottomleft", text.width=c(0,0.085,0.235,0.35),
inset = c(0, -0.2), bty = "n", x.intersp=0.5,
xjust=0, yjust=0,
legend=c("AAPL", "Information Technology",
"Technology Hardware and Equipment", "S&P 500"),
col=c("black", "red", "blue3", "olivedrab3"),
lwd=3, cex = 0.75, xpd = TRUE, horiz=TRUE)
I used text.width
with four arguments to set the space between strings in the legend. The second argument inside text.width managed to set the distance between "AAPL" and "Information technology", and so on for the third and fourth arguments.
Unfortunately, I needed to reset the values inside text.width
every time I changed the plot size.
On my system (platform: x86_64-w64-mingw32, R version: 3.4.1 (2017-06-30)) the solutions provided so far by Andre Silva and pangja are not satisfactory. Both solutions require user input and are dependent on the device size. Since I get never used to the text.width
command and always had to adjust the values with try-and-error, I wrote the function (f.horlegend
). The function has similar arguments as the function legend
and is based on the idea posted here.
The function creates a horizontal (one row) legend, which can be positioned by the commands known from the function legend
, e.g. "bottomleft"
f.horlegend <- function(pos, legend, xoff = 0, yoff = 0,
lty = 0, lwd = 1, ln.col = 1, seg.len = 0.04,
pch = NA, pt.col = 1, pt.bg = NA, pt.cex = par("cex"), pt.lwd = lwd,
text.cex = par("cex"), text.col = par("col"), text.font = NULL, text.vfont = NULL,
bty = "o", bbord = "black", bbg = par("bg"), blty = par("lty"), blwd = par("lwd"), bdens = NULL, bbx.adj = 0, bby.adj = 0.75
) {
### get original par values and re-set them at end of function
op <- par(no.readonly = TRUE)
on.exit(par(op))
### new par with dimension [0,1]
par(new=TRUE, xaxs="i", yaxs="i", xpd=TRUE)
plot.new()
### spacing between legend elements
d0 <- 0.01 * (1 + bbx.adj)
d1 <- 0.01
d2 <- 0.02
pch.len <- 0.008
ln.len <- seg.len/2
n.lgd <- length(legend)
txt.h <- strheight(legend[1], cex = text.cex, font = text.font, vfont = text.vfont) *(1 + bby.adj)
i.pch <- seq(1, 2*n.lgd, 2)
i.txt <- seq(2, 2*n.lgd, 2)
### determine x positions of legend elements
X <- c(d0 + pch.len, pch.len + d1, rep(strwidth(legend[-n.lgd])+d2+pch.len, each=2))
X[i.txt[-1]] <- pch.len+d1
### adjust symbol space if line is drawn
if (any(lty != 0)) {
lty <- rep(lty, n.lgd)[1:n.lgd]
ln.sep <- rep(ln.len - pch.len, n.lgd)[lty]
ln.sep[is.na(ln.sep)] <- 0
X <- X + rep(ln.sep, each=2)
lty[is.na(lty)] <- 0
}
X <- cumsum(X)
### legend box coordinates
bstart <- 0
bend <- X[2*n.lgd]+strwidth(legend[n.lgd])+d0
### legend position
if (pos == "top" | pos == "bottom" | pos == "center") x_corr <- 0.5 - bend/2 +xoff
if (pos == "bottomright" | pos == "right" | pos == "topright") x_corr <- 1. - bend + xoff
if (pos == "bottomleft" | pos == "left" | pos == "topleft") x_corr <- 0 + xoff
if (pos == "bottomleft" | pos == "bottom" | pos == "bottomright") Y <- txt.h/2 + yoff
if (pos == "left" | pos == "center" | pos =="right") Y <- 0.5 + yoff
if (pos == "topleft" | pos == "top" | pos == "topright") Y <- 1 - txt.h/2 + yoff
Y <- rep(Y, n.lgd)
### draw legend box
if (bty != "n") rect(bstart+x_corr, Y-txt.h/2, x_corr+bend, Y+txt.h/2, border=bbord, col=bbg, lty=blty, lwd=blwd, density=bdens)
### draw legend symbols and text
segments(X[i.pch]+x_corr-ln.len, Y, X[i.pch]+x_corr+ln.len, Y, col = ln.col, lty = lty, lwd = lwd)
points(X[i.pch]+x_corr, Y, pch = pch, col = pt.col, bg = pt.bg, cex = pt.cex, lwd = pt.lwd)
text(X[i.txt]+x_corr, Y, legend, pos=4, offset=0, cex = text.cex, col = text.col, font = text.font, vfont = text.vfont)
}
Arguments
pos
position of the legend (c("bottomleft", "bottom", "bottomright", "left", "center", "right", "topleft", "top", "topright"))
legend
legend text
xoff
adjustement of position in x-direction. NB: the legend is plotted on a plot with limits = c(0,1)
yoff
same as xoff but in y-directin
lty
The line type. Line types can only be specified as an integer (0=blank, 1=solid (default), 2=dashed, 3=dotted, 4=dotdash, 5=longdash, 6=twodash)
lwd
The line width, a positive number, defaulting to 1
ln.col
The line color
seg.len
The length of the line, deafult to 0.04
pch
An integer specifying the symbol.
pt.col
The symbol color.
pt.bg
The background color of the symbol.
pt.cex
expansion factor for the symbol
pt.lwd
line width of symbol
text.cex
expansion factor for the text
text.col
text color
text.font
text font
text.vfont
see vfont in text-help
bty
the type of box to be drawn around the legend. The allowed values are "o" (the default) and "n"
bbord
color of the legend box border
bbg
background color
blty
border style
blwd
border line width
bdens
density of lines, see segment-help
bbx.adj
relative value to increase space between text and horizontal box line
bby.adj
same as bbx.adj but for vertical boc line
Unfortunately, I don't have time to create a package at the moment. But feel free to use the function. Any comments and ideas to improve the functions are welcome.
Some examples
plot(1:100, rnorm(100))
lgd.text <- c("12", "12")
sapply(c("bottomleft", "bottom", "bottomright", "left", "center", "right", "topleft", "top", "topright"), function(x) f.horlegend(x, lgd.text, pch=16, lty=c(NA, 1), bbg="orange"))
plot(1:100, rnorm(100))
lgd.text <- c("12", "132", "12345")
f.horlegend("topleft", lgd.text, pch=NA)
f.horlegend("top", lgd.text, pch=NA, bby.adj=1.5, bbord="red")
f.horlegend("left", lgd.text, xoff=0.2, pch=1, bby.adj=0, bbord="red", bbg="lightgreen")
f.horlegend("left", lgd.text, xoff=0.2, yoff=-0.05, pch=c(NA, 1, 6), lty=1, bbx.adj=2, bby.adj=0, bbord="red", bbg="lightgreen")
f.horlegend("topright", lgd.text, yoff=0.1, pch=c(1,2,NA), lty=c(NA, NA, 2), bbord="red", blty=2, blwd=2)
lgd.text <- c("12", "123456", "12345", "123")
f.horlegend("bottom", lgd.text, yoff=0.1, pch=c(1,2,NA), lty=c(NA, 1, 2), text.font=2)
f.horlegend("bottom", lgd.text, pch=c(1,2,NA), lty=c(NA, 1, 2), text.font=c(1,2,3))
plot(seq(as.POSIXct("2017-08-30"), as.POSIXct("2017-09-30"), by="weeks"), rnorm(5), type="l")
f.horlegend("topleft", "random values", lty=1)
For my case, there were 5 legends in horizontal way. I have to customize the spacing between each legend. Following was the code snippet for this purpose.
legend("topright",horiz = T, legend = df2, fill = col_box,
inset = c(-0.2,-0.1), xpd = TRUE, bty = 'n', density = density_value, angle = angle_value, x.intersp=0.3,
text.width=c(3.5,3.4,3.7,4.3,7))
It was the text.width
function that do the magic