Search code examples
rggplot2positioning

Place text at a fixed distance from chart


Please help me find a universal way to place text at a constant distance from the right side of the plot area as illustrated below. Since the plot area is narrower on the right, unfortunately, the current placement calculation positions the text too far to the left. Note the text is right-aligned.

My last idea: If we knew the distance in centimeters between say x=1 and x=2, we could calculate the position easily. Unfortunately, it's apparently not that easy to get that distance.

FYI: I am not looking to label the lines.

enter image description here Full-size image

library(ggplot2)
library(reshape)
library(gridExtra)

df = data.frame(x =(1:3),One=c(12, 8, 13),Two=c(13, 7, 11),Three=c(11, 9, 11))
df.melt = melt(df, id.vars="x")
xmax = max(df.melt$x); xmin = min(df.melt$x)
ymax = max(df.melt$value); ymin = min(df.melt$value)

dfa = data.frame(x=(xmax-xmin)*1.15+xmin, y=c(11,12,13.5), ann=c("10.1|","1.1|","Texttexttext|"))
dfa.melt = melt(dfa, id.vars=c("x","ann"))

p = ggplot() + 
  geom_line(data=df.melt,  aes(x=x, y=value, color=variable), show_guide=F) +
  geom_text(data=dfa.melt, aes(x=x, y=value, hjust = 1, label=ann), size=3) + 
  coord_cartesian(xlim=c(xmin,xmax), ylim=c(ymin,ymax))

p1 = p + theme(plot.margin=unit(c(1,3,0,0),"cm"), axis.text.y=element_text(size=10))
p2 = p + theme(plot.margin=unit(c(1,3,0,3),"cm"), axis.text.y=element_text(size=35))
p1c <- ggplot_gtable(ggplot_build(p1))
p1c$layout$clip[p1c$layout$name=="panel"] <- "off"
p2c <- ggplot_gtable(ggplot_build(p2))
p2c$layout$clip[p2c$layout$name=="panel"] <- "off"
grid.arrange(p1c, p2c, ncol=2)

Solution

  • Because everything is named, it is possible to access any component of the plot using the grid functions.

    The problem is ggplot2 creates many viewports and grobs when it draws a plot. So it is is not easy to make changes to this plot. I notice also that the name of grobs and viewports changes for each new execution of the plot. So I tried to get viewports by some criteria (here depth = 4)

    #Get the viewports: 
    scene.vps <- grid.ls(viewports=T,grobs=F)
    scene.flat <- as.data.frame(do.call('cbind',scene.vps))
    vps <- subset(scene.flat ,vpDepth == '4')$name[1:2]
    vps <- as.character(vps)
    
    
    # modify the plot by grid.text
    depth <- downViewport(vps[1])
    grid.text("1.1|",x=unit(1, "npc") - unit(1, "mm"),
                     y=unit(1, "npc") - unit(20, "mm"),
                     just=c("right", "top"))
    
    grid.text("10.1|",x=unit(1, "npc") - unit(1, "mm"),
                      y=unit(1, "npc") - unit(60, "mm"),
                      just=c("right", "top"))
    
    grid.text("Texttexttext|",x=unit(1, "npc") - unit(10, "mm"),
                              y=unit(1, "npc") -unit(2,'mm'),
              just=c("right", "top"))
    
    upViewport(depth)  # come back to the root viewport
    
    depth <- downViewport(vps[2])
    
    grid.text(...
    

    enter image description here