Dear Helmut,
I'm not sure why you're seeing an error, but replacing the last several
commands in your code with
legend(loc, legend = lgd, x.intersp = 0, title = paste("n =",
n.pts[1]), bg = bg)
works perfectly fine for me. I suspect that the example you posted
differs in some respect from the code that produces the error.
I hope this helps,
John
On 2022-05-31 9:07 a.m., 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.
>
> Best,
> Helmut
--
John Fox, Professor Emeritus
McMaster University
Hamilton, Ontario, Canada
web: https://socialsciences.mcmaster.ca/jfox/