Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

npc coordinates of geom_point in ggplot2

Tags:

r

ggplot2

grob

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.

like image 274
LBogaardt Avatar asked Mar 22 '20 18:03

LBogaardt


People also ask

What is Geom_point in Ggplot?

The point geom is used to create scatterplots. The scatterplot is most useful for displaying the relationship between two continuous variables.

Is Geom_point a function?

The function geom_point() adds a layer of points to your plot, which creates a scatterplot. ggplot2 comes with many geom functions that each add a different type of layer to a plot.


Video Answer


1 Answers

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)

like image 151
Allan Cameron Avatar answered Sep 28 '22 11:09

Allan Cameron