Varying axis labels formatter per facet in ggplot/R
Asked Answered
M

1

26

I have a dataframe capturing several measures over time that I would like to visualize a 3x1 facet. However, each measure contains different units/scales that would benefit from custom transformations and labeling schemes.

So, my question is: If the units and scales are different across different facets, how can I specify a custom formatter or transformation (i.e., log10) to a particular axis within a facet?

For example, let's say I have the data:

df = data.frame(dollars=10^rlnorm(50,0,1), counts=rpois(50, 100))
melted.df = melt(df, measure.var=c("dollars", "counts"))

How would one go upon setting up a 2x1 facet showing dollars and counts over the index with labels=dollars and scale_y_continuous(trans = "log10", ...) for the df$dollars data?

Thank you!

Mozell answered 20/7, 2012 at 19:40 Comment(2)
Yeah. Shucks. I just came across this link where someone had a similar issue: comments.gmane.org/gmane.comp.lang.r.ggplot2/4496.Mozell
Probably easier to do two separate plots and arrange them together.Cajeput
Z
47

As you discovered, there isn't an easy solution to this, but it comes up a lot. Since this sort of thing is asked so often, I find it helpful to explain why this is hard, and suggest a potential solution.

My experience has been that people coming to ggplot2 or lattice graphics fundamentally misunderstand the purpose of faceting (or trellising, in lattice). This feature was developed with a very specific idea in mind: the visualization of data across multiple groups that share a common scale. It comes from something called the principle of small multiples, espoused by Tufte and others.

Placing panels next to each other with very different scales is something that visual design experts will tend to avoid, because it can be at best misleading. (I'm not scolding you here, just explaining the rationale...)

But of course, once you have this great tool out in the open, you never know how folks are going to use it. So it gets stretched: the requests come in for the ability to allows the scales to vary by panel, and to set various aspects of the plot separately for each panel. And so faceting in ggplot2 has been expanded well beyond its original intent.

One consequence of this is that some things are difficult to implement simply due to the original design intent of the feature. This is likely one such instance.

Ok, enough explanation. Here's my solution.

The trick here is to recognize that you aren't plotting graphs that share a scale. To me, that means you shouldn't even be thinking of using faceting at all. Instead, make each plot separately, and arrange them together in one plot:

library(gridExtra)

p1 <- ggplot(subset(melted.df,variable == 'dollars'),
                aes(x = value)) + 
            facet_wrap(~variable) + 
            geom_density() + 
            scale_x_log10(labels = dollar_format())

p2 <- ggplot(subset(melted.df,variable == 'counts'),
                aes(x = value)) + 
            facet_wrap(~variable) + 
            geom_density()

grid.arrange(p1,p2)

enter image description here

I've just guessed at what geom_* you wanted to use, and I'm sure this isn't really what you wanted to plot, but at least it illustrates the principle.

Zaidazailer answered 20/7, 2012 at 20:20 Comment(10)
Thank you for the insightful response! I completely understand the implications of straying away from the original design intentions for ggplot2. Practically speaking, I have two data sets where one follows a normal distribution while the other follows a log-normal distribution, so I was hoping to be able to compare that visually-normalized data across time. Do you have any insights on properly aligning the plot areas? I've come across the ggExtra library before, which supposedly provides some ad-hoc support for that, but I'd appreciate any direction with that.Mozell
@StefanNovak I'm glad it was helpful! I want to emphasize again that I wasn't criticizing your design choices. One principle of SO questions is that they "live forever" and so should help serve more than just the original asker. As I said, this comes up over and over, so my explanation was more directed at future readers than you specifically.Zaidazailer
@StefanNovak Well, the hacky way to do it is to adjust the axis tick labels such that they have the same number of digits, even if that means padding the labels with blank spaces. I'm sure that's been asked before too...I'll see if I can find anything relavent.Zaidazailer
Thanks again! I'm able to use grid.arrange in combination with plot.margin to shift around the alignment of each subplot.Mozell
@joran: the facet wrap calls are unnecessary, aren't they?Cambodia
@Cambodia I find the faceting a single panel a convenient way to add a 'title' thats visually appealing and doesnt take up much space.Zaidazailer
@joran: Ahh, nice one, although I suspect there is a more "proper" way to do it, somehow...Cambodia
@Cambodia I fail to see anything improper about it. I just happen to find it appealing, aesthetically, but opinions on that will surely differ.Zaidazailer
@joran: I meant proper as in coding - using the more primitive functions that the facet system uses. I agree it's very appealing.Cambodia
This is really helpful for cases where you want a plot that is similar to the example above but with different sets of categorical variables on the x-axis.Rheta

© 2022 - 2024 — McMap. All rights reserved.