如何使用ggproto扩展ggplot2 boxplot?

时间:2016-01-19 12:20:03

标签: r ggplot2 fonts boxplot ggproto

我经常在工作中使用箱形图,并且喜欢ggplot2美学。但标准geom_boxplot缺少两件对我很重要的事情:胡须和中间标签的末端。感谢来自这里的信息,我写了一个函数:

gBoxplot <- function(formula = NULL, data = NULL, font = "CMU Serif", fsize = 18){
  require(ggplot2)
  vars <- all.vars(formula)
  response <- vars[1]
  factor <- vars[2]
  # A function for medians labelling
  fun_med <- function(x){
    return(data.frame(y = median(x), label = round(median(x), 3)))
  }
  p <- ggplot(data, aes_string(x = factor, y = response)) +
  stat_boxplot(geom = "errorbar", width = 0.6) +
  geom_boxplot() +
  stat_summary(fun.data = fun_med, geom = "label", family = font, size = fsize/3, 
                                                                         vjust = -0.1) +
  theme_grey(base_size = fsize, base_family = font)
  return(p)
}

还有字体设置,但这只是因为我懒得制作主题。这是一个例子:

gBoxplot(hwy ~ class, mpg)

plot1

对我来说足够好,但是有一些限制(不能使用自动躲避等),最好根据geom_boxplot制作一个新的geom。我已经阅读了小插图Extending ggplot2,但无法理解如何实现它。 任何帮助将不胜感激。

1 个答案:

答案 0 :(得分:9)

所以一直想着这个。基本上,当您创建新基元时,通常会编写以下组合:

  1. 图层功能
  2. stat-ggproto
  3. geom-ggproto
  4. 用户只能看到图层功能。如果您需要一些转换数据的新方法来制作基元,则只需要编写 stat-ggproto 。如果您要创建一些基于网格的新图形,则只需要编写 geom-ggproto

    在这种情况下,我们基本上堆肥已经存在的图层功能,我们真的不需要编写新的ggprotos。编写一个新的图层函数就足够了。此图层功能将创建您已使用的三个图层,并按照您的方式映射参数。在这种情况下:

    • 第1层 - 使用geom_errorbarstat_boxplot - 来获取错误栏
    • 第2层 - 使用geom_boxplotstat_boxplot - 来创建箱图
    • 第3层 - 用户geom_labelstat_summary - 创建文本标签,其中包含方框中心的平均值。

    当然,您可以编写一个新的 stat-ggproto 和一个新的 geom-ggproto ,它们可以同时执行所有这些操作。或者也许您将stat_summarystat_boxplot组合成一个,并将三个 geom-protos 组合在一起,这样就可以用一层来完成。但除非我们有效率问题,否则没有什么意义。

    无论如何,这是代码:

    geom_myboxplot <- function(formula = NULL, data = NULL,
                               stat = "boxplot", position = "dodge",coef=1.5,
                               font = "sans", fsize = 18, width=0.6,
                               fun.data = NULL, fun.y = NULL, fun.ymax = NULL,
                               fun.ymin = NULL, fun.args = list(),
                               outlier.colour = NULL, outlier.color = NULL,
                               outlier.shape = 19, outlier.size = 1.5,outlier.stroke = 0.5,
                               notch = FALSE,  notchwidth = 0.5,varwidth = FALSE,
                               na.rm = FALSE, show.legend = NA,
                               inherit.aes = TRUE,...) {
        vars <- all.vars(formula)
        response <- vars[1]
        factor <- vars[2]
        mymap <- aes_string(x=factor,y=response)
        fun_med <- function(x) {
            return(data.frame(y = median(x), label = round(median(x), 3)))
        }
        position <- position_dodge(width)
        l1 <- layer(data = data, mapping = mymap, stat = StatBoxplot,
                geom = "errorbar", position = position, show.legend = show.legend,
                inherit.aes = inherit.aes, params = list(na.rm = na.rm,
                    coef = coef, width = width, ...))
        l2 <- layer(data = data, mapping = mymap, stat = stat, geom = GeomBoxplot,
                position = position, show.legend = show.legend, inherit.aes = inherit.aes,
                params = list(outlier.colour = outlier.colour, outlier.shape = outlier.shape,
                    outlier.size = outlier.size, outlier.stroke = outlier.stroke,
                    notch = notch, notchwidth = notchwidth, varwidth = varwidth,
                    na.rm = na.rm, ...))
        l3 <- layer(data = data, mapping = mymap, stat = StatSummary,
                geom = "label", position = position, show.legend = show.legend,
                inherit.aes = inherit.aes, params = list(fun.data = fun_med,
                    fun.y = fun.y, fun.ymax = fun.ymax, fun.ymin = fun.ymin,
                    fun.args = fun.args, na.rm=na.rm,family=font,size=fsize/3,vjust=-0.1,...))
        return(list(l1,l2,l3))
    }
    

    允许您创建自定义的箱图,如下所示:

    ggplot(mpg) +
      geom_myboxplot( hwy ~ class, font = "sans",fsize = 18)+
      theme_grey(base_family = "sans",base_size = 18 )
    

    它们看起来像这样:

    enter image description here

    注意:我们实际上不必使用layer函数,我们可以使用原始stat_boxplotgeom_boxplot和{{1}在他们的地方打电话。但是如果我们想要从我们的自定义箱图中控制它们,我们仍然必须填写所有参数,所以我认为这样更清晰 - 至少从结构的角度而不是功能性的角度来看。也许不是,这是一个品味问题...

    此外,我没有那种看起来好多了的字体。但我不想跟踪并安装它。