title: R: legend with points and lines being different colors (for the same legend item)
body: <p>Using the legend() function is it possible to have the point and line be different colors? I feel like I'm missing something fairly obvious. The <code>pt.bg</code> option can change the background color, but I'm not seeing a <code>pt.fg</code> option</p>
  
  <p>The arises in the case when you use the lines() and points() command separately with different colors and want the legend to represent what is plotted.</p>
  
  <p>I thought it might be possible with the <code>merge</code> options, but I clearly don't quite understand what that is for.</p>
  
  <p>Example:</p>
  
  <pre><code>plot( 0, type="n", xlim=c(0,5), ylim=c(0,5) )
  A &lt;- matrix( c( c(1,2,3,4), c(2,1,2,4)), ncol=2 )
  B &lt;- matrix( c( c(1,2,3,4), c(1,3,3,2)), ncol=2 )
  lines( A, col="red" )
  points( A, col="blue", pch=15 )
  lines( B, col="green" )
  points( B, col="purple", pch=17 )
  
  legend( x="topleft", 
          legend=c("Red line, blue points","Green line, purple points"),
          col=c("red","green"), lwd=1, lty=c(1,2), 
          pch=c(15,17) )
  
  legend( x="bottomleft", 
          legend=c("Red line","blue points","Green line","purple points"),
          col=c("red","blue","green","purple"), lwd=1, lty=c(1,NA,2,NA),
          pch=c(NA,15,NA,17) )
  
  legend( x="left", 
          legend=c("Red line, blue points","Green line, purple points"),
          col=c("red","green"), lwd=1, lty=c(1,2), pch=c(15,17), merge=FALSE )
  
  legend( x="bottomright", 
          legend=c("Red line","blue points","Green line","purple points"), 
          col=c("red","blue","green","purple"), lwd=1, lty=c(1,NA,2,NA), 
          pch=c(NA,15,NA,17), merge=FALSE )
  
  legend( x="topright", 
          legend=c("Red line, blue points","Green line, purple points"), 
          col=c("red","blue","green","purple"), lwd=1, lty=c(1,2), 
          pch=c(15,17), merge=FALSE )
  </code></pre>
  
  <p><a href="https://i.stack.imgur.com/gp3SC.png" rel="noreferrer"><img src="https://i.stack.imgur.com/gp3SC.png" alt="IMG"></a>
  </p>
  
  <p><strong>Solution</strong></p>
  
  <p>I hacked the legend() function to use two different color vectors:</p>
  
  <pre><code>LEGEND &lt;- function (x, y = NULL, legend, fill = NULL, 
      col = par("col"), pt.col=col, line.col=col,
      border = "black", lty, lwd, pch, angle = 45, density = NULL,
      bty = "o", bg = par("bg"), box.lwd = par("lwd"), box.lty = par("lty"),
      box.col = par("fg"), pt.bg = NA, cex = 1, pt.cex = cex, pt.lwd = lwd,
      xjust = 0, yjust = 1, x.intersp = 1, y.intersp = 1, adj = c(0,
          0.5), text.width = NULL, text.col = par("col"), text.font = NULL,
      merge = do.lines &amp;&amp; has.pch, trace = FALSE, plot = TRUE,
      ncol = 1, horiz = FALSE, title = NULL, inset = 0, xpd, title.col = text.col,
      title.adj = 0.5, seg.len = 2)
  {
      if (missing(legend) &amp;&amp; !missing(y) &amp;&amp; (is.character(y) ||
          is.expression(y))) {
          legend &lt;- y
          y &lt;- NULL
      }
      mfill &lt;- !missing(fill) || !missing(density)
      if (!missing(xpd)) {
          op &lt;- par("xpd")
          on.exit(par(xpd = op))
          par(xpd = xpd)
      }
      title &lt;- as.graphicsAnnot(title)
      if (length(title) &gt; 1)
          stop("invalid 'title'")
      legend &lt;- as.graphicsAnnot(legend)
      n.leg &lt;- if (is.call(legend))
          1
      else length(legend)
      if (n.leg == 0)
          stop("'legend' is of length 0")
      auto &lt;- if (is.character(x))
          match.arg(x, c("bottomright", "bottom", "bottomleft",
              "left", "topleft", "top", "topright", "right", "center"))
      else NA
      if (is.na(auto)) {
          xy &lt;- xy.coords(x, y)
          x &lt;- xy$x
          y &lt;- xy$y
          nx &lt;- length(x)
          if (nx &lt; 1 || nx &gt; 2)
              stop("invalid coordinate lengths")
      }
      else nx &lt;- 0
      xlog &lt;- par("xlog")
      ylog &lt;- par("ylog")
      rect2 &lt;- function(left, top, dx, dy, density = NULL, angle,
          ...) {
          r &lt;- left + dx
          if (xlog) {
              left &lt;- 10^left
              r &lt;- 10^r
          }
          b &lt;- top - dy
          if (ylog) {
              top &lt;- 10^top
              b &lt;- 10^b
          }
          rect(left, top, r, b, angle = angle, density = density,
              ...)
      }
      segments2 &lt;- function(x1, y1, dx, dy, ...) {
          x2 &lt;- x1 + dx
          if (xlog) {
              x1 &lt;- 10^x1
              x2 &lt;- 10^x2
          }
          y2 &lt;- y1 + dy
          if (ylog) {
              y1 &lt;- 10^y1
              y2 &lt;- 10^y2
          }
          segments(x1, y1, x2, y2, ...)
      }
      points2 &lt;- function(x, y, ...) {
          if (xlog)
              x &lt;- 10^x
          if (ylog)
              y &lt;- 10^y
          points(x, y, ...)
      }
      text2 &lt;- function(x, y, ...) {
          if (xlog)
              x &lt;- 10^x
          if (ylog)
              y &lt;- 10^y
          text(x, y, ...)
      }
      if (trace)
          catn &lt;- function(...) do.call("cat", c(lapply(list(...),
              formatC), list("\n")))
      cin &lt;- par("cin")
      Cex &lt;- cex * par("cex")
      if (is.null(text.width))
          text.width &lt;- max(abs(strwidth(legend, units = "user",
              cex = cex, font = text.font)))
      else if (!is.numeric(text.width) || text.width &lt; 0)
          stop("'text.width' must be numeric, &gt;= 0")
      xc &lt;- Cex * xinch(cin[1L], warn.log = FALSE)
      yc &lt;- Cex * yinch(cin[2L], warn.log = FALSE)
      if (xc &lt; 0)
          text.width &lt;- -text.width
      xchar &lt;- xc
      xextra &lt;- 0
      yextra &lt;- yc * (y.intersp - 1)
      ymax &lt;- yc * max(1, strheight(legend, units = "user", cex = cex)/yc)
      ychar &lt;- yextra + ymax
      if (trace)
          catn("  xchar=", xchar, "; (yextra,ychar)=", c(yextra,
              ychar))
      if (mfill) {
          xbox &lt;- xc * 0.8
          ybox &lt;- yc * 0.5
          dx.fill &lt;- xbox
      }
      do.lines &lt;- (!missing(lty) &amp;&amp; (is.character(lty) || any(lty &gt;
          0))) || !missing(lwd)
      n.legpercol &lt;- if (horiz) {
          if (ncol != 1)
              warning(gettextf("horizontal specification overrides: Number of columns := %d",
                  n.leg), domain = NA)
          ncol &lt;- n.leg
          1
      }
      else ceiling(n.leg/ncol)
      has.pch &lt;- !missing(pch) &amp;&amp; length(pch) &gt; 0
      if (do.lines) {
          x.off &lt;- if (merge)
              -0.7
          else 0
      }
      else if (merge)
          warning("'merge = TRUE' has no effect when no line segments are drawn")
      if (has.pch) {
          if (is.character(pch) &amp;&amp; !is.na(pch[1L]) &amp;&amp; nchar(pch[1L],
              type = "c") &gt; 1) {
              if (length(pch) &gt; 1)
                  warning("not using pch[2..] since pch[1L] has multiple chars")
              np &lt;- nchar(pch[1L], type = "c")
              pch &lt;- substr(rep.int(pch[1L], np), 1L:np, 1L:np)
          }
          if (!is.character(pch))
              pch &lt;- as.integer(pch)
      }
      if (is.na(auto)) {
          if (xlog)
              x &lt;- log10(x)
          if (ylog)
              y &lt;- log10(y)
      }
      if (nx == 2) {
          x &lt;- sort(x)
          y &lt;- sort(y)
          left &lt;- x[1L]
          top &lt;- y[2L]
          w &lt;- diff(x)
          h &lt;- diff(y)
          w0 &lt;- w/ncol
          x &lt;- mean(x)
          y &lt;- mean(y)
          if (missing(xjust))
              xjust &lt;- 0.5
          if (missing(yjust))
              yjust &lt;- 0.5
      }
      else {
          h &lt;- (n.legpercol + (!is.null(title))) * ychar + yc
          w0 &lt;- text.width + (x.intersp + 1) * xchar
          if (mfill)
              w0 &lt;- w0 + dx.fill
          if (do.lines)
              w0 &lt;- w0 + (seg.len + x.off) * xchar
          w &lt;- ncol * w0 + 0.5 * xchar
          if (!is.null(title) &amp;&amp; (abs(tw &lt;- strwidth(title, units = "user",
              cex = cex) + 0.5 * xchar)) &gt; abs(w)) {
              xextra &lt;- (tw - w)/2
              w &lt;- tw
          }
          if (is.na(auto)) {
              left &lt;- x - xjust * w
              top &lt;- y + (1 - yjust) * h
          }
          else {
              usr &lt;- par("usr")
              inset &lt;- rep_len(inset, 2)
              insetx &lt;- inset[1L] * (usr[2L] - usr[1L])
              left &lt;- switch(auto, bottomright = , topright = ,
                  right = usr[2L] - w - insetx, bottomleft = ,
                  left = , topleft = usr[1L] + insetx, bottom = ,
                  top = , center = (usr[1L] + usr[2L] - w)/2)
              insety &lt;- inset[2L] * (usr[4L] - usr[3L])
              top &lt;- switch(auto, bottomright = , bottom = , bottomleft = usr[3L] +
                  h + insety, topleft = , top = , topright = usr[4L] -
                  insety, left = , right = , center = (usr[3L] +
                  usr[4L] + h)/2)
          }
      }
      if (plot &amp;&amp; bty != "n") {
          if (trace)
              catn("  rect2(", left, ",", top, ", w=", w, ", h=",
                  h, ", ...)", sep = "")
          rect2(left, top, dx = w, dy = h, col = bg, density = NULL,
              lwd = box.lwd, lty = box.lty, border = box.col)
      }
      xt &lt;- left + xchar + xextra + (w0 * rep.int(0:(ncol - 1),
          rep.int(n.legpercol, ncol)))[1L:n.leg]
      yt &lt;- top - 0.5 * yextra - ymax - (rep.int(1L:n.legpercol,
          ncol)[1L:n.leg] - 1 + (!is.null(title))) * ychar
      if (mfill) {
          if (plot) {
              if (!is.null(fill))
                  fill &lt;- rep_len(fill, n.leg)
              rect2(left = xt, top = yt + ybox/2, dx = xbox, dy = ybox,
                  col = fill, density = density, angle = angle,
                  border = border)
          }
          xt &lt;- xt + dx.fill
      }
      if (plot &amp;&amp; (has.pch || do.lines)) {
          pt.COL &lt;- rep_len(pt.col, n.leg)
          line.COL &lt;- rep_len(line.col, n.leg)
      }
      if (missing(lwd) || is.null(lwd))
          lwd &lt;- par("lwd")
      if (do.lines) {
          if (missing(lty) || is.null(lty))
              lty &lt;- 1
          lty &lt;- rep_len(lty, n.leg)
          lwd &lt;- rep_len(lwd, n.leg)
          ok.l &lt;- !is.na(lty) &amp; (is.character(lty) | lty &gt; 0) &amp;
              !is.na(lwd)
          if (trace)
              catn("  segments2(", xt[ok.l] + x.off * xchar, ",",
                  yt[ok.l], ", dx=", seg.len * xchar, ", dy=0, ...)")
          if (plot)
              segments2(xt[ok.l] + x.off * xchar, yt[ok.l], dx = seg.len *
                  xchar, dy = 0, lty = lty[ok.l], lwd = lwd[ok.l],
                  col = line.COL[ok.l])
          xt &lt;- xt + (seg.len + x.off) * xchar
      }
      if (has.pch) {
          pch &lt;- rep_len(pch, n.leg)
          pt.bg &lt;- rep_len(pt.bg, n.leg)
          pt.cex &lt;- rep_len(pt.cex, n.leg)
          pt.lwd &lt;- rep_len(pt.lwd, n.leg)
          ok &lt;- !is.na(pch)
          if (!is.character(pch)) {
              ok &lt;- ok &amp; (pch &gt;= 0 | pch &lt;= -32)
          }
          else {
              ok &lt;- ok &amp; nzchar(pch)
          }
          x1 &lt;- (if (merge &amp;&amp; do.lines)
              xt - (seg.len/2) * xchar
          else xt)[ok]
          y1 &lt;- yt[ok]
          if (trace)
              catn("  points2(", x1, ",", y1, ", pch=", pch[ok],
                  ", ...)")
          if (plot)
              points2(x1, y1, pch = pch[ok], col = pt.COL[ok], cex = pt.cex[ok],
                  bg = pt.bg[ok], lwd = pt.lwd[ok])
      }
      xt &lt;- xt + x.intersp * xchar
      if (plot) {
          if (!is.null(title))
              text2(left + w * title.adj, top - ymax, labels = title,
                  adj = c(title.adj, 0), cex = cex, col = title.col)
          text2(xt, yt, labels = legend, adj = adj, cex = cex,
              col = text.col, font = text.font)
      }
      invisible(list(rect = list(w = w, h = h, left = left, top = top),
          text = list(x = xt, y = yt)))
  }
  </code></pre>
  
  <p>And used as follows:</p>
  
  <pre><code>LEGEND( x="bottomleft", 
          legend=c("Red line, blue points","Green line, purple points"),
          col=c("red","green"), 
          lwd=1, lty=c(1,2), pch=c(15,17) )
  
  LEGEND( x="bottomright", 
          legend=c("Red line, blue points","Green line, purple points"),
          pt.col=c("blue","purple"), line.col=c("red","green"),
          lwd=1, lty=c(1,2), pch=c(15,17) )
  </code></pre>