I seem unable to correctly color axis text on a faceted plot when the scales
parameter is set to "free"
. Consider the following dataset:
library( ggplot2 )
X <- data.frame( V1 = LETTERS, V2 = runif( 26 ),
V3 = rep( c("F1", "F2"), each = 13 ) )
We can plot the data on a single facet, highlighting the letters D, O, T as follows:
v <- ifelse( X$V1 %in% c( "D", "O", "T" ), "red", "black" )
g <- ggplot( X, aes( x = V1, y = V2 ) ) + geom_point() +
theme( axis.text.x = element_text( color = v ) )
Making the plot faceted using the default scales = "fixed"
correctly highlights D, O, T on both facets.
g + facet_wrap( ~V3 )
However, switching the scales
parameter to "free"
leads to unexpected behavior, where only D and Q are highlighted.
g + facet_wrap( ~V3, scales = "free" )
My question: is this a bug or do I need to somehow modify my definition of v
to account for free scales. If it is a bug, does anybody know of a workaround to highlight specific axis text in each (free-scaled) facet?
EDIT: Own answer moved to answers, as suggested by Henrik.
I don't think it's a bug. The problem is that
v
here is basically a string of characters, length 26, which defines colours for the first 26 breaks on the x-axis. When the x-axis has 26 breaks exactly, well & good; when it has less than that (which is the case when you setscales="free"
), it simply restarts at the beginning for each axis. Q is red here because it's in the fourth position in the second plot, although thev[4]
's red was meant for D, in the first plot.Based on what I've tried & read here on SO, one can't map aesthetics into
theme()
, which controls the appearance of axis text in ggplot.It's possible to hack a solution by hiding the axis & using
geom_text()
instead to simulate an axis, since the latter does accept aesthetics mapped from the data. It may not be very elegant, though:After digging through the graphical objects (grobs) associated with the plot, I came across a potential hack to get around the issue. While not as elegant as Z.Lin's solution, I wanted to share it for educational purposes.
We begin by retrieving grobs with
Grobs are hierarchical objects and the general rules for traversing these structures fall into two categories:
gtable
(asgt
above), accessing individual grobs that go into the table can be done through$grobs
.gtable
, its children grobs can be accessed through$children
.Looking at the
gtable
above, we observe that grobs 6 and 7 correspond to the bottom axes of facets 1 and 2, respectively. Each of these axis grobs is of typeabsoluteGrob
, so using the two rules above, we can examine what they are made up of like this:Noting that the second child is a
gtable
, we can continue descending the hierarchy of grobs until we arrive atgt$grobs[[6]]$children[[2]]$grobs[[2]]$children[[1]]
, which is a leaf of the grob hierarchy (its$children
isNULL
) and corresponds to the axis text. Let's examine its graphical parameters, which can be accessed through$gp
:Note that the
col
attribute is of length 26 and corresponds exactly to thev
variable from the question. If we look at the bottom axis of the second facet (gt$grobs[[7]]$...
), we see that the samecol
value is used there as well, leading to identical axis text coloring in both facets (as suggested in Z.Lin's solution).Consequently, setting these color setting to only be the corresponding portions of
v
"by hand" allows us to modify the original plot and achieve the desired result.