Расширение ggplot2: как построить геометрию и статистику?

Я нахожусь на ранних этапах изучения того, как расширять ggplot2. Я хочу создать собственный geom и связанный stat. Моей отправной точкой была виньетка. Кроме того, мне помогли это и это. Я пытаюсь составить шаблон, чтобы научить себя и, надеюсь, других.

Главный вопрос:

Внутри моей функции calculate_shadows() необходимый параметр params$anchor равен NULL. Как я могу получить к нему доступ?

Описанная ниже цель предназначена исключительно для изучения того, как создавать собственные stat и geom функции, это не настоящая цель: как вы можете видеть на снимках экрана, я знаю, как использовать возможности ggplot2 для построения графиков.

  1. geom будет считывать данные, а для предоставленных переменных ("x", "y") построит график (за отсутствием лучшего слова) shadows: горизонтальная линия min(x)--max(x) по умолчанию y=0 и вертикальная линия min(y)--max(y) по умолчанию x=0. Если имеется опция, эти «якоря» можно изменить, например если пользователь предоставляет x = 35, y = 1, горизонтальная линия будет нарисована в точке пересечения y = 1, а вертикальная линия будет нарисована в точке пересечения x = 35. Использование:

    library(ggplot2)
    ggplot(data = mtcars, aes(x = mpg, y = wt)) + 
        geom_point() +
        geom_shadows(x = 35, y = 1) 
    

введите описание изображения здесь

  1. stat прочитает данные и для предоставленных переменных ("x", "y") вычислит shadows в соответствии со значением stat. Например, при передаче stat = "identity" тени будут вычисляться для минимума и максимума данных (как это делает geom_shadows). Но при передаче stat = "quartile" тени будут вычисляться для первого и третьего квартилей. В более общем плане можно передать функцию типа stats::quantile с аргументами args = list(probs = c(0.10, 0.90), type = 6) для вычисления теней с использованием 10-го и 90-го процентилей и метода квантилей типа 6. Использование:

    ggplot(data = mtcars, aes(x = mpg, y = wt)) + 
        geom_point() +
        stat_shadows(stat = "quartile") 
    

введите описание изображения здесь

К сожалению, недостаточное знакомство с расширением ggplot2 помешало мне достичь моей цели. Эти сюжеты были «сфальсифицированы» с помощью geom_segment. Основываясь на руководстве и обсуждениях, упомянутых выше, и изучая существующий код, например stat-qq или stat-smooth, я собрал базовую архитектуру для этой цели. Там должно быть много ошибок, буду благодарен за руководство. Также обратите внимание, что подойдет любой из этих подходов: geom_shadows(anchor = c(35, 1)) или geom_shadows(x = 35, y = 1).

Теперь вот мои старания. Во-первых, geom-shadows.r, чтобы определить geom_shadows(). Во-вторых, stat-shadows.r, чтобы определить stat_shadows(). Код не работает как есть. Но если я выполню его контент, он выдаст желаемую статистику. Для ясности я удалил большинство вычислений в stat_shadows(), таких как квартили, чтобы сосредоточиться на самом важном. Есть очевидная ошибка в макете?

geom-shadows.r

#' documentation ought to be here
geom_shadows <- function(
  mapping = NULL, 
  data = NULL, 
  stat = "shadows", 
  position = "identity", 
  ...,
  anchor = list(x = 0, y = 0),
  shadows = list("x", "y"), 
  type = NULL,
  na.rm = FALSE,
  show.legend = NA, 
  inherit.aes = TRUE) {
    layer(
      data = data,
      mapping = mapping,
      stat = stat,
      geom = GeomShadows,
      position = position,
      show.legend = show.legend,
      inherit.aes = inherit.aes,
      params = list(
        anchor = anchor,
        shadows = shadows,
        type = type,  
        na.rm = na.rm,
        ...
    )
  )
}

GeomShadows <- ggproto("GeomShadows", Geom, 

  # set up the data, e.g. remove missing data
  setup_data = function(data, params) { 
    data 
  }, 

  # set up the parameters, e.g. supply warnings for incorrect input
  setup_params = function(data, params) {
    params
  },

  draw_group = function(data, panel_params, coord, anchor, shadows, type) { 
    # draw_group uses stats returned by compute_group

    # set common aesthetics
    geom_aes <- list(
      alpha = data$alpha,
      colour = data$color,
      size = data$size,
      linetype = data$linetype,
      fill = alpha(data$fill, data$alpha),
      group = data$group
    )

    # merge aesthetics with data calculated in setup_data
    geom_stats <- new_data_frame(c(list(
          x = c(data$x.xmin, data$y.xmin),
          xend = c(data$x.xmax, data$y.xmax),
          y = c(data$x.ymin, data$y.ymin),
          yend = c(data$x.ymax, data$y.ymax),
          alpha = c(data$alpha, data$alpha) 
        ), geom_aes
      ), n = 2) 

    # turn the stats data into a GeomPath
    geom_grob <- GeomSegment$draw_panel(unique(geom_stats), 
        panel_params, coord) 

    # pass the GeomPath to grobTree
    ggname("geom_shadows", grobTree(geom_grob)) 
  },

  # set legend box styles
  draw_key = draw_key_path,

  # set default aesthetics 
  default_aes = aes(
    colour = "blue",
    fill = "red",
    size = 1,
    linetype = 1,
    alpha = 1
  )

)

stat-shadows.r

#' documentation ought to be here
stat_shadows <-  
  function(mapping = NULL, 
           data = NULL,
           geom = "shadows", 
           position = "identity",
           ...,
           # do I need to add the geom_shadows arguments here?
           anchor = list(x = 0, y = 0),
           shadows = list("x", "y"), 
           type = NULL,
           na.rm = FALSE,
           show.legend = NA,
           inherit.aes = TRUE) {
  layer(
    stat = StatShadows,  
    data = data,
    mapping = mapping,
    geom = geom,
    position = position,
    show.legend = show.legend,
    inherit.aes = inherit.aes,
    params = list(
      # geom_shadows argument repeated here?
      anchor = anchor,  
      shadows = shadows,
      type = type,
      na.rm = na.rm,
      ...
    )
  )
}

StatShadows <- 
  ggproto("StatShadows", Stat,

    # do I need to repeat required_aes?
    required_aes = c("x", "y"), 

    # set up the data, e.g. remove missing data
    setup_data = function(data, params) {
      data
    },

    # set up parameters, e.g. unpack from list
    setup_params = function(data, params) {
      params
    },

    # calculate shadows: returns data_frame with colnames: xmin, xmax, ymin, ymax 
    compute_group = function(data, scales, anchor = list(x = 0, y = 0), shadows = list("x", "y"), type = NULL, na.rm = TRUE) {

      .compute_shadows(data = data, anchor = anchor, shadows = shadows, type = type)

  }
)

# Calculate the shadows for each type / shadows / anchor
.compute_shadows <- function(data, anchor, shadows, type) {

# Deleted all type-checking, etc. for MWE
# Only 'type = c(double, double)' accepted, e.g. type = c(0, 1)

qs <- type

# compute shadows along the x-axis
if (any(shadows == "x")) {
    shadows.x <- c(
    xmin = as.numeric(stats::quantile(data[, "x"], qs[[1]])),
    xmax = as.numeric(stats::quantile(data[, "x"], qs[[2]])),
    ymin = anchor[["y"]], 
    ymax = anchor[["y"]]) 
}

# compute shadows along the y-axis
if (any(shadows == "y")) {
    shadows.y <- c(
    xmin = anchor[["x"]], 
    xmax = anchor[["x"]], 
    ymin = as.numeric(stats::quantile(data[, "y"], qs[[1]])),
    ymax = as.numeric(stats::quantile(data[, "y"], qs[[2]])))
} 

# store shadows in one data_frame
stats <- new_data_frame(c(x = shadows.x, y = shadows.y))

# return the statistics
stats
}

.

person PatrickT    schedule 26.12.2018    source источник
comment
Этот вопрос был отредактирован, чтобы отразить наиболее важные проблемы: в частности, я исправил очевидные проблемы в draw_group при объединении эстетики с данными. Код в основном основан на geom_boxplot и stat_boxplot, особенно на whiskers.   -  person PatrickT    schedule 30.12.2018
comment
Обратите внимание, что для компиляции приведенного выше кода вам необходимо либо поставить перед специальными функциями префикс ggplot2:: и скопировать такие функции, как new_data_frame, либо (что я делаю) клонировать пакет ggplot2 и скомпилировать его с двумя указанными выше файлами, добавленными и правильно указанными в NAMESPACE и DESCRIPTION   -  person PatrickT    schedule 30.12.2018


Ответы (1)


Пока не появится более подробный ответ: вы упускаете

extra_params = c("na.rm", "shadows", "anchor", "type"),

внутри GeomShadows <- ggproto("GeomShadows", Geom,

и, возможно, также внутри StatShadows <- ggproto("StatShadows", Stat,.

Внутри geom-.r и stat-.r есть много очень полезных комментариев, разъясняющих, как работают геометрии и статистика. В частности (шляпа подсказывает Клаусу Уилке по вопросам на github):

# Most parameters for the geom are taken automatically from draw_panel() or
# draw_groups(). However, some additional parameters may be needed
# for setup_data() or handle_na(). These can not be imputed automatically,
# so the slightly hacky "extra_params" field is used instead. By
# default it contains `na.rm`
extra_params = c("na.rm"),
person PatrickT    schedule 30.12.2018