Как расширить ggplot2 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, Я прочитал виньетка Расширение ggplot2, но не могу понять, как это реализовать. Любая помощь будет оценена.

1 ответ

Решение

Так что думал об этом некоторое время. В основном, когда вы создаете новый примитив, вы обычно пишете комбинацию:

  1. Слой-функция
  2. Stat-ggproto,
  3. Геом-ggproto

Только функция слоя должна быть видимой для пользователя. Вам нужно только написать stat-ggproto, если вам нужен какой-то новый способ преобразования ваших данных, чтобы сделать ваш примитив. И вам нужно написать geom-ggproto, только если у вас есть новая графика для создания сетки.

В этом случае, когда мы в основном компостируем уже существующую функцию слоя, нам не нужно писать новые ggprotos. Достаточно написать новую функцию-слой. Эта функция слоя создаст три слоя, которые вы уже используете, и отобразит параметры так, как вы хотите. В этом случае:

  • Layer1 - использует geom_errorbar а также stat_boxplot - чтобы получить наши ошибки
  • Layer2 - использует geom_boxplot а также stat_boxplot - для создания боксов
  • Layer3 - пользователи geom_label а также stat_summary - создать текстовые метки со средним значением в центре полей.

Конечно, вы могли бы написать новый stat-ggproto и новый geom-ggproto, которые делают все эти вещи одновременно. Или, может быть, вы компост stat_summary а также stat_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 )

И они выглядят так:

Примечание: нам на самом деле не нужно было использовать layer функция, мы могли бы использовать оригинал stat_boxplot, geom_boxplot, а также stat_summary звонки на их место. Но нам все равно пришлось бы заполнить все параметры, если бы мы хотели иметь возможность управлять ими из нашего собственного коробочного графика, поэтому я думаю, что это было бы более понятным - по крайней мере, с точки зрения структуры, а не функциональности., Может быть, это не так, это вопрос вкуса...

Кроме того, у меня нет того шрифта, который выглядит намного лучше. Но мне не хотелось выслеживать и устанавливать его.

Другие вопросы по тегам