Similar to this question, I'm trying to add a, b, c, ... to a grid of facets so they can be referenced in individually elsewhere. With scales = 'fixed'
, this is relatively easy, as you can even hardcode the x,y coordinates for a geom_text
label. However, with scales = 'free'
, it's a pain to compute all the x,y coordinates for each facet so they labels end up in the same location, visually. Can this be done automatically?
CodePudding user response:
A pure vanilla ggplot2 solution would be to use Inf
, -Inf
to snap text to the limits of each panel. To automatically get the labels you can use after_stat()
to grab the PANEL internal variable. If you plot it as a label, you can have control over the offset from the panel edges by hiding the label itself and setting label.padding
.
library(ggplot2)
x = data.frame(a=c('a','b','a','b'),b=c(1,1,2,2),v=runif(8))
ggplot(x,aes(x=v,y=v))
geom_point()
facet_grid('a~b',scales='free')
geom_label(
aes(x = -Inf, y = Inf, label = after_stat(ifelse(
duplicated(PANEL), "", letters[as.numeric(PANEL)]
))),
vjust = 1, hjust = 0,
fill = NA, label.size = 0, # Don't show box
label.padding = unit(5, "mm") # Control margins to panel bounds
)
If that spacing mechanism seems to finnicky to you, you can use the ggpp::geom_text_npc()
function to directly set relative coordinates for the text labels.
x = data.frame(a=c('a','b','a','b'),b=c(1,1,2,2),v=runif(8))
ggplot(x,aes(x=v,y=v))
geom_point()
facet_grid('a~b',scales='free')
ggpp::geom_text_npc(
aes(npcx = 0.05, npcy = 0.95,
label = after_stat(
ifelse(duplicated(PANEL), "", letters[as.numeric(PANEL)])
))
)
Created on 2022-10-18 by the reprex package (v2.0.1)
CodePudding user response:
Here is a solution with letters
as default, but allowing other labels too, and ...
is also passed to geom_text
.
library(ggplot2)
# main solution
add.letters = function(g,px,py,lab=NULL,...){
data = ggplot_build(g)$layout$layout
if (is.null(lab)){ data$lab = letters[1:nrow(data)] } else { data$lab = lab }
p.range = function(r,p){ r[1] (r[2]-r[1])*p }
data = do.call(rbind,lapply(1:nrow(data),function(i){
ls = layer_scales(g,data[i,]$ROW,data[i,]$COL)
data.i = cbind(data[i,],
x = p.range(ls$x$range$range,px),
y = p.range(ls$y$range$range,py))
}))
g = g geom_text(data=data,aes(label=lab,x=x,y=y),...)
}
# mwe
x = data.frame(a=c('a','b','a','b'),b=c(1,1,2,2),v=runif(8))
g = ggplot(x,aes(x=v,y=v))
geom_point()
facet_grid('a~b',scales='free')
g = add.letters(g,.05,.95,lab=c('a','bb','ccc','dddd'),hjust='left')
print(g)