Расширение ggplot собственными гемами: адаптируйте масштаб по умолчанию - PullRequest
3 голосов
/ 01 апреля 2019

Фон

После прочтения этого прекрасного ответа о том, как расширить ggplot и соответствующей виньетки Я пытался понять, какрастянуть ggplot.

В двух словах

Я понимаю, как соединяются части, но мне не хватает важной информации: как определяет ggplotдиапазон по умолчанию для оси?

Код

Рассмотрим следующий пример игрушки:

library(grid)
library(ggplot2)

GeomFit <- ggproto("GeomFit", GeomBar,
                   required_aes = c("x", "y"),
                   setup_data = .subset2(GeomBar, "setup_data"),
                   draw_panel = function(self, data, panel_scales, coord, width = NULL) {
                     bars <- ggproto_parent(GeomBar, self)$draw_panel(data,
                                                                      panel_scales, 
                                                                      coord)
                     coords <- coord$transform(data, panel_scales)    
                     tg <- textGrob("test", coords$x, coords$y * 2 - coords$ymin)
                     grobTree(bars, tg)
                   }
)

geom_fit <- function(mapping = NULL, data = NULL,
                     stat = "count", position = "stack",
                     ...,
                     width = NULL,
                     binwidth = NULL,
                     na.rm = FALSE,
                     show.legend = NA,
                     inherit.aes = TRUE) {

  layer(
    data = data,
    mapping = mapping,
    stat = stat,
    geom = GeomFit,
    position = position,
    show.legend = show.legend,
    inherit.aes = inherit.aes,
    params = list(
      width = width,
      na.rm = na.rm,
      ...
    )
  )
}

set.seed(1234567)
data_gd <- data.frame(x = letters[1:5], 
                      y = 1:5)

p <- ggplot(data = data_gd, aes(x = x, y = y, fill = x)) + 
  geom_fit(stat = "identity")

Который создает этот график: Barplot with text

Задача

Как видите, часть текста не отображается.Я предполагаю, что ggplot каким-то образом вычисляет диапазоны для оси, и поскольку он не знает о дополнительном пространстве, необходимом для моего textGrob.Как я могу решить это?(Желаемый результат эквивалентен p + expand_limits(y = 10)

Примечание. Конечно, я мог бы довести проблему до конечного пользователя, потребовав добавить ручную шкалу. Но в идеале я хотел бы, чтобы шкалыбыть правильно настроенным.

1 Ответ

2 голосов
/ 02 апреля 2019

Эврика

Нашел ужасный взлом благодаря потрясающей помощи @ Z.Lin о том, как отлаживать ggplot код.Вот как я придумал этот довольно уродливый хак для дальнейшего использования:

Как я туда попал

Во время отладки debug(ggplot2:::ggplot_build.ggplot) я узнал, что виновника можно найти где-нибудьв FacetNull$train_scales (на моем графике без граней другие грани, такие как FacetGrid, работают аналогично).Это я узнал через debug(environment(FacetNull$train_scales)$f), что, в свою очередь, я узнал в ответе З.Лина в другом потоке .

Как только я смог отладить ggproto объекты, я мог видеть, чтов этой самой функции весы обучаются.В основном, функция смотрит на эстетику, которая имеет отношение к конкретному масштабу (не знаю, где эта информация изначально задана, есть идеи?) И смотрит, какая из этих эстетик присутствует в данных слоя.

Я видел, что поле ymax_final (которое - согласно этой таблице - используется только для stat_boxplot) относится к числу тех, которые рассматриваются для настройки весов.С помощью этой информации было легко найти некрасивый взлом, установив для этого поля соответствующее значение в setup_data.

Код

GeomFit <- ggproto("GeomFit", GeomBar,
                   required_aes = c("x", "y"),
                   setup_data = function(self, data, params) {
                      data <- ggproto_parent(GeomBar, self)$setup_data(data, params)
                      ## here's the hack: add a field which is not needed in this geom, 
                      ## but which is used by Facet*$train_scales which 
                      ## eventually sets up the scales
                      data$ymax_final <- 2 * data$y
                      data
                   }, 
                   draw_panel = function(self, data, panel_scales, coord, width = NULL) {
                     bars <- ggproto_parent(GeomBar, self)$draw_panel(data,
                                                                      panel_scales, 
                                                                      coord)
                     coords <- coord$transform(data, panel_scales)    
                     tg <- textGrob("test", coords$x, coords$y * 2 - coords$ymin)
                     grobTree(bars, tg)
                   }
)

Результат

Barplot with labels

Открытый вопрос

Где установлены шкалы (оси), еслиВы не определяете их вручную?Полагаю, там установлены поля, которые имеют отношение к масштабированию.

...