2

The following produces a 2 x 2 facetted plot, with different x and y ranges on each of the facets:

set.seed(1)
n = 4
x = runif(n)
y = runif(n)
df = data.frame(x,y)
cv = c(0,0.5,1)
df$facetx = letters[cut(df$x,cv)]
df$facety = letters[cut(df$y,cv)]

base = ggplot(data=df,aes(x,y)) + 
  geom_point() +
  facet_grid(facetx~facety,scales='free')
base

Now I want to place a text / label on each of the facets, like so:

df.labs       = expand.grid(facetx=c('a','b'),facety=c('a','b')); 
df.labs$label = sprintf("Label %s",1:nrow(df.labs))
base + 
  geom_text(data = df.labs,x=0.5,y=0.5,aes(label=label))

How can I define the positioning of the text / label, so that it is PRECISELY some fraction of the distance along the facets, say at x = 0.75 an y = 0.75 (three-quarters horizontally, and three-quarters vertically).

In other words, I want it to be positioned relative to the facet viewport, not the values of the points rendered within the viewport.

Nicholas Hamilton
  • 10,044
  • 6
  • 57
  • 88
  • `annotation_custom()` would help, as you [could position a textGrob within a grobTree filling the panel](http://stackoverflow.com/a/32832732/471093). However [Hadley decided that annotations should always be the same for all panels](https://github.com/hadley/ggplot2/issues/1399#issuecomment-155156602), and it's not clear how to make a `geom_` version of `annotation_custom`. – baptiste Oct 16 '16 at 23:51
  • alternatively, use `ggplot_build` to find all the relevant axes limits, then convert the relative positions to absolute ones. – baptiste Oct 16 '16 at 23:52
  • @baptiste created fractional positional adjustment function, see my answer below. – Nicholas Hamilton Oct 17 '16 at 00:46

1 Answers1

4

So I ended up creating a 'fraction' positional adjustment, as such:

position_fraction <- function(x=0.5,y=0.5) {
ggplot2:::ggproto(NULL, PositionFraction,
                  x = x,
                  y = y)
}

PositionFraction <- ggplot2:::ggproto("PositionFraction", ggplot2::Position,
x = 0.5,
y = 0.5,
required_aes = c("x", "y"),
setup_params = function(self, data) {
  list(x = self$x, 
       y = self$y)
},
compute_layer = function(self,data, params, panel) {
  layout = panel$layout
  data  = ddply(data,'PANEL',function(df){
    p   = df$PANEL[1]; ix = which(layout$PANEL == p)[1]
    sx  = panel$x_scales[[ layout$SCALE_X[ix]  ]]
    sy  = panel$y_scales[[ layout$SCALE_Y[ix] ]]
    f   = function(v,s){ lims = s$get_limits(); v*diff(lims) + min(lims) }
    df$x = params$x
    df$y = params$y
    ggplot2:::transform_position(df, function(x) f(x,sx), function(y) f(y,sy))
  })
  data
}

)

Which in the case of my original question, should be called like this:

base + 
  geom_text(data = df.labs,aes(label=label),position=position_fraction(.75,.75))

fractional example

Nicholas Hamilton
  • 10,044
  • 6
  • 57
  • 88