Adjust spacing between text in horizontal legend

前端 未结 4 1265
甜味超标
甜味超标 2020-12-24 08:46

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(\"AAP         


        
相关标签:
4条回答
  • 2020-12-24 09:03

    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

    0 讨论(0)
  • 2020-12-24 09:09

    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).

    0 讨论(0)
  • 2020-12-24 09:26

    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)
    
    0 讨论(0)
  • 2020-12-24 09:27
    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)
    

    horizontal_legend

    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.

    0 讨论(0)
提交回复
热议问题