use npc units in annotate()

让人想犯罪 __ 提交于 2021-01-19 06:41:07

问题


I have a ggplot object. I would like to add some text with annotate(), and I would like to specify the coordinates of the text in npc units. Is this possible?

This minimal example demonstrates how text is ordinarily positioned with annotate():

library(ggplot2)
p <- ggplot(mtcars, aes(mpg, wt)) + geom_point()
p + annotate("text", x = 30, y = 4.5, label = "hello")

I would like to achieve the same effect, but instead of specifying x and y in native coordinates, I would like to specify them with npc coordinates. For the purpose of this example, I am not worried about exactly translating x = 30 and y = 4.5 into npc units. I just want to know whether npc units can be used in annotate() at all.

There are two related strategies, but they're not entirely satisfactory:

  1. One can use npc units by specifying them to, say, grid::textGrob(). And one can then place the grob with annotation_custom(), as in this answer by @baptiste. But this solution is a bit more cumbersome than I would like.

  2. The "ggpmisc" package includes geom_text_npc(). But it doesn't yet work with annotate(). That is, annotate("text_npc", ...) doesn't seem to work. [Edit: it now works. See Pedro Aphalo's message below.]

There are also many related posts. In particular, Greg Snow has suggested using grid to create a viewport with the dimensions of p and then annotating that viewport. And @teunbrand has suggested a method that entails converting p to a "gtable" object (with ggplotGrob()) and then drawing that "gtable" object. Either of these strategies can probably be adapted to my purposes. But is there a more straightforward way to use npc coordinates with annotate()?


回答1:


Personally, I would use Baptiste's method but wrapped in a custom function to make it less clunky:

annotate_npc <- function(label, x, y, ...)
{
  ggplot2::annotation_custom(grid::textGrob(
    x = unit(x, "npc"), y = unit(y, "npc"), label = label, ...))
}

Which allows you to do:

p + annotate_npc("hello", 0.5, 0.5)

Note this will always draw your annotation in the npc space of the viewport of each panel in the plot, (i.e. relative to the gray shaded area rather than the whole plotting window) which makes it handy for facets. If you want to draw your annotation in absolute npc co-ordinates (so you have the option of plotting outside of the panel's viewport), your two options are:

  1. Turn clipping off with coord_cartesian(clip = "off") and reverse engineer the x, y co-ordinates from the given npc co-ordinates before using annotate. This is complicated but possible
  2. Draw it straight on using grid. This is far easier, but has the downside that the annotation has to be drawn over the plot rather than being part of the plot itself. You could do that like this:
annotate_npc_abs <- function(label, x, y, ...) 
{
  grid::grid.draw(grid::textGrob(
    label, x = unit(x, "npc"), y = unit(y, "npc"), ...))
}

And the syntax would be a little different:

p 
annotate_npc_abs("hello", 0.05, 0.75)




回答2:


As of 'ggpmisc' (>= 0.3.6) the following code works as expected (in CRAN as of 2020-09-10).

library(ggpmisc)
p <- ggplot(mtcars, aes(mpg, wt)) + geom_point()
# default justification is "inward"
p + annotate("text_npc", npcx = 0.8, npcy = 0.75, label = "hello")
# same justification as default for "geom_text()"
p + annotate("text_npc", npcx = 0.8, npcy = 0.75, label = "hello",
             hjust = "center", vjust = "middle")


来源:https://stackoverflow.com/questions/63741925/use-npc-units-in-annotate

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!