3

Say I create a plot:

df <- data.frame(A = 1:100, B = jitter(1:100, 25), C = c('One', 'Two'))

p <- ggplot(df, aes(A, B, colour = C)) + 
  geom_point() + 
  scale_colour_manual(values = c('red', 'green'))

Where are those colours (red and green) stored in p?


I can see the palette of functions being used in a function here:
p$scales$scales[[1]]$palette

The contents of this function are:

function (n) 
{
  if (n > length(values)) {
    stop("Insufficient values in manual scale. ", n, " needed but only ", 
      length(values), " provided.", call. = FALSE)
  }
  values
}

I think the colours must be stored in values there but I have no idea where they actually are in p.


P.s. I've seen this question: How to extract the fill colours from a ggplot object?. But for what I'm trying to do I can't build the plot. I need to get at the colours before it's built.

If there was some way of recursively searching p for the characters "red" or "green" that would probably help find these values.


EDIT: What I'm ultimately trying to do.

I'm trying to edit plots before they are plotted. The idea being that given some plot p you can just do something like this:

apply_theme(p) 

...and a colour scheme is applied to the entire plot (including scales, gradients etc.). This is to avoid having to do things like:

p + some_theme + scales_colour_manual(values = plot_theme) 

I'm trying to reduce effort by the user so that they can just apply a theme to an entire plot and not have to worry about whether they are colouring a gradient, discrete scale or whatever.

Building the plot is a partial solution. But I would like to able to apply the theme and still be able to edit the plot later.

I've been able to edit p so that whatever colours are applied to geoms retrospectively. But I just can't find how to do that with scale colours. I know the colours must be in there somewhere!

Community
  • 1
  • 1
Ciarán Tobin
  • 7,306
  • 1
  • 29
  • 45

2 Answers2

3

Not much progress, but I've ruled out a few places that it might be hiding.

You can see all the character vector that p contains with

Filter(is.character, unlist(p))
# $labels.x
# [1] "A"

# $labels.y
# [1] "B"

# $labels.colour
# [1] "C"

So it isn't stored directly.

By setting options(error = recover) and then forcing an error by only providing one colour, we can inspect the call stack.

(p <- ggplot(df, aes(A, B, colour = C)) + 
  geom_point() + 
  scale_colour_manual(values = 'red')
)

 1: print(list(data = list(A = 1:100, B = c(4.32692646654323, 3.46481398493052, 0.4989527114667, 
...
13: scale$palette(n)

Entering frame 13: scale$palette(n), we can see that the variable values does indeed store the colour information.

Browse[1]> values
[1] "red"

We can hunt through each frame in the call stack, but something odd is happening - it doesn't appear to be anywhere.

Browse[1]> sapply(
  sys.frames(), 
  function(envir) 
  {
    exists("values", envir, inherits = FALSE)
  }
)
[1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[16] FALSE FALSE

(See sys.status() for the full details of the call stack.)

I suspect that some odd evaluation technique is being used with the Scales reference class.

Richie Cotton
  • 118,240
  • 47
  • 247
  • 360
  • +1 thanks for the effort. Part of the difficulty comes from the amount of nesting going on in `p`. E.g. `Filter(is.character, unlist(p))` only returns three characters... but I know for a fact there are more (Try: `Filter(is.character, unlist(p$scales$scales))` just as an example). `values` might not exist in `p`... the colours may be under some other name. I probably need some method to completely `unlist()` `p`. – Ciarán Tobin Jan 08 '13 at 17:19
0
g <- ggplot_build(p)
data.frame(colours = unique(g$data[[1]]["colour"]),
             label = g$plot$scales$scales[[1]]$get_labels())

Your will see the color - legend data.frame here:

enter image description here

jrcalabrese
  • 2,184
  • 3
  • 10
  • 30
Y. Sun
  • 1
  • 1