简体   繁体   English

如何在ggplot2中的绘图区域之外绘制线?

[英]How to draw lines outside of plot area in ggplot2?

I created this plot with ggplot2 : 我用ggplot2创建了这个图:

在此处输入图片说明

The outside lines need to correspond to the Y scale, (ie the Y position of the lines for Text1 should be 100 and 85). 外部线需要对应于Y比例尺(即Text1的线的Y位置应为100和85)。 The only way I can do it by drawing a blank plot to the right of the figure with the same scale as the barchart and then using annotate function to draw the lines. 我可以做到的唯一方法是在图的右侧绘制一个与条形图相同比例的空白图,然后使用注释功能绘制线条。 Another approach is to simply "manually" draw the lines with grid.lines , however the coordinates of grid.lines will not correspond to the Y scale of the plot. 另一种方法是简单的“手动”绘制线条grid.lines ,但是坐标grid.lines将不符合情节的Y规模。

Is it possible to somehow draw these lines using a different approach? 是否可以使用其他方法以某种方式画出这些线? I assume it would have to be done with grid.lines . 我认为必须使用grid.lines完成。 How could I pass Y coordindates of the barchart to grid.lines ? 如何将条形图的Y坐标传递给grid.lines

Below is the minimal code used to create this figure: 以下是用于创建该图形的最少代码:

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"))

在此处输入图片说明

How can I draw the lines to the right of the bars? 如何在条形的右边画线?

I recently asked a question about drawing text outside of plot area in ggplot2 and the solution was to use gt$layout and grid.draw . 我最近问了一个有关在ggplot2中在绘图区域之外绘制文本的问题,解决方案是使用gt$layoutgrid.draw

Displaying text below the plot generated by ggplot2 在ggplot2生成的绘图下方显示文本

Could the similar approach be used here? 可以在这里使用类似的方法吗? It is my understanding that annotation_custom is for text only and won't work with other graphical elements. 据我了解,annotation_custom仅用于文本,不能与其他图形元素一起使用。 Thanks 谢谢

Update 更新资料

The original solution used annotation_custom , but a problem with annotation_custom is that it draws the annotation in all panels. 最初的解决方案中使用annotation_custom ,但有一个问题annotation_custom是,它在吸引所有面板的注释。 However, with a simple modification, annotation_custom can be made to draw in one panel only (taken from Baptiste's answer here ) 然而,简单的修改, 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 原始解决方案

I think almost any Grob created using grid() can be used in annotation_custom() . 我认为几乎所有使用grid()创建的Grob都可以在annotation_custom() There might be neater ways to do this, but here's a way using grid , annotation_custom and @baptiste's code from here to override the clipping (as in the earlier post). 可能有更巧妙的方法来执行此操作,但是这是从此处使用gridannotation_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)

在此处输入图片说明

Updated opts has been deprecated; 更新 opts已弃用; use theme instead. 改用theme

Here's another solution. 这是另一个解决方案。 It gets round the problem of annotation_custom() drawing grobs in both panels. 解决了两个面板中的annotation_custom()绘图问题。 It draws two graphs: the first is your bar plot; 它绘制了两个图形:第一个是您的条形图;第二个是您的条形图。 the second contains only the annotations. 第二个仅包含注释。 Then the two are put together using grid.arrange() from the gridExtra package. 然后使用gridExtra包中的grid.arrange()将两者放在一起。 However, your polyline issue remains. 但是,您的折线问题仍然存在。

There is still the issue of getting the y-axis scales the same in the two plots. 在两个图中,仍然存在使y轴比例相同的问题。 But with care, it can be done. 但是只要小心,就可以做到。 In the plot that contains the annnotations, notice how elements that could have an impact on the y-axis scale are not removed (via theme_blank() , but rather are hidden (using colour = NA ). 在包含注释的图形中,请注意如何删除不会对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)

机器人

I added the lines/text using code from this link: Using grconvertX/grconvertY in ggplot2 . 我使用以下链接中的代码添加了行/文本: 在ggplot2中使用grconvertX / grconvertY This approach uses grid.text and grid.lines instead of grobs. 这种方法使用grid.text和grid.lines而不是grobs。 I am not sure which approach is better. 我不确定哪种方法更好。

I think grid.lines could be combined into grid.polyline statement or possibly done via a loop. 我认为可以将grid.lines合并为grid.polyline语句,也可以通过循环来完成。 The x and y positions can be set to one variable instead of hardcoding in every line. 可以将x和y位置设置为一个变量,而不是在每一行中进行硬编码。

The only possible complication is passing the scale to the viewport. 唯一可能的麻烦是将比例传递到视口。 However, as long as the the same scale is used in GGPLOT and the viewport this code should work. 但是,只要在GGPLOT和视口中使用相同的比例尺,此代码就应该起作用。 Note that the viewport is using the entire height of the plot as 0 to 100. 请注意,视口将图的整个高度用作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' )

Apologies for any formatting problems - I simply pasted my code and used the code button to indent it. 对于任何格式问题,我们深表歉意-我只是粘贴了代码,然后使用代码按钮对其进行了缩进。

在此处输入图片说明

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM