如何获取包括超出范围的对象的图的尺寸

时间:2018-07-11 17:22:53

标签: r ggplot2 gridextra r-grid

我可以这样计算图的高度:

library(ggplot2)
library(egg)
library(gridExtra)

g <- ggplot(iris, aes(x = Species, y = Petal.Length)) +
  stat_summary(geom = 'bar', fun.y = mean) +
  geom_point() +
  scale_y_continuous(limits = c(0,8), expand = c(0,0), oob = function(x, ...) x)

gt <- egg::set_panel_size(g)
gt$layout$clip[gt$layout$name=="panel"] <- "off"
gridExtra::grid.arrange(gt)

sum(as.numeric(grid::convertUnit(gt$heights, "mm")))

但是,如果我有一些超出范围的geom对象,它将返回相同的高度:

g <- ggplot(iris, aes(x = Species, y = Petal.Length)) +
  stat_summary(geom = 'bar', fun.y = mean) +
  geom_point() +
  scale_y_continuous(limits = c(0,8), expand = c(0,0), oob = function(x, ...) x) +
  geom_text(label = 'obText', aes(x = 2, y = 8.5))

gt <- egg::set_panel_size(g)
gt$layout$clip[gt$layout$name=="panel"] <- "off"
gridExtra::grid.arrange(gt)

sum(as.numeric(grid::convertUnit(gt$heights, "mm")))

即使现在文本的位置高于53.35411mm。

有没有一种方法可以获取包括超出范围的文字在内的绘图的高度?

1 个答案:

答案 0 :(得分:1)

我不确定您的用例是什么,但是是的,有可能。

问题的症结在于,文本(或任何其他geom层)的高度不是在gt$heights中捕获的,不是 ,而是在各个小齿轮的高度参数(表示为如.$x$height)在嵌套层次结构的下方。

# same code as yours, except that I positioned the label ever further up, to increase the contrast
g <- ggplot(iris, aes(x = Species, y = Petal.Length)) +
  stat_summary(geom = 'bar', fun.y = mean) +
  geom_point() +
  scale_y_continuous(limits = c(0,8), expand = c(0,0), oob = function(x, ...) x) +
  geom_text(label = 'obText', aes(x = 2, y = 18.5))    

gt <- egg::set_panel_size(g)

看看gt$heights。我们可以验证所有高度值均保持不变,而不管面板的剪辑是否已关闭:

> gt$heights
 [1] 5.5pt                    0cm                      0cm                     
 [4] 0cm                      0cm                      0cm                     
 [7] 4cm                      sum(2.75pt, 1grobheight) 1grobheight             
[10] 0cm                      0pt                      5.5pt    

gt$layout$clip[gt$layout$name=="panel"] <- "off"

> gt$heights
 [1] 5.5pt                    0cm                      0cm                     
 [4] 0cm                      0cm                      0cm                     
 [7] 4cm                      sum(2.75pt, 1grobheight) 1grobheight             
[10] 0cm                      0pt                      5.5pt   

在上述所有值中,您应该关心的是[7] 4cm,因为这是面板的高度。我们知道,由于基于gt的布局,面板位于第7行第5列,可以通过检查gt本身的控制台打印输出或通过gtable_show_layout()来验证该面板从gtable包中:

> gt
TableGrob (12 x 9) "layout": 18 grobs
    z         cells       name                                           grob
1   0 ( 1-12, 1- 9) background               rect[plot.background..rect.3020]
2   5 ( 6- 6, 4- 4)     spacer                                 zeroGrob[NULL]
3   7 ( 7- 7, 4- 4)     axis-l           absoluteGrob[GRID.absoluteGrob.3008]
4   3 ( 8- 8, 4- 4)     spacer                                 zeroGrob[NULL]
5   6 ( 6- 6, 5- 5)     axis-t                                 zeroGrob[NULL]
6   1 ( 7- 7, 5- 5)      panel                      gTree[panel-1.gTree.2994]
7   9 ( 8- 8, 5- 5)     axis-b           absoluteGrob[GRID.absoluteGrob.3001]
8   4 ( 6- 6, 6- 6)     spacer                                 zeroGrob[NULL]
9   8 ( 7- 7, 6- 6)     axis-r                                 zeroGrob[NULL]
10  2 ( 8- 8, 6- 6)     spacer                                 zeroGrob[NULL]
11 10 ( 5- 5, 5- 5)     xlab-t                                 zeroGrob[NULL]
12 11 ( 9- 9, 5- 5)     xlab-b titleGrob[axis.title.x.bottom..titleGrob.3011]
13 12 ( 7- 7, 3- 3)     ylab-l   titleGrob[axis.title.y.left..titleGrob.3014]
14 13 ( 7- 7, 7- 7)     ylab-r                                 zeroGrob[NULL]
15 14 ( 4- 4, 5- 5)   subtitle         zeroGrob[plot.subtitle..zeroGrob.3016]
16 15 ( 3- 3, 5- 5)      title            zeroGrob[plot.title..zeroGrob.3015]
17 16 (10-10, 5- 5)    caption          zeroGrob[plot.caption..zeroGrob.3018]
18 17 ( 2- 2, 2- 2)        tag              zeroGrob[plot.tag..zeroGrob.3017]

> gtable::gtable_show_layout(gt)

plot

要获取各个geom图层的高度,我们可以更深入地研究面板对象的子对象:

> gt$grobs[[which(gt$layout$name == "panel")]]$children
(gTree[grill.gTree.2992], zeroGrob[NULL], rect[geom_rect.rect.2978], 
points[geom_point.points.2980], text[GRID.text.2981], zeroGrob[NULL], 
zeroGrob[panel.border..zeroGrob.2982]) 

在这种情况下,我们知道(因为该示例是通过这种方式创建的),令人讨厌的geom是文本层,因此我们可以直接转到第5个子对象,并查看其中的高度:

> gt$grobs[[which(gt$layout$name == "panel")]]$children[[5]]$y
[1] 2.3125native 2.3125native 2.3125native 2.3125native 2.3125native 2.3125native
[7] 2.3125native 2.3125native 2.3125native 2.3125native 2.3125native 2.3125native
...

参考网格包中的?unit帮助文件,“本机”坐标系意味着测量是相对于视口的xscale和yscale。因此,2.3125native可以解释为2.3125 x面板高度(4cm)= 9.25cm。

更一般而言,要获取两个方向的高度限制:

# rect grobs such as those created by geom_bar() have "height" / "width" measurements,
# while point & text grobs have "y" / "x" measurements, & we look for both
max.grob.heights <- sapply(gt$grob[[which(gt$layout$name == "panel")]]$children,
                       function(x) ifelse(!is.null(x$height) & "unit" %in% class(x$height),
                                          max(as.numeric(x$height)),
                                          ifelse(!is.null(x$y) & "unit" %in% class(x$y),
                                                 max(as.numeric(x$y)),
                                                 0)))
max.grob.heights = max(max.grob.heights)

min.grob.heights <- sapply(gt$grob[[which(gt$layout$name == "panel")]]$children,
                           function(x) ifelse(!is.null(x$height) & "unit" %in% class(x$height),
                                              min(as.numeric(x$height)),
                                              ifelse(!is.null(x$y) & "unit" %in% class(x$y),
                                                     min(as.numeric(x$y)),
                                                     0)))
min.grob.heights = min(min.grob.heights)

# identify panel row & calculate panel height
panel.row <- gt$layout[gt$layout$name == "panel", "t"] # = 7
panel.height <- as.numeric(grid::convertUnit(gt$heights[panel.row],"mm"))

如果只希望包括所有几何图层的面板组件的高度(并且不在乎它们与整个grob对象的组合高度的关系),则可以将max / min grob高度用作面板的乘数高度:

panel.multiplier <- max(1, max.grob.heights) + abs(min.grob.heights)
result <- panel.multiplier * panel.height

如果要计算绘图的整体高度,则必须分别比较顶部/底部边界对象的高度:如果它们在绘图范围之内,请使用原始高度;如果超过该高度,请改用其高度。

# calculate height of all the grobs above the panel
height.above.panel <- gt$heights[1:(panel.row - 1)]
height.above.panel <- sum(as.numeric(grid::convertUnit(height.above.panel, "mm")))

# check whether the out-of-bound object (if any) exceeds this height, & replace if necessary
if(max.grob.heights > 1){
  oob.height.above.panel <- (max.grob.heights - 1) * panel.height
  height.above.panel <- max(height.above.panel, oob.height.above.panel)
}

# as above, calculate the height of all the grobs below the panel
height.below.panel <- gt$heights[(panel.row + 1):length(gt$heights)]
height.below.panel <- sum(as.numeric(grid::convertUnit(height.below.panel, "mm")))

# as above
if(min.grob.heights < 0){
  oob.height.below.panel <- abs(min.grob.heights) * panel.height
  height.below.panel <- max(height.below.panel, oob.height.below.panel)
}

# sum the result
result <- height.above.panel + panel.height + height.below.panel