Combining scale_*_manual in a function

Hi

I would like to create a single function that would combine multiple calls to scale_*_manual function. The simple reprex below illustrates in a simplified way what I am trying and failing to do.

Thanks in advance for your help

library(ggplot2)

gdata <- data.frame(x = 1:10, y = 1:10, g = factor(1:10))

my_scale <- function(){
  scale_colour_manual(values=1:10) +
    scale_shape_manual(values=1:10)
}

# Doesn't work!
ggplot(data = gdata) +
  aes(x = x, y = y, colour = g, shape = g) +
  geom_point() +
  my_scale()

# Works !
# ggplot(data = gdata) +
#   aes(x = x, y = y, colour = g, shape = g) +
#   geom_point() +
#   scale_colour_manual(values=1:10) +
#   scale_shape_manual(values=1:10)

I do not know why my_scale doesn't work the way you defined it. But I've found in the past that if you past layers as a list, it works. Like this:

my_scale_mod <- function() {
    list(scale_colour_manual(values=1:10) ,
         scale_shape_manual(values=1:10))
}
1 Like

The error message from my_scale() gives a jargon-y reason:

Error: Cannot add ggproto objects together. Did you forget to add this object to a ggplot object?

In short, you can only add a scale object (created with scale_...) to a ggplot object. The my_scale function tries to add the two scales, and neither are ggplot objects, so it fails.

So why does the following code work (I ask rhetorically):

ggplot(data = gdata) +
  aes(x = x, y = y, colour = g, shape = g) +
  geom_point() +
  scale_colour_manual(values=1:10) +
  scale_shape_manual(values=1:10)

It's because each step adds to the ggplot object, so the next component is added to the result of the previous addition. If we could see each combination in sequence...

  1. The ggplot object is created with ggplot(data = gdata).
  2. The aesthetics from aes(...) are added to the ggplot object, and a ggplot object is returned for the next step.
  3. The geometry is added, and a ggplot object is returned for the next step.
  4. A color scale is added, and a ggplot object is returned for the next step.
  5. A shape scale is added, and a ggplot object returned for R to print.

This happens because the ggplot2 does "operator overloading," where a common operator is given a new ability. You can see it with ggplot2:::"+.gg":

function (e1, e2) 
{
    if (missing(e2)) {
        stop("Cannot use `+.gg()` with a single argument. ", 
            "Did you accidentally put + on a new line?", 
            call. = FALSE)
    }
    e2name <- deparse(substitute(e2))
    if (is.theme(e1)) 
        add_theme(e1, e2, e2name)
    else if (is.ggplot(e1)) 
        add_ggplot(e1, e2, e2name)
    else if (is.ggproto(e1)) {
        stop("Cannot add ggproto objects together.", " Did you forget to add this object to a ggplot object?", 
            call. = FALSE)
    }
}
<bytecode: 0x000002ccb37ce248>
<environment: namespace:ggplot2>

This is a shallow glimpse into the reasoning, because the ggplot2 package is complex. It has a plethora of classes, operator overloading, its own object system, and tons of non-standard evaluation. And I'm sure I've shown my ignorance at least three times in my explanation above.

2 Likes

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.

If you have a query related to it or one of the replies, start a new topic and refer back with a link.