1
votes

Suppose that a R package defines S3 classes representing data for scatter points and lines for fits. It would be sensible to write the autoplot and autolayer methods for both points and lines. Then we could build a plot with autoplot and add stuff with a sequence of calls to autolayer, possibly specifying new colour, fill, linetype at each call.

library(ggplot2)
autolayer.myLines <- function(object, ...) {
    geom_line(data = object,
              mapping = aes(x = x, y = y, colour = .group, linetype = .group))
}
autoplot.myLines <- function(object, ...) {
    ggplot() + autolayer(object, ...)   
}
autolayer.myPoints  <- function(object, ...) {
    geom_point(data = object,
               mapping = aes(x = x, y = y, colour = .group, shape = .group))
}
autoplot.myPoints  <- function(object, ...) {
    ggplot() + autolayer(object, ...)
}

set.seed(123)
xP <- runif(10)
## objects with class "myPoints"
myP1 <- data.frame(x = xP, y = xP + rnorm(10, sd = 0.1), .group = "points 1")
myP2 <- data.frame(x = xP, y = xP * xP + rnorm(10, sd = 0.1), .group = "points 2")
class(myP1) <- class(myP2) <- c("myPoints", "data.frame")

## objects with class "myLines"
xL <- seq(from = 0, to = 1, length.out = 80)
myL1 <- data.frame(x = xL, y = xL, .group = "line 1")
myL2 <- data.frame(x = xL, y = xL * xL, .group = "line 2")
class(myL1) <- class(myL2) <- c("myLines", "data.frame")

autoplot(myP1) + autolayer(myL1) + autolayer(myP2) + autolayer(myL2)  +
    scale_colour_manual(values = c("orangered", "green", "magenta", "SteelBlue"))

With R code

The nice point is that legends are updated when some new material is added. However the legend created by the common colour keyword is quite confusing. It would make sense to have only two legends, one for the points and one for the lines each with the elements of the aesthetics in geom_point and geom_line. So shapes would be shown for the "points" legend, but not for the "lines" one, as if the legends arose from two ggplots using two data frames: one for the points and one for the lines (see image below). So the question is: what is the best way to get the two legends, making sure they are updated when more autolayer calls are added?

Wanted legend

More generally some keywords entering in the definition of aesthetics via aes such as fill have a quite different meaning across geometries and it seems strange to gather these in a common legend as is the case e.g. if geom_ribbon and geom_point are used in the same plot both with aesthetics including fill. Is there a simple solution to avoid this default behaviour and generate legends from aesthetics within geoms?

1

1 Answers

3
votes

library(ggnewscale) helps here:

library(ggnewscale)
autoplot(myP1) + 
    autolayer(myP2) +
    scale_colour_manual(values = c("magenta", "SteelBlue")) +
    new_scale_color() +
    autolayer(myL1) + 
    autolayer(myL2)  +
    scale_colour_manual(values = c("black", "red"))

Two Color Scales