recreate scale_fill_brewer with scale_fill_manual

PatrickT

I am trying to understand the connection between scale_fill_brewer and scale_fill_manual of package ggplot2.

First, generate a ggplot with filled colors:

library(ggplot2)
p <- ggplot(data = mtcars, aes(x = mpg, y = wt, 
    group = cyl, fill = factor(cyl))) + 
    geom_area(position = 'stack')

# apply ready-made palette with scale_fill_brewer from ggplot2
p + scale_fill_brewer(palette = "Blues")

enter image description here

Now, replicate with scale_fill_manual

library(RColorBrewer)
p + scale_fill_manual(values = brewer.pal(3, "Blues"))  

where 3 is the number of fill-colors in the data. For convenience, I have used the brewer.pal function of package RColorBrewer.

As far as I understand, the convenience of scale_fill_brewer is that it automatically computes the number of unique levels in the data (3 in this example). Here is my attempt at replicating:

p + scale_fill_manual(values = brewer.pal(length(levels(factor(mtcars$cyl))), "Blues"))

My question is: how does scale_fill_brewer compute the number of levels in the data?

I'm interested in understanding what else fill_color_brewer might be doing under the hood. Might I run into any difficulty if I replace the more user friendly fill_color_brewer with a more contorted implementation of scale_fill_manual like the one above.

Perusing the source code:

scale_fill_brewer
function (..., type = "seq", palette = 1) {
    discrete_scale("fill", "brewer", brewer_pal(type, palette), ...)
}

I couldn't see through this how scale_fill_brewer computes the number of unique levels in the data. Perhaps hidden in the ... ?

Edit: Where does the function scale_fill_brewer receive instructions to compute the number of levels in the data? Is it in "seq" or in ... or elsewhere?

The discrete_scale function is intricate and I'm lost. Here are its arguments:

discrete_scale <- function(aesthetics, scale_name, palette, name = NULL, 
    breaks = waiver(), labels = waiver(), legend = NULL, limits = NULL, 
    expand = waiver(), na.value = NA, drop = TRUE, guide="legend") {

Does any of this compute the number of levels?

Miguel Manese

The easiest way is to trace it is to think in terms of (1) setting up the plot data structure, and (2) resolving the aesthetics. It uses S3 so the branching is implicit

The setup call sequence

  1. [scale-brewer.R] scale_fill_brewer(type="seq", palette="Blues")

  2. [scale-.R] discrete_scale(...) - return an object representing the scale

  structure(list(
    call = match.call(),

    aesthetics = aesthetics,
    scale_name = scale_name,
    palette = palette,

    range = DiscreteRange$new(),        ## this is scales::DiscreteRange 
    ...), , class = c(scale_name, "discrete", "scale"))

The resolve call sequence

  1. [plot-build.R] ggplot_build(plot) - for non-position scales, apply scales_train_df
    # Train and map non-position scales
    npscales <- scales$non_position_scales()       ## scales is plot$scales, S4 type Scales
    if (npscales$n() > 0) {
      lapply(data, scales_train_df, scales = npscales)
      data <- lapply(data, scales_map_df, scales = npscales)
    }
  1. [scales-.r] scales_train_df(...) - iterate again over scales$scales (list)

  2. [scale-.r] scale_train_df(...) - iterate again

  3. [scale-.r] scale_train(...) - S3 generic function

  4. [scale-.r] scale_train.discrete(...) - almost there...

    scale$range$train(x, drop = scale$drop)
  1. but scale$range is a DiscreteRange instance, so it calls (scales::DiscreteRange$new())$train, which overwrites scale$range!
    range <<- train_discrete(x, range, drop)
  1. scales:::train_discrete(...) - again, almost there...

  2. scales:::discrete_range(...) - still not there..

  3. scales:::clevels(...) - there it is!

As of this point, scale$range has been overwritten by the levels of the factor. Unwinding the call stack to #1, we now call scales_map_df

  1. [plot-build.R] ggplot_build(plot) - for non-position scales, apply scales_train_df
    # Train and map non-position scales
    npscales <- scales$non_position_scales()       ## scales is plot$scales, S4 type Scales
    if (npscales$n() > 0) {
      lapply(data, scales_train_df, scales = npscales)
      data <- lapply(data, scales_map_df, scales = npscales)
    }
  1. [scales-.r] scale_maps_df(...) - iterate

  2. [scale-.r] scale_map_df(...) - iterate

  3. [scale-.r] scale_map.discrete - fill up the palette (non-position scale!)

    scale_map.discrete <- function(scale, x, limits = scale_limits(scale)) { n <- sum(!is.na(limits)) pal <- scale$palette(n) ... }

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

How to set scale_fill_brewer and scale_fill_discrete at the same ggplot bar chart?

ggplot scale_fill_manual within groups

scale_fill_manual() will not change colors on legend?

scale_fill_manual with contradicting results

R scale_fill_manual how to indicate three colors

How to change bars colors with scale_fill_manual() in R?

scale_fill_manual factor levels, colours, or order ignored

scale_fill_manual is only applying to the legend and not bars

ggplot `scale_fill_manual()` alternate colors infinitely

How to create custom color palette to be used by scale_fill_manual()

Using Multiple scale_fill_manual in ggplot2

scale_fill_manual define color for NA values

ggplot not respectig order of colours in scale_fill_manual()?

geom_point isn't filled by scale_fill_manual

scale_fill_manual based on another factor in ggplot2

scale_fill_manual not respecting to the order of values and labels

Color and order of coefficients within scale_fill_manual not consistent

ggplot2: scale_fill_manual with symbol and shape

Problem with color changing in R using scale_fill_manual

scale_fill_discrete and scale_fill_manual - legend options confusion

Display color in ggplot 2 with scale_fill_manual and scale_fill_discrete in stacked bar graph

Conflict between scale_fill_manual and scale_x_discrete in a bar plot

Modifying the colors with commands scale_fill_manual() and scal_fill_discrete() on a ggplot2 histogram

Manually colouring plots with `scale_fill_manual` in ggplot2 not working

The ggplotly() ignores legend labels editing despite using scale_fill_manual()

how to change default color for the value not defined when we use scale_fill_manual

R ggplot scale_fill_manual using colors for values within a range

Manually assigning colors with scale_fill_manual only works for certain hexagon sizes

When using scale_fill_manual() in a loop, only the colour values assigned in the last iteration are retained