Varying axis labels formatter per facet in ggplot/R
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)
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.
R ggplot facet: varying x axis formatting and varying aesthetics by facet
It is easy enough to get the smoother on one panel:
gp<-ggplot(g,aes(x=Date,y=value,group=variable)) +
opts(
panel.background = theme_rect(size = 1, colour = "lightgray"),
panel.grid.minor = theme_blank(),
strip.background = theme_blank(),
axis.title.x = theme_blank(),
axis.title.y = theme_blank()
,strip.text.y = theme_text(size = 12,angle = 0)
)
gp<- gp + geom_line(size=1)
gp <-gp + facet_grid(variable ~ var2, scales="free")
gp<-gp+geom_smooth(data=g[g$var2=="History",],
method=loess,size=1,span=.35,alpha=.005)
gp
And as far changing the appearance of the labels on the x axis, look at the options here: https://github.com/hadley/ggplot2/wiki/+opts()-List
Showing different axis labels using ggplot2 with facet_wrap
In ggplot2_2.2.1 you could move the panel strips to be the y axis labels by using the strip.position
argument in facet_wrap
. Using this method you don't have both strip labels and different y axis labels, though, which may not be ideal.
Once you've put the strip labels to be on the y axis (the "left"), you can change the labels by giving a named vector to labeller
to be used as a look-up table.
The strip labels can be moved outside the y-axis via strip.placement
in theme
.
Remove the strip background and y-axis labels to get a final graphic with two panes and distinct y-axis labels.
ggplot(my.df, aes(x = time, y = value) ) +
geom_line( aes(color = variable) ) +
facet_wrap(~Unit, scales = "free_y", nrow = 2,
strip.position = "left",
labeller = as_labeller(c(A = "Currents (A)", V = "Voltage (V)") ) ) +
ylab(NULL) +
theme(strip.background = element_blank(),
strip.placement = "outside")
Removing the strip from the top makes the two panes pretty close together. To change the spacing you can add, e.g., panel.margin = unit(1, "lines")
to theme
.
ggplot2 Facet_wrap graph with custom x-axis labels?
There are a few ways to do this... but none that are very direct like you are probably expecting. I'll assume that you want to replace the default x axis title with new titles, so we'll go from there. Here's an example from the iris
dataset:
library(ggplot2)
p <- ggplot(iris, aes(Sepal.Length, Sepal.Width)) +
geom_point() +
facet_wrap(~Species)
Use Strip Text Placement
One way to create an axis title specific for each is to use the strip text (also called the facet label). The idea is to position the strip text at the bottom of the facet (usually it's at the top by default) and mess with formatting.
ggplot(iris, aes(Sepal.Length, Sepal.Width)) +
geom_point() +
labs(x=NULL) + # remove axis title
facet_wrap(
~Species,
strip.position = "bottom") + # move strip position
theme(
strip.placement = "outside", # format to look like title
strip.background = element_blank()
)
Here we do a few things:
- Remove axis title
- Move strip text placement to the bottom, and
- Format the strip text to look like an axis title by removing the rectangle around and making sure it is placed "outside" the plot area below the axis ticks
Make your Own Labels with Facet Labels
What about doing what we did above... but making your own labels? You can adjust the strip text labels (facet labels) by setting a named vector as.labeller()
. Otherwise, it's the same changes as above. Here's an example:
my_strip_labels <- as_labeller(c(
"setosa" = "My Setosa",
"versicolor" = "Your versicolor",
"virginica" = "Some other stuff"
))
ggplot(iris, aes(Sepal.Length, Sepal.Width)) +
geom_point() +
labs(x=NULL) +
facet_wrap(
~Species, labeller = my_strip_labels, # add labels
strip.position = "bottom") +
theme(
strip.placement = "outside",
strip.background = element_blank()
)
Keep Facet Labels
What about if you want to keep your facet labels, and just add an axis title below each facet? Well, perhaps you can do that via annotation_custom()
and make some grobs, but I think it might be easier to place those as a text geom. For this to work, the idea is that you add a text geom outside of your plot area and map the label text itself to the facets. You'll need to do this with a separate data frame (to avoid overlabeling), and the data frame needs to contain two columns: one that is labeled the same as the label of your facetting column, and one that is to be used to store our preferred text for the axis title.
Here's something that works:
axis_titles <- data.frame(
Species = c("setosa", "versicolor", "virginica"),
axis_title = c("Setosa's Axis", "Versi's Axis", "Virgin's Axis")
)
p + labs(x=NULL) +
geom_text(
data=axis_titles,
aes(label=axis_title), hjust=0.5,
x=min(iris$Sepal.Length) + diff(range(iris$Sepal.Length))/2,
y=1.7, color='red', fontface='bold'
) +
coord_cartesian(clip="off") +
theme(
plot.margin= margin(b=30)
)
Here we have to do a few things:
- Create the data frame to store our axis titles
- Remove default axis title
- Add a
geom_text()
linked to the new data frame and modify placement. Note I'm mathematically fixing the position to be "in the middle" of the x axis. I manually placed the y value, but you could use an equation there too if you want. - Turn
clip="off"
. This is important, because withclip="on"
it will prevent any geoms from being shown if they are outside the panel area. - Extend the plot margin down a bit so that we can actually see our text.
R ggplot facet_wrap with different y-axis labels, one values, one percentages
I agree with the above comments that facets are really not intended for this use case. Aligning separate plots is the orthodox way to go.
That said, if you already have a bunch of nicely formatted ggplot objects, and really don't want to refactor the code just for axis labels, you can convert them to grob objects and dig underneath the hood:
library(grid)
# Convert from ggplot object to grob object
gp <- ggplotGrob(out_gg)
# Optional: Plot out the grob version to verify that nothing has changed (yet)
grid.draw(gp)
# Also optional: Examine the underlying grob structure to figure out which grob name
# corresponds to the appropriate y-axis label. In this case, it's "axis-l-2-1": axis
# to the left of plot panels, 2nd row / 1st column of the facet matrix.
gp[["layout"]]
gtable::gtable_show_layout(gp)
# Some of gp's grobs only generate their contents at drawing time.
# Using grid.force replaces such grobs with their drawing time content (if you check
# your global environment, the size of gp should increase significantly after running
# the grid.force line).
# This step is necesary in order to use gPath() to generate the path to nested grobs
# (& the text grob for y-axis labels is nested rather deeply inside the rabbit hole).
gp <- grid.force(gp)
path.to.label <- gPath("axis-l-2", "axis", "axis", "GRID.text")
# Get original label
old.label <- getGrob(gTree = gp,
gPath = path.to.label,
grep = TRUE)[["label"]]
# Edit label values
new.label <- percent(as.numeric(old.label))
# Overwrite ggplot grob, replacing old label with new
gp = editGrob(grob = gp,
gPath = path.to.label,
label = new.label,
grep = TRUE)
# plot
grid.draw(gp)
In ggplot/facet_wrap(), how to marke axis Y have different format
One option would be the ggh4x
package which via facetted_pos_scales
allows to set the scales individually for each facet:
library(ggplot2)
library(ggh4x)
ggplot(plot_data, aes(x=mseq,y=amount))+geom_line()+geom_point()+
facet_wrap(.~type,scales='free_y') +
facetted_pos_scales(
y = list(
type == "price" ~ scale_y_continuous(labels = scales::comma_format()),
type == "x_to_price" ~ scale_y_continuous(labels = scales::percent_format())
)
)
Specifying different x-tick labels for two facet groups in ggplot2
The ggplot package's facet options were not designed for varying axis labels / scales across facets (see here for a detailed explanation), but one work around in this instance would be to vary the underlying x-axis variable's values for different facets, & set scales = "free_x"
in facet_wrap()
so that only the relevant values are shown in each facet's x-axis:
library(ggplot2)
library(dplyr)
ggplot(data %>%
mutate(Validation = case_when(Validation == "Recursive" ~ "Recursive",
Method == "Method 1" ~ "100-iterations",
TRUE ~ "10-iterations")),
aes(x = Validation, y = Accuracy, fill = Scenario)) +
geom_boxplot() +
facet_wrap(~ Method, scales = "free_x")
Data:
set.seed(1)
data <- data.frame(
Method = rep(c("Method 1", "Method 2"), each = 100),
Validation = rep(c("Iterations", "Recursive"), times = 100),
Scenario = sample(c("Scenario 1", "Scenario 2", "Scenario 3"), 200, replace = TRUE),
Accuracy = runif(200)
)
x-axis interval labeling for facets
You didn't provide enough data to recreate your graph, but here's my answer
df <- structure(
list(
Year = c(1993, 1994, 1995, 1996, 1997, 1998),
type = c("PS", "PS", "PS", "PS", "PS", "PS"),
measure = c("A",
"A", "A", "A", "A", "A"),
value = c(19034.33, 9550.89, 12934.76,
10779.04, 7433.43, 7955.47)
),
.Names = c("Year", "type",
"measure", "value"),
row.names = c(NA, 6L),
class = "data.frame"
)
library(ggplot2)
ggplot(df, aes(x = Year, y = value, fill = type)) +
geom_area() +
facet_wrap(~ measure) +
scale_x_continuous(breaks = 1993:2016, limits = c(1993, 2016))
How to change facet labels?
Change the underlying factor level names with something like:
# Using the Iris data
> i <- iris
> levels(i$Species)
[1] "setosa" "versicolor" "virginica"
> levels(i$Species) <- c("S", "Ve", "Vi")
> ggplot(i, aes(Petal.Length)) + stat_bin() + facet_grid(Species ~ .)
Related Topics
Text-Mining with the Tm-Package - Word Stemming
Create Barplot from Data.Frame
Rounding Time to Nearest Quarter Hour
Minus Operation of Data Frames
Ggplot2 Draw Individual Ellipses But Color by Group
As.Date(As.Posixct()) Gives the Wrong Date
Ggplot2 Overlay of Barplot and Line Plot
How to Get All Possible Subsets of a Character Vector in R
Format Date-Time as Seasons in R
How to Read Huge CSV File into R by Row Condition
Display a Matrix, Including the Values, as a Heatmap
How to View an HTML Table in the Viewer Pane
Si Prefixes in Ggplot2 Axis Labels
Different Colors for Each Bar in Stacked Bar Graph - Base Graphics