[R] Placement of legend in base plot()

Spencer Graves @pencer@gr@ve@ @end|ng |rom e||ect|vede|en@e@org
Tue May 31 15:17:24 CEST 2022



On 5/31/22 8:07 AM, Helmut Schütz wrote:
> Dear all,
> 
> I try to figure out where to automatically place the legend in a scatter 
> plot.
> If there is large variability, points may cover the legend. Hence, I 
> assess in which section the fewest points are.
> Example:
> 
> set.seed(27) # for reproducibility
> n      <- 25
> slope  <- +1
> sd     <- 10
> x      <- 1:n
> mean.x <- mean(x)
> y      <- slope * x + rnorm(n = n, mean = mean.x, sd = sd)
> mean.y <- mean(y)
> top    <- which(y >= mean.y)
> bottom <- which(y < mean.y)
> left   <- which(x <= mean.x)
> right  <- which(x > mean.x)
> n.pts  <- data.frame("topleft"     = sum(top %in% left),
>                       "topright"    = sum(top %in% right),
>                       "bottomleft"  = sum(bottom %in% left),
>                       "bottomright" = sum(bottom %in% right))
> loc    <- names(n.pts)[n.pts == min(n.pts)]
> if (length(loc) > 1) loc <- loc[1] # arbitrary selection (better 
> approaches?)
> bg     <- "transparent"
> lgd    <- paste("Pretty long legend line number #", 1:3)
> plot(x, y, type ="n", pch = 19, xlab = "", ylab = "", axes = FALSE, 
> frame.plot = TRUE)
> abline(h = mean.y, v = mean.x)
> mtext(text = paste0("top left: n = ", n.pts[1], ", right: n = ", n.pts[2]),
>        side = 3, line = 1)
> mtext(text = paste0("bottom left: n = ", n.pts[3], ", right: n = ", 
> n.pts[4]),
>        side = 1, line = 1)
> mtext(text = paste0("bottom: n = ", sum(n.pts[3:4]),
>                      ", top = ", sum(n.pts[1:2])), side = 2, line = 1)
> points(x, y, pch = 19, col = "red", cex = 1.25)
> print(n.pts); loc
> if (loc == "topleft")     legend("topleft", legend = lgd, x.intersp = 0,
>                                   title = paste("n =", n.pts[1]), bg = bg)
> if (loc == "topright")    legend("topright", legend = lgd, x.intersp = 0,
>                                   title = paste("n =", n.pts[2]), bg = bg)
> if (loc == "bottomleft")  legend("bottomleft", legend = lgd, x.intersp = 0,
>                                   title = paste("n =", n.pts[3]), bg = bg)
> if (loc == "bottomright") legend("bottomright", legend = lgd, x.intersp 
> = 0,
>                                   title = paste("n =", n.pts[4]), bg = bg)
> 
> Unfortunately, one of the keywords in legend() instead of x, y cannot be 
> a variable.
> Hence, legend(loc, ...) throws an error...
> Error in match.arg(x, c("bottomright", "bottom", "bottomleft", "left",  :
> 'arg' must be of length 1
> ... and I had to resort to conditionally specify all 4.
> Given.
> 
> Problems:
> 1. If there are the same number of points in sections, I select the 
> first though another might lead to fewer overlapping points. Is there a 
> better approach?
> 2. I know how to get the width/height of the legend box with (..., plot 
> = FALSE) but couldn't figure out how to squeeze it between points where 
> enough space might exist.


"bty='n'" will suppress the background, so you can still see the points 
that would otherwise be covered up by the legend box.


That does NOT answer your question, but it does make it less critical.


Hope this helps, Spencer

> 
> Best,
> Helmut



More information about the R-help mailing list