Подтвердить что ты не робот

Удаление слоя из диаграммы ggplot2

Я хотел бы удалить layer (в этом случае результаты geom_ribbon) из созданного ggplot2 объекта сетки. Есть ли способ удалить его, когда он уже является частью объекта?

library(ggplot2)
dat <- data.frame(x=1:3, y=1:3, ymin=0:2, ymax=2:4)
p <- ggplot(dat, aes(x=x, y=y)) + geom_ribbon(aes(ymin=ymin, ymax=ymax), alpha=0.3) 
     + geom_line()

# This has the geom_ribbon
p

# This overlays another ribbon on top
p + geom_ribbon(aes(ymin=ymin, ymax=ymax, fill=NA))

Я бы хотел, чтобы эта функциональность позволяла мне создавать более сложные сюжеты поверх менее сложных. Я использую функции, которые возвращают объект сетки, а затем распечатывают окончательный график после его полной сборки. На базовом участке имеется одна строка с соответствующей geom_ribbon (geom_ribbon). Более сложный сюжет будет иметь несколько линий, а многократные перекрывающиеся объекты geom_ribbon отвлекают. Я хотел бы удалить их с участков с несколькими строками. Кроме того, я смогу быстро создавать альтернативные версии с использованием фасетов или других функций ggplot2.


Изменение: Принять ответ @mnel, поскольку он работает. Теперь мне нужно определить, как динамически обращаться к слою geom_ribbon, который geom_ribbon в вопросе SO здесь.


Изменить 2: Для полноты, это функция, которую я создал для решения этой проблемы:

remove_geom <- function(ggplot2_object, geom_type) {
  layers <- lapply(ggplot2_object$layers, function(x) if(x$geom$objname == geom_type) NULL else x)
  layers <- layers[!sapply(layers, is.null)]

  ggplot2_object$layers <- layers
  ggplot2_object
}

Изменить 3: см. Принятый ответ ниже для последних версий ggplot (> = 2.xy)

4b9b3361

Ответ 1

Для ggplot2 версии 2.2.1 мне пришлось изменить предлагаемую функцию remove_geom следующим образом:

remove_geom <- function(ggplot2_object, geom_type) {
  # Delete layers that match the requested type.
  layers <- lapply(ggplot2_object$layers, function(x) {
    if (class(x$geom)[1] == geom_type) {
      NULL
    } else {
      x
    }
  })
  # Delete the unwanted layers.
  layers <- layers[!sapply(layers, is.null)]
  ggplot2_object$layers <- layers
  ggplot2_object
}

Вот пример того, как его использовать:

library(ggplot2)

set.seed(3000)
d <- data.frame(
  x = runif(10),
  y = runif(10),
  label = sprintf("label%s", 1:10)
)

p <- ggplot(d, aes(x, y, label = label)) + geom_point() + geom_text()

Покажите исходный график:

p

сюжет с текстовыми метками

Теперь удалим ярлыки и снова покажу график:

p <- remove_geom(p, "GeomText")
p

график без текстовых меток

Ответ 2

Если вы посмотрите

p$layers
[[1]]
mapping: ymin = ymin, ymax = ymax 
geom_ribbon: na.rm = FALSE, alpha = 0.3 
stat_identity:  
position_identity: (width = NULL, height = NULL)

[[2]]
geom_line:  
stat_identity:  
position_identity: (width = NULL, height = NULL)

Вы увидите, что вы хотите удалить первый слой

Вы можете сделать это, переопределив слои как только второй компонент в списке.

p$layer <- p$layer[2]

Теперь построим и построим p

p

Обратите внимание, что p$layer[[1]] <- NULL будет работать. Я согласен с комментариями @Andrie и @Joran относительно тех случаев, когда это может быть полезно, и не ожидал бы, что это будет обязательно надежным. enter image description here

Ответ 3

Поскольку эта проблема выглядела интересной, я расширил свой пакет ggpmisc с функциями для управления слоями в объекте ggplot (в настоящее время в пакете "gginnards"). Функции - это более отполированные версии примера в моем предыдущем ответе на этот же вопрос. Однако имейте в виду, что в большинстве случаев это не лучший способ работы, поскольку он нарушает грамматику графики. В большинстве случаев можно обычным образом собирать различные варианты одного и того же рисунка с помощью оператора +, возможно, "упаковывать" группы слоев в списки, чтобы иметь объединенные строительные блоки, которые могут упростить сборку сложных фигур. В исключительных случаях мы можем захотеть отредактировать существующий сюжет или вывод графика с помощью функции более высокого уровня, чье определение мы не можем изменить. В таких случаях эти функции манипулирования слоями могут быть полезны. Приведенный выше пример становится.

library(gginnards)
p1 <- delete_layers(p, match_type = "GeomText")

См. Документацию пакета для других примеров и информацию о вспомогательных функциях, полезных для изменения порядка слоев, а также для вставки новых слоев в произвольные позиции.

Ответ 4

@Kamil Slowikowski Спасибо! Очень полезно. Однако я не мог остановить себя от создания нового варианта по той же теме... надеюсь, легче понять, чем в оригинальном посте или обновленной версии Kamil, также избегая некоторых назначений.

remove_geoms <- function(x, geom_type) {
  # Find layers that match the requested type.
  selector <- sapply(x$layers,
                     function(y) {
                       class(y$geom)[1] == geom_type
                     })
  # Delete the layers.
  x$layers[selector] <- NULL
  x
}

Эта версия функционально идентична функции Kamil, поэтому здесь не нужно повторять приведенный выше пример использования.

В стороне эта функция может быть легко адаптирована для выбора слоев на основе класса stat вместо класса геометрии.

remove_stats <- function(x, stat_type) {
  # Find layers that match the requested type.
  selector <- sapply(x$layers,
                     function(y) {
                       class(y$stat)[1] == stat_type
                     })
  # Delete the layers.
  x$layers[selector] <- NULL
  x
}

Ответ 5

@Kamil и @Pedro Спасибо большое! Для тех, кто заинтересован, можно также увеличить функцию Pedro, чтобы выбрать только определенные слои, как показано здесь с аргументом last_only:

remove_geoms <- function(x, geom_type, last_only = T) {
  # Find layers that match the requested type.
  selector <- sapply(x$layers,
                     function(y) {
                       class(y$geom)[1] == geom_type
                     })
  if(last_only) 
    selector <- max(which(selector))
  # Delete the layers.
  x$layers[selector] <- NULL
  x
}

Возвращаясь к примерному примеру @Kamil:

set.seed(3000)
d <- data.frame(
  x = runif(10),
  y = runif(10),
  label = sprintf("label%s", 1:10)
)

p <- ggplot(d, aes(x, y, label = label)) + geom_point() + geom_point(color = "green") + geom_point(size = 5, color = "red")

p

enter image description here

p %>% remove_geoms("GeomPoint")

enter image description here

p %>% remove_geoms("GeomPoint")  %>% remove_geoms("GeomPoint")

enter image description here