关于 r:如何手动将具有固定纵横比的视口放入其父级中,这样不会像 ggplot 那样浪费空间? | 珊瑚贝

How do I manually fit a viewport with a fixed aspect ratio into its parent such that no space is wasted like ggplot can do?


我有一个视口,它必须具有固定的纵横比,因为它在其原生坐标系中的 x 和 y 单位之间的距离必须相等。

我想将此视口调整到父视口中,以便它可以最大程度地缩放,但保持其纵横比。

使用网格单元”snpc”,我能够保持纵横比,尽管我无法达到最大程度。请参阅下面的代码,它打印出我到目前为止以四种不同的设备纵横比归档的内容。

Plot

虽然感兴趣的视口(灰色和网格)在设备宽度较小时填充了最大可用区域,但如果设备宽度变得如此之大以至于设备高度成为视口大小的限制因素,则该方法会失败。视口没有覆盖整个可能的高度。我希望感兴趣的视口覆盖最右边图中的整个设备高度。

编辑:我发现 ggplot 可以做到这一点,并更新了我的示例以显示这一点。注意ggplot如何触摸最右边图像的上下设备边框和最左边图像的左右边框,为什么我的自制解决方案即使有空间也不会触摸最右边图像的上下设备边框.但是,我不能使用 ggplot,因为我想包含一个仅使用网格构建的自定义绘图,但它依赖于原生 x 和 y 坐标系上的相等距离??。

ggplot

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# — Helper functions ——————————————————

# Draw something (inside fun) for different paper sizes
forDifferentSizes <- function(names, width, height, fun, …){
  cyc <- function(x, along) rep_len(x, length(along))
  mapply( names, cyc(width, names), cyc(height, names)
        , FUN = function(n, w, h){
            png(paste0(n,’.png’), width = w, height = h, …)
            on.exit(dev.off())
            fun(n, w, h)
        })
}

# — Own attempt ———————————————————–
library(grid)

# Coordinate system
x <- c(1,6)
y <- c(1,4)
range <- c(diff(x), diff(y))
dims <- range / max(range)

annot <- function(name){
  grid.rect(gp = gpar(fill = NA))
  grid.text( name,unit(1, ‘npc’),unit(0,’npc’), just = c(1,0))
}

forDifferentSizes( paste0(‘X’,letters[1:4]), seq(100, 500, length.out = 4), 250
  , fun = function(…){
  grid.newpage()

  pushViewport(
    viewport( width  = unit( dims[1], ‘snpc’)
              , height = unit( dims[2], ‘snpc’)
              , xscale = x
              , yscale = y
    )
  )
  annot(‘vp2’)
  grid.grill(v = x[1]:x[2], h = y[1]:y[2], default.units = ‘native’)
})

# — ggplot2 can do it —————————————————–

library(ggplot2)
data(“mtcars”)

forDifferentSizes(paste0(‘G’,letters[1:4]), seq(100, 500, length.out = 4), 250
  , pointsize = 8
  , fun = function(…){
  p <- ggplot(mtcars) + aes(x = drat, y = mpg) + geom_point() +
    theme(aspect.ratio = dims[2]/dims[1])
  print(p)
})

# — Make the output images for post (imagemagick required) —————
system(‘convert G*.png -bordercolor black -border 1×1 +append G.png’)
system(‘convert X*.png -bordercolor black -border 1×1 +append X.png’)

  • 您将 vp1 设置为每边的间距为 2cm。在所示示例中,vp2 很好地填充了 vp1。 vp2 不能大于 vp1,因为它位于 vp2 内部。你希望这如何工作?
  • @aocall 图片与代码不匹配。即使没有视口 vp1,该行为仍然存在。我现在更正了(删除了视口 vp1)。我很抱歉造成混乱
  • 我不知道 snpc 是否可行。如文档中所述,snpc”使用 npc-width 和 npc-height 中的较小者”。因此,由于 npc-height 小于 npc-width,它使用 npc-height 来计算视口大小。
  • @aocall你是绝对正确的。不幸的是,’snpc’ 是迄今为止我可以在网格中找到的唯一一种机制来制作具有固定纵横比的视口……所以 ggplot 以一种我迄今为止无法找到的非常聪明的方式使用这种机制,或者另一个我不知道的。


ggplot2 使用具有空单位的网格布局和 respect 参数来强制纵横比。这是一个例子,

1
2
3
4
5
6
7
library(grid)

ar <- (1+sqrt(5))/2
gl <- grid.layout(1,1,widths=unit(1,”null”), height=unit(1/ar,”null”), respect = TRUE)
grid.newpage()
grid.rect(vp=vpTree(viewport(layout = gl),
                    vpList(viewport(layout.pos.row = 1, layout.pos.col = 1))))


user9169915 做到了!惊人的!我在这里以程序网格样式发布他的解决方案,以供参考。另外,我添加了等距坐标系。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
ar <- (1+sqrt(5))/2 # aspect ratio
# Native coordinate system of the target viewport: make x and y equidistant
xrange <- c(0,5)
yrange <- xrange/arN

forDifferentSizes( paste0(‘L’,letters[1:4]), seq(100, 500, length.out = 4), 250
  , fun = function(…){

  gl <- grid.layout(1,1,widths=unit(1,”null”), height=unit(1/ar,”null”), respect = TRUE)
  grid.newpage()
  pushViewport(viewport(layout = gl))
  annot(‘vp1’) # see question for definition
  pushViewport(viewport(layout.pos.row = 1, layout.pos.col = 1,
                        xscale = xrange, yscale = yrange))
  annot(‘vp2’)
  grid.grill(h=0:floor(yrange[2]), v=0:floor(xrange[2]), default.units = ‘native’)
  popViewport(2)

})

plot


来源:https://www.codenong.com/47721895/

微信公众号
手机浏览(小程序)
0
分享到:
没有账号? 忘记密码?