用 scale_fill_manual 重新创建 scale_fill_brewer

recreate scale_fill_brewer with scale_fill_manual

我想了解包 ggplot2scale_fill_brewerscale_fill_manual 之间的联系。

首先,生成一个填充颜色的 ggplot:

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")

现在,用scale_fill_manual

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

其中 3 是数据中 fill-colors 的数量。为了方便起见,我使用了 RColorBrewer.

包的 brewer.pal 函数

据我了解,scale_fill_brewer 的便利之处在于它会自动计算数据中唯一级别的数量(本例中为 3)。这是我的复制尝试:

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

我的问题是:scale_fill_brewer如何计算数据中的水平数?

我有兴趣了解 fill_color_brewer 的幕后工作。如果我将更用户友好的 fill_color_brewer 替换为像上面那样更扭曲的 scale_fill_manual 实现,我 运行 可能会遇到任何困难。

阅读源代码:

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

我无法看穿 scale_fill_brewer 如何计算数据中唯一级别的数量。也许隐藏在 ... ?

编辑:函数 scale_fill_brewer 在哪里接收指令来计算数据中的级别数?它是在 "seq" 还是在 ... 或其他地方?

discrete_scale 函数错综复杂,我迷路了。这是它的参数:

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") {

这是否计算了级别数?

追踪它的最简单方法是从 (1) 设置情节数据结构和 (2) 解决 美学的角度思考。它使用 S3,所以分支是隐式的

设置调用序列

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

  2. [scale-.R] discrete_scale(...) - return 表示比例的对象

  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"))

解析调用顺序

  1. [plot-build.R] ggplot_build(plot) - 对于非位置尺度,应用 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(...) - 再次遍历 scales$scales (list)

  2. [scale-.r] scale_train_df(...) - 再次迭代

  3. [scale-.r] scale_train(...) - S3 通用函数

  4. [scale-.r] scale_train.discrete(...) - 差不多了...

    scale$range$train(x, drop = scale$drop)
  1. 但是 scale$range 是一个 DiscreteRange 实例,所以它调用 (scales::DiscreteRange$new())$train,它会覆盖 scale$range!
    range <<- train_discrete(x, range, drop)
  1. scales:::train_discrete(...) - 又一次,差不多了...

  2. scales:::discrete_range(...) - 仍然没有..

  3. scales:::clevels(...) - 就是这样!

至此,scale$range 已被因子水平覆盖。将调用堆栈展开到 #1,我们现在调用 scales_map_df

  1. [plot-build.R] ggplot_build(plot) - 对于非位置尺度,应用 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(...) - 迭代

  2. [scale-.r] scale_map_df(...) - 迭代

  3. [scale-.r] scale_map.discrete - 填满调色板(非位置比例!)

    scale_map.discrete <- 函数(比例,x,限制 = scale_limits(比例)){ n <-总和(!is.na(限制)) pal <- scale$调色板(n) ... }