Вопрос по r, ggplot2 – Как рисовать линии за пределами области графика в ggplot2?

25

Я создал этот сюжет сggplot2:

enter image description here

Внешние линии должны соответствовать шкале Y (то есть позиция Y строк для Text1 должна быть 100 и 85). Единственный способ сделать это - нарисовать пустой график справа от фигуры с тем же масштабом, что и на диаграмме, а затем использовать функцию аннотирования для рисования линий. Другой подход заключается в том, чтобы просто «вручную» рисовать линии сgrid.linesОднако координатыgrid.lines не будет соответствовать шкале Y графика.

Можно ли как-то нарисовать эти линии, используя другой подход? Я предполагаю, что это должно быть сделано сgrid.lines, Как я мог передать координаты Y диаграммыgrid.lines?

Ниже приведен минимальный код, используемый для создания этой фигуры:

library (ggplot2)
test= data.frame(
  group=c(rep(1,6), rep(2,6)),
  subgroup=c( 1,1,1,2,2,2,1,1,1,2,2,2),
  category=c( rep(1:3, 4)),
  count=c( 10,80,10,5,90,5,  10,80,10,5,90,5   )
  )

qplot(subgroup, 
      count, 
      data=test, 
      geom="bar", 
      stat="identity",
      fill =category,  
      facets =  .~ group,  width=0.9)+
      opts(legend.position="none",
           plot.margin = unit(c(0,9,2,0), "lines"))

enter image description here

Как я могу нарисовать линии справа от столбцов?

Я недавно задал вопрос о рисовании текста вне области графика вggplot2 и решение было использоватьgt$layout а такжеgrid.draw.

Отображение текста под графиком, сгенерированным ggplot2

Можно ли использовать подобный подход здесь? Насколько я понимаю, annotation_custom предназначена только для текста и не будет работать с другими графическими элементами. Спасибо

Ваш Ответ

3   ответа
20

Update

Используется оригинальное решениеannotation_custom, но проблема сannotation_custom является то, что он рисует аннотации на всех панелях. Тем не менее, с простой модификацией,annotation_custom рисовать можно только на одной панели (взято из ответа Баптиста)Вот)

annotation_custom2 <- 
function (grob, xmin = -Inf, xmax = Inf, ymin = -Inf, ymax = Inf, data) 
{
  layer(data = data, stat = StatIdentity, position = PositionIdentity, 
        geom = ggplot2:::GeomCustomAnn,
        inherit.aes = TRUE, params = list(grob = grob, 
                                          xmin = xmin, xmax = xmax, 
                                          ymin = ymin, ymax = ymax))
}

library(ggplot2)
library(grid)

 #Some data
test = data.frame(
  group=c(rep(1,6), rep(2,6)),
  subgroup=c( 1,1,1,2,2,2,1,1,1,2,2,2),
  category=c( rep(1:3, 4)),
  count=c( 10,80,10,5,90,5,  10,80,10,5,90,5   )
  )

# base plot
p <- ggplot(test) +
   geom_bar(aes(subgroup, count, fill = category), stat = "identity") +
   facet_grid(. ~ group) +
  theme(legend.position = "none",  
        plot.margin = unit(c(1,5,1,1), "lines"))

# Create the text Grobs
Text1 = textGrob("Text 1")
Text2 = textGrob("Text 2")
Text4 = textGrob("Text 4")

## Add the annotations
# Which panel to attach the annotations
data = data.frame(group=2)

# Text 1
p1 = p + annotation_custom2(Text1,  xmin = 3., xmax = 3., ymin = 85, ymax = 100, data = data) +
    annotation_custom2(linesGrob(), xmin = 2.6, xmax = 2.75, ymin = 100, ymax = 100, data = data) +
    annotation_custom2(linesGrob(), xmin = 2.6, xmax = 2.75, ymin = 85, ymax = 85, data = data) +
    annotation_custom2(linesGrob(), xmin = 2.75, xmax = 2.75, ymin = 85, ymax = 100, data = data)

# Text 2
p1 = p1 + annotation_custom2(Text2,  xmin = 3, xmax = 3, ymin = 20, ymax = 80, data = data) +
    annotation_custom2(linesGrob(), xmin = 2.6, xmax = 2.75, ymin = 80, ymax = 80, data = data) +
    annotation_custom2(linesGrob(), xmin = 2.6, xmax = 2.75, ymin = 20, ymax = 20, data = data) +
    annotation_custom2(linesGrob(), xmin = 2.75, xmax = 2.75, ymin = 20, ymax = 80, data = data)

# Text 4
p1 = p1 + annotation_custom2(Text4,  xmin = 3, xmax = 3, ymin = 0, ymax = 15, data = data) +
    annotation_custom2(linesGrob(), xmin = 2.6, xmax = 2.75, ymin = 15, ymax = 15, data = data) +
    annotation_custom2(linesGrob(), xmin = 2.6, xmax = 2.75, ymin = 0, ymax = 0, data = data) +
    annotation_custom2(linesGrob(), xmin = 2.75, xmax = 2.75, ymin = 0, ymax = 15, data = data)


# Code to override clipping
gt <- ggplotGrob(p1)
gt$layout[grepl("panel", gt$layout$name), ]$clip <- "off"

# Draw the plot
grid.newpage()
grid.draw(gt)

Original Solution

Я думаю, что почти любой Grob создан с использованиемgrid() может быть использован вannotation_custom(). There might be neater ways to do this, but here's a way using grid, annotation_custom и код @ baptisteотсюда переопределить отсечение (как в предыдущем посте).

library (ggplot2)
library(grid)

test= data.frame(
  group=c(rep(1,6), rep(2,6)),
  subgroup=c( 1,1,1,2,2,2,1,1,1,2,2,2),
  category=c( rep(1:3, 4)),
  count=c( 10,80,10,5,90,5,  10,80,10,5,90,5   )
  )

## EDIT:  Updated qplot() command
p <- qplot(subgroup, count, 
  data = test, geom = "bar",  stat = "identity",
  fill = category,  
  facets = .~ group,  width = 0.9)+
  theme(legend.position="none",  plot.margin = unit(c(0,9,2,0), "lines"))



# Create the text Grobs
Text1 = textGrob("Text 1")
Text2 = textGrob("Text 2")
Text4 = textGrob("Text 4")

# Draw the plot
# Text 1
p1 = p + annotation_custom(grob = Text1,  xmin = 3., xmax = 3., ymin = 85, ymax = 100) +
    annotation_custom(grob = linesGrob(), xmin = 2.6, xmax = 2.75, ymin = 100, ymax = 100) +
    annotation_custom(grob = linesGrob(), xmin = 2.6, xmax = 2.75, ymin = 85, ymax = 85) +
    annotation_custom(grob = linesGrob(), xmin = 2.75, xmax = 2.75, ymin = 85, ymax = 100)

# Text 2
p1 = p1 + annotation_custom(grob = Text2,  xmin = 3, xmax = 3, ymin = 20, ymax = 80) +
    annotation_custom(grob = linesGrob(), xmin = 2.6, xmax = 2.75, ymin = 80, ymax = 80) +
    annotation_custom(grob = linesGrob(), xmin = 2.6, xmax = 2.75, ymin = 20, ymax = 20) +
    annotation_custom(grob = linesGrob(), xmin = 2.75, xmax = 2.75, ymin = 20, ymax = 80)

# Text 4
p1 = p1 + annotation_custom(grob = Text4,  xmin = 3, xmax = 3, ymin = 0, ymax = 15) +
    annotation_custom(grob = linesGrob(), xmin = 2.6, xmax = 2.75, ymin = 15, ymax = 15) +
    annotation_custom(grob = linesGrob(), xmin = 2.6, xmax = 2.75, ymin = 0, ymax = 0) +
    annotation_custom(grob = linesGrob(), xmin = 2.75, xmax = 2.75, ymin = 0, ymax = 15)

p1

# Code to override clipping
gt <- ggplot_gtable(ggplot_build(p1))
gt$layout$clip[gt$layout$name=="panel"] <- "off"
grid.draw(gt)

enter image description here

Сэнди: я пытаюсь использовать polylineGrob, но у меня возникают проблемы при передаче его в annotation_custom. Я не хочу публиковать здесь длинную программу или создавать другой вопрос, в основном это похоже на работу, но я не могу понять, как х из полилинии взаимодействуют с теми из пользовательских аннотаций Max C
Error: User Rate Limit Exceededp + annotation_custom(grob = textGrob("Text anywhere"), xmin = 1.5, xmax = 1.5, ymin = 50, ymax = 50)
Сэнди: Еще раз спасибо !!! Я предполагал, что хитрость в текстовом вопросе работает только с текстом. Это очень помогает. Max C
Error: User Rate Limit ExceededpolylineGrob это должно сделать код немного более эффективным.
_______________________________________________________________ Max C
6

Updated opts устарела; использованиеtheme вместо.

Вот еще одно решение. Это обходит проблемуannotation_custom() рисование гробов в обеих панелях. Он рисует два графика: первый - это ваш гистограмма; вторая содержит только аннотации. Затем два соединяются с помощьюgrid.arrange() отgridExtra пакет. Тем не менее, ваша проблема ломаной линии остается.

Существует проблема получения одинаковых масштабов оси Y на двух графиках. Но с осторожностью это можно сделать. Обратите внимание, что на графике, содержащем аннотации, элементы, которые могут повлиять на шкалу оси Y, не удаляются (с помощьюtheme_blank(), а скорее скрыты (используяcolour = NA).

library(ggplot2)
library(gridExtra)
library(grid)

test= data.frame(
  group=c(rep(1,6), rep(2,6)),
  subgroup=c( 1,1,1,2,2,2,1,1,1,2,2,2),
  category=c( rep(1:3, 4)),
  count=c( 10,80,10,5,90,5,  10,80,10,5,90,5))

# The bar plot
p1 <- ggplot(test, aes(subgroup, count, fill = category)) +
   geom_bar(stat = "identity") +
   facet_grid(.~ group) +
   theme(legend.position = "none",  
         plot.margin = unit(c(1,0,2,0), "lines"))

p1 <- p1 + ylim(0, 100)

# The empty plot to contain the annotations
p2 = ggplot(data.frame(x = c(1,2), y = c(0,100), z = c(1,1)), aes(x,y)) + theme_bw() + facet_wrap(~ z) +
   theme(axis.title.y = element_blank(),
        axis.title.x = element_text(colour = NA),
        axis.text.y = element_blank(),
        axis.text.x = element_text(colour = NA),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        axis.ticks = element_line(colour = NA),
        panel.border = element_rect(colour = NA),
        strip.background = element_rect(colour = NA, fill = NA),
        strip.text.x = element_text(colour = NA), 
        plot.margin = unit(c(1,0,2,0), "lines"))


# The annotations
Text1 = textGrob("Text 1")
Text2 = textGrob("Text 2")
Text4 = textGrob("Text 4")

p2 = p2 + annotation_custom(grob = Text1,  xmin = 1.4, xmax = 1.4, ymin = 85, ymax = 100) +
        annotation_custom(grob = linesGrob(), xmin = 1, xmax = 1.1, ymin = 100, ymax = 100) +
        annotation_custom(grob = linesGrob(), xmin = 1, xmax = 1.1, ymin = 85, ymax = 85) +
        annotation_custom(grob = linesGrob(), xmin = 1.1, xmax = 1.1, ymin = 85, ymax = 100)

p2 = p2 + annotation_custom(grob = Text2,  xmin = 1.4, xmax = 1.4, ymin = 20, ymax = 80) +
        annotation_custom(grob = linesGrob(), xmin = 1, xmax = 1.1, ymin = 80, ymax = 80) +
        annotation_custom(grob = linesGrob(), xmin = 1, xmax = 1.1, ymin = 20, ymax = 20) +
        annotation_custom(grob = linesGrob(), xmin = 1.1, xmax = 1.1, ymin = 20, ymax = 80)

p2 = p2 + annotation_custom(grob = Text4,  xmin = 1.4, xmax = 1.4, ymin = 0, ymax = 15) +
        annotation_custom(grob = linesGrob(), xmin = 1, xmax = 1.1, ymin = 15, ymax = 15) +
        annotation_custom(grob = linesGrob(), xmin = 1, xmax = 1.1, ymin = 0, ymax = 0) +
        annotation_custom(grob = linesGrob(), xmin = 1.1, xmax = 1.1, ymin = 0, ymax = 15)

# Putting the two plots together
plot = arrangeGrob(p1, p2, ncol = 2, widths = unit(c(10, 2), c("null", "null")))
grid.draw(plot)

bot

Рассматривали ли вы вопрос о переносе вопроса наggplot2 mailing list?
В прошлом мне не особо везло с рассылкой R. В любом случае, я думаю, что решение здесь достаточно хорошее. Я, конечно, могу комментировать сюжет, используя ваш метод и метод, предложенный на другом графике. Спасибо Max C
На данный момент, похоже, нет простого способа аннотировать GGPLOT. Это, конечно, можно сделать, но это не так просто, как с помощью SAS DATA ANNOTATE или с базовыми функциями R. Max C
Сэнди: Большое спасибо. Это на самом деле то, что я сделал с моим оригинальным сюжетом - фигура в самом начале была сделана таким образом. Я упоминаю этот подход в моем первоначальном вопросе здесь. Я также использовал тот же подход для отображения текста под графиком в моем другом посте. Тем не мение. Я нахожу этот подход довольно сложным, тем более что мои данные со временем меняются и потому что мне действительно нужно подготовить 4 графика для одного отчета. Вот почему я решил искать лучшее решение. Еще раз большое спасибо. Max C
5

Использование grconvertX / grconvertY в ggplot2, Этот подход использует grid.text и grid.lines вместо grobs. Я не уверен, какой подход лучше.

Я думаю, что grid.lines можно объединить в оператор grid.polyline или, возможно, сделать через цикл. Позиции x и y могут быть установлены в одну переменную вместо жесткого кодирования в каждой строке.

Единственное возможное осложнение - передача шкалы в окно просмотра. Однако, если в GGPLOT и в окне просмотра используется тот же масштаб, этот код должен работать. Обратите внимание, что область просмотра использует всю высоту графика от 0 до 100.

library (ggplot2)
library(grid)
library(gridBase)

test= data.frame(
  group=c(rep(1,6), rep(2,6)),
  subgroup=c( 1,1,1,2,2,2,1,1,1,2,2,2),
  category=c( rep(1:3, 4)),
  count=c( 10,80,10,5,90,5,  10,80,10,5,90,5   )
  )

qplot(subgroup, count, 
           data=test, geom="bar",  stat="identity",
           fill =category,  
           facets =  .~ group,  width=0.9)+
             opts(legend.position="none",  plot.margin = unit(c(0,9,2,0), "lines"))

current.vpTree()
downViewport('panel-4-6')
pushViewport(dataViewport( yscale=c(0,100), clip='off',xscale=c(0,1)))

grid.text(x=1.21,  y = 90, default.units='native' ,label="Text 1")
grid.text(x=1.21,  y = 55, default.units='native' ,label="Text 2")
grid.text(x=1.21,  y = 10, default.units='native' ,label="Text 3")

grid.lines(x=c(1.02,1.12), y = c(95,95), default.units='native' )
grid.lines(x=c(1.02,1.12), y = c(85, 85), default.units='native' )
grid.lines(x=c(1.12,1.12), y = c(85, 95), default.units='native' )

grid.lines(x=c(1.02,1.12), y = c(80, 80), default.units='native' )
grid.lines(x=c(1.02,1.12), y = c(20, 20), default.units='native' )
grid.lines(x=c(1.12,1.12), y = c(80, 20), default.units='native' )

grid.lines(x=c(1.02,1.12), y = c(5, 5), default.units='native' )
grid.lines(x=c(1.02,1.12), y = c(15, 15), default.units='native' )
grid.lines(x=c(1.12,1.12), y = c(5, 15), default.units='native' )

Извиняюсь за любые проблемы с форматированием - я просто вставил свой код и использовал кнопку кода, чтобы сделать отступ.

enter image description here

Сэнди: I Ваше решение с гробами сработало лучше, если применить к моей исходной фигуре (не к макету). Grobs ссылаются на фактическую высоту столбцов, в то время как grid.lines / text должны быть скорректированы. Я испробовал оба подхода, и ваш намного лучше. Еще раз большое спасибо! Max C
Если вы удалите отступ на оси Y сscale_y_continuous(expand = c(0, 0)) тогда масштаб кажется правильным.

Похожие вопросы