ggplot2 中 geom_point 的 npc 坐标

11

我如何在ggplot中获取geom_pointxy坐标,参考框架是整个绘图图像?

我可以使用以下代码创建一些包含geom_pointggplot

library(ggplot2)

my.plot <- ggplot(data.frame(x = c(0, 0.456, 1), y = c(0, 0.123, 1))) +
             geom_point(aes(x, y), color = "red")

这样做会得到:

enter image description here

将其转换为grob后,我可以提取有关此ggplot的其他信息,例如相对于图面板的坐标(由紫色箭头标记)。但是,这将忽略坐标轴占用的空间。

my.grob <- ggplotGrob(my.plot)
my.grob$grobs[[6]]$children[[3]]$x
# [1] 0.0454545454545455native 0.46native 0.954545454545454native 
my.grob$grobs[[6]]$children[[3]]$y
# [1] 0.0454545454545455native 0.157272727272727native 0.954545454545454native

如何从整个图像左下角(绿箭头标记)开始测量时获取xy坐标的值?

如果可能的话,我希望解决方案可以考虑ggplottheme。添加theme,例如+ theme_void()会影响轴,并使得点相对于整个绘图图像的位置发生变化。

更新:我意识到轴的字体大小取决于绘图的宽度和高度,这会影响plot panel的相对大小。因此,如果不定义plot widthplot height,提供npc单位的位置不会很容易。如果可能的话,请将geom_points的位置作为plot widthplot height的函数给出。


5
为什么会有两个反对票? - markus
2
是的,请在评论中提出您的疑虑或建议。 - LBogaardt
1
请考虑到图形本身没有固定的尺寸——绝对点位置不仅取决于您打印到的设备的尺寸,还取决于您轴的比例。因此,我很好奇您需要它做什么。 - tjebo
1
关于这些负评,我不需要为我的问题辩护,它在SE上是一个有效的问题。为什么我想知道?因为我就是想知道。 - LBogaardt
1
这个也是。但从未得到真正的答案。https://stackoverflow.com/questions/48710478/getting-npc-coordinates-of-ggplot2-grob - tjebo
显示剩余5条评论
1个回答

7
当你调整ggplot的大小时,面板内元素的位置不是在npc空间中固定的位置。这是因为绘图的一些组件具有固定的大小,而其中一些组件(例如面板)根据设备的大小而改变尺寸。
这意味着任何解决方案都必须考虑设备的大小,并且如果您想调整绘图的大小,您需要重新运行计算。话虽如此,对于大多数应用程序(包括您的应用程序),这并不是一个问题。
另一个困难是确保您正确地识别面板grob中的正确grob,而且很难看出这个问题如何容易地被泛化。在您的示例中使用列表子集函数[[6]][[3]]并不能推广到其他绘图。
无论如何,这个解决方案通过测量gtable中的面板大小和位置,并将所有尺寸转换为毫米,然后再除以毫米单位的绘图尺寸来转换为npc空间。我试图通过按名称提取面板和点而不是数字索引来使其更加通用。
library(ggplot2)
library(grid)
require(gtable)

get_x_y_values <- function(gg_plot)
{
  img_dim      <- grDevices::dev.size("cm") * 10
  gt           <- ggplot2::ggplotGrob(gg_plot)
  to_mm        <- function(x) grid::convertUnit(x, "mm", valueOnly = TRUE)
  n_panel      <- which(gt$layout$name == "panel")
  panel_pos    <- gt$layout[n_panel, ]
  panel_kids   <- gtable::gtable_filter(gt, "panel")$grobs[[1]]$children
  point_grobs  <- panel_kids[[grep("point", names(panel_kids))]]
  from_top     <- sum(to_mm(gt$heights[seq(panel_pos$t - 1)]))
  from_left    <- sum(to_mm(gt$widths[seq(panel_pos$l - 1)]))
  from_right   <- sum(to_mm(gt$widths[-seq(panel_pos$l)]))
  from_bottom  <- sum(to_mm(gt$heights[-seq(panel_pos$t)]))
  panel_height <- img_dim[2] - from_top - from_bottom
  panel_width  <- img_dim[1] - from_left - from_right
  xvals        <- as.numeric(point_grobs$x)
  yvals        <- as.numeric(point_grobs$y)
  yvals        <- yvals * panel_height + from_bottom
  xvals        <- xvals * panel_width + from_left
  data.frame(x = xvals/img_dim[1], y = yvals/img_dim[2])
}

现在我们可以用你的例子来测试一下:
my.plot <- ggplot(data.frame(x = c(0, 0.456, 1), y = c(0, 0.123, 1))) +
             geom_point(aes(x, y), color = "red")

my.points <- get_x_y_values(my.plot)
my.points
#>           x         y
#> 1 0.1252647 0.1333251
#> 2 0.5004282 0.2330669
#> 3 0.9479917 0.9442339

我们可以通过在您的红点上绘制一些点图形,使用我们的值作为npc坐标来确认这些值是正确的。
my.plot
grid::grid.draw(pointsGrob(x = my.points$x, y = my.points$y, default.units = "npc"))

enter image description here

2020-03-25由reprex package (v0.3.0)创建


1
这个 sum(to_mm(gt$heights[seq(panel_pos$t - 1)])) 部分很奇怪。你能解释一下你在这里求和的内容吗? - LBogaardt
1
@LBogaardt panel_pos$t 给出了(灵活)面板所在网格的行数,因此 seq(panel_pos$t -1) 是所有位于其上方的行号,因此 sum(to_mm(gt$heights[seq(panel_pos$t - 1)])) 是这些行高度的总和。 - Allan Cameron
@LBogaardt 我试图在简洁和清晰之间取得平衡,但也许我还没有做到完全正确... - Allan Cameron
啊,所以ggplot就像是一个表格,有一个列用于y轴标签,一个列用于y轴刻度等等。同样地,每一行都有一个宽度/高度。面板就是这个表格中的一个单元格?明白了,谢谢Allan。 - LBogaardt
1
确切地说,@LBogaardt。事实上,ggplotGrob生成一个gTable,即grob表格。 - Allan Cameron

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接