расширение ggplot, чтобы брать списки и делать их доступными для пользовательских геомов - PullRequest
0 голосов
/ 29 апреля 2019

Я пытаюсь заставить ggplot2 принимать списки и делать элементы списков доступными для других пользовательских функций geom.

У меня есть новая функция ggplot, которая принимает списки:

ggplot.list <- function(data = NULL,
                           mapping = ggplot2::aes(),
                           ...,
                           environment = parent.frame()) {

    p <- ggplot2::ggplot(data=data[[1]],mapping=mapping,..., environment=environment)

    p$data_ext <- data[[2]]
    p
}

Я создаю свой список и строю первый data.frame:

l <- list(tibble(x=1:10, y=1:10), tibble(x=1:10+100, y =1:10+200))

ggplot(l) + geom_point(aes(x=x,y=y))

В идеале я хотел бы создать что-то вроде этого (которое не работает), еще один geom, который по умолчанию берет data_ext из объекта ggplot

geom_point2 <- function (mapping = NULL, data_ext = NULL, stat = "identity", position = "identity", 
                         ..., na.rm = FALSE, show.legend = NA, inherit.aes = TRUE) 
{
    layer(data_ext = data_ext, mapping = mapping, stat = stat, geom = GeomPoint, 
          position = position, show.legend = show.legend, inherit.aes = inherit.aes, 
          params = list(na.rm = na.rm, ...))
}

ggplot(l) + geom_point(aes(x=x,y=y)) +  geom_point2(aes(x=x,y=y))

Я вижу, что мой второй data.frame находится внутри объекта ggplot, но я не знаю, как получить к нему доступ; то есть ggplot(l)$data_ext работает. Я пытался поиграть с ggproto, но у меня недостаточно опыта, чтобы понять, что с ним делать, и может ли это помочь.

ДОБАВЛЕНО Кстати, я могу достичь желаемого с помощью конвейера, но я не хочу путать потенциальных пользователей моих функций:

pipe_point2 <-function (plot, mapping = NULL, data = NULL, stat = "identity", position = "identity", 
                        ..., na.rm = FALSE, show.legend = NA, inherit.aes = TRUE) 
{

  plot +  layer(data = plot$data_ext, mapping = mapping, stat = stat, geom = GeomPoint,
          position = position, show.legend = show.legend, inherit.aes = inherit.aes, 
          params = list(na.rm = na.rm, ...))
}

{ggplot(l) + geom_point(aes(x=x,y=y))} %>%  pipe_point2(aes(x=x,y=y))

1 Ответ

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

Важные указания, с которых я начал создавать свои собственные объекты ggproto, пришли по следующей ссылке Расширение ggplot2 . TL; DR: Будет очень трудно писать новые геомы без объектов ggproto.

Я попытался переправить матрицу в пользовательский geom_raster (), который был бы аналогичен контрабанде списка в пользовательские гемы. Я узнал, что шкалы очень трудно обучить, то есть шкалы положения не знали, каковы пределы матрицы, а шкала заполнения не могла знать, каковы были пределы матрицы. Я дошел до того, что все выглядело правильно, но руководство по цветной полосе не показывало правильные цифры.

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

mydf <- data.frame(x = 1:3, y = 1:3)
mydf$z <- list(c("A","B"), 1:5, function(x) x/10)
print(mydf)
  x y                   z
1 1 1                A, B
2 2 2       1, 2, 3, 4, 5
3 3 3 function (x) , x/10

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

g <- ggplot(mydf) + geom_point(aes(x = x, y = y, z = z))
# Warning: Ignoring unknown aesthetics: z

И убедитесь, что слой имеет к нему доступ:

layer_data(g)
  x y                   z PANEL group shape colour size fill alpha stroke
1 1 1                A, B     1    -1    19  black  1.5   NA    NA    0.5
2 2 2       1, 2, 3, 4, 5     1    -1    19  black  1.5   NA    NA    0.5
3 3 3 function (x) , x/10     1    -1    19  black  1.5   NA    NA    0.5

И это так, теперь вам нужно всего лишь написать свою собственную функцию рисования панели geom, чтобы изобразить этот список на графике, для которого я рекомендую ссылку выше.

Единственное, что делает следующее, - это печатает z до рисования точек и принимает z в качестве дополнительной эстетики:

MyGeom <- ggproto(
  "MyGeom", GeomPoint,
  draw_panel = function(data, panel_params, coord, na.rm = FALSE) {
    print(data$z)
    GeomPoint$draw_panel(data, panel_params, coord, na.rm = FALSE)
  },
  optional_aes = "z"
)

Теперь вам нужна оболочка для вашего слоя, которая указывает на MyGeom:

geom_mine <- function (mapping = NULL, data = NULL, stat = "identity", position = "identity", 
                         ..., na.rm = FALSE, show.legend = NA, inherit.aes = TRUE) 
{
  layer(mapping = mapping, stat = stat, geom = MyGeom, 
        position = position, show.legend = show.legend, inherit.aes = inherit.aes, 
        params = list(na.rm = na.rm, ...))
}

Теперь вы можете использовать это на графике:

ggplot(mydf) + geom_mine(aes(x = x, y = y, z = z))
[[1]]
[1] "A" "B"

[[2]]
[1] 1 2 3 4 5

[[3]]
function (x) 
x/10

И вуаля, он делает сюжет и печатает z, как мы и сказали.

Надеюсь, эти указатели помогут!

...