Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
417 views
in Technique[技术] by (71.8m points)

r - npc coordinates of geom_point in ggplot2

How can I get the x, y coordinates of a geom_point in a ggplot, where the reference frame is the entire plotted image?

I can create a ggplot with some geom_points using:

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

This gives:

enter image description here

By converting this into a grob, I can extract some additional information about this ggplot, like the coordinates with respect to the plot panel, marked by the purple arrow. However, this ignores the space taken up by the axes.

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

How can I get the values of the x, y coordinates when I start measuring from the bottom-left corner of the entire image, marked by the green arrow?

If it's possible, I would like the solution to take into account the theme of the ggplot. Adding a theme like + theme_void() affects the axes and also shifts the location of the points with respect to the entire plotted image.

Update: I realised that the font size of the axes changes depending on the width and height of the plot, affecting the relative size of the plot panel. So it won't be trivial to provide the location in npc units without defining the plot width and plot height. If possible, give the location of the geom_points as a function of the plot width and plot height.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

When you resize a ggplot, the position of elements within the panel are not at fixed positions in npc space. This is because some of the components of the plot have fixed sizes, and some of them (for example, the panel) change dimensions according to the size of the device.

This means that any solution must take the device size into account, and if you want to resize the plot, you would have to run the calculation again. Having said that, for most applications (including yours, by the sounds of things), this isn't a problem.

Another difficulty is making sure you are identifying the correct grobs within the panel grob, and it is difficult to see how this could easily be generalised. Using the list subset functions [[6]] and [[3]] in your example is not generalizable to other plots.

Anyway, this solution works by measuring the panel size and position within the gtable, and converting all sizes to milimetres before dividing by the plot dimensions in milimetres to convert to npc space. I have tried to make it a bit more general by extracting the panel and the points by name rather than numerical index.

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])
}

Now we can test it with your example:

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

And we can confirm these values are correct by plotting some point grobs over your red points, using our values as npc co-ordinates:

my.plot
grid::grid.draw(pointsGrob(x = my.points$x, y = my.points$y, default.units = "npc"))

Created on 2020-03-25 by the reprex package (v0.3.0)


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...