Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

R ggplot facet_wrap with different y-axis labels, one values, one percentages

I'm plotting a time series value with its percentages using facet_wrap in ggplot:

For the plot below, the upper plot is the value, and the lower plot is percentage change. And I would like the y-axis in the lower plot to be "%". Normally in ggplot I would do something like

+ scale_y_continuous(labels = scales::percent)

But since I'm using facet_wrap, how do I specify that I only want one of the 2 plots' y-axis label to be percentages?

enter image description here

P.S. Here is the code to generate this plot:

library(data.table)
library(ggplot2)
library(scales)
library(dplyr)

pct <- function(x) {x/lag(x)-1}
Dates = seq(from = as.Date("2000-01-01"), 
            to =as.Date("2018-10-01"), 
            by = "1 month")
set.seed(1024)
this_raw = data.frame(CM = Dates,
                      value = rnorm(n = length(Dates)),
                      variable = rep("FAKE",length(Dates)))
this_diff = na.omit(as.data.table(this_raw %>% 
                                    group_by(variable) %>%
                                    mutate_each(funs(pct), c(value))))
this_diff$type = "PerCng"
this_raw$type = "RAW"
plot_all = rbindlist(list(this_raw,this_diff))
plot_all$type = factor(plot_all$type, levels = c("RAW", "PerCng"))

out_gg = plot_all %>%
  ggplot(aes(x=CM, y=value)) + 
  geom_line(color = "royalblue3") +
  theme(legend.position='bottom')+
  ggtitle("FAKE DATA") + 
  facet_wrap(~ type, scale = "free_y", nrow = 2,
             strip.position = "left", 
             labeller = as_labeller(c(RAW = "Original", PerCng = "% Change") ) )+
  scale_x_date(date_breaks = "12 month", date_labels = "%Y-%m", 
               date_minor_breaks = "3 month")+
  ylab("")+
  theme(plot.title = element_text(hjust = 0.5,size = 12),  
        axis.text.x = element_text(size = 6,angle = 45, hjust = 1),
        axis.text.y = element_text(size = 6),  
        axis.title.y = element_text(size = 6)) + 
  theme(strip.background = element_blank(),
        strip.placement = "outside")+
  theme(legend.title=element_blank())
print(out_gg)
like image 762
Chenying Gao Avatar asked Dec 07 '18 22:12

Chenying Gao


People also ask

How do you change Y axis labels to percentages in R?

Since we need to add percentages in the labels of the Y-axis, the keyword “labels” is used. Now use scales: : percent to convert the y-axis labels into a percentage. This will scale the y-axis data from decimal to percentage. It simply multiplies the value by 100.

How do I change the Y label in ggplot2?

To alter the labels on the axis, add the code +labs(y= "y axis name", x = "x axis name") to your line of basic ggplot code. Note: You can also use +labs(title = "Title") which is equivalent to ggtitle .

How do I stop Ggplot from scientific notation?

The scale_x_continuous() and scale_y_continuous() methods can be used to disable scientific notation and convert scientific labels to discrete form.

How do you change y label in R?

The easiest way to change the Y-axis title in base R plot is by using the ylab argument where we can simply type in the title. But the use of ylab does not help us to make changes in the axis title hence it is better to use mtext function, using which we can change the font size, position etc. of the title.


1 Answers

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)

modified grob

like image 179
Z.Lin Avatar answered Oct 19 '22 04:10

Z.Lin