Search code examples
rggplot2axiscowplot

Make a common x-axis for multiple plots in a cowplot


I have multiple plots made with ggplot2 that I arrange together using cowplot. They all have the same length, and an x-axis with the same breaks at the same places, but with different labels.

This is a MWE:

mylist <- list()
mylist[["ID1"]][["sequence"]] <- "AGAATATTATACATTCATCT"
mylist[["ID2"]][["sequence"]] <- "GCTAGCGTTTAGTTTAGCTG"
mylist[["ID3"]][["sequence"]] <- "AACCCTTTAAACTCGAAGTA"
set.seed(123)
mylist[["ID1"]][["data"]] <- data.frame(time=1:100, value=rnorm(100, mean=10, sd=2))
mylist[["ID2"]][["data"]] <- data.frame(time=1:100, value=rnorm(100, mean=2, sd=1))
mylist[["ID3"]][["data"]] <- data.frame(time=1:100, value=rnorm(100, mean=5, sd=3))
indexes <- seq(5, 100, length.out=20)
all_df <- NULL
for (id in names(mylist)){
  seqsplit <- unlist(strsplit(mylist[[id]][["sequence"]], ""))
  ind_df <- data.frame(call=seqsplit, time=indexes)
  final_df <- dplyr::left_join(mylist[[id]][["data"]], ind_df, by="time")
  xcolors <- ifelse(seqsplit=="A", "green", ifelse(seqsplit=="C", "blue", ifelse(seqsplit=="G", "black", "red")))
  P <- ggplot2::ggplot(final_df, ggplot2::aes(x=time, y=value)) +
    ggplot2::geom_line(linewidth=0.5) +
    ggplot2::scale_x_continuous(breaks=indexes, labels=seqsplit, expand=c(0,1)) +
    ggplot2::scale_y_continuous(breaks=seq(-5, 15, 5), limits=c(-5,15)) +
    ggplot2::theme_light() +
    ggplot2::theme(axis.title=ggplot2::element_blank(),
                   axis.text.x=ggtext::element_markdown(face="bold", color=xcolors))
  mylist[[id]][["plot"]] <- P
}
plot_list <- sapply(mylist, "[", "plot")
grDevices::pdf(file="test.pdf", height=4, width=10)
print(
  cowplot::plot_grid(plotlist=plot_list, ncol=1)
)
grDevices::dev.off()

which produces:

fig1

What I want to accomplish now, is to make a common x-axis for all, with the common indexes (or rather from 1 to the length of the sequences), that I would place on top of the cowplot; something like this:

fig2

This would probably involve making the x-axis indexes ruler as a separate ggplot2 of the same width (accounting for the y-axis horizontal space and all), and then place it on the cowplot with the appropriate relative height... any help is welcome!


Solution

  • One way is to add a secondary x-axis on the top of the first plot:

    for (id in names(mylist)){
    ...
      P <- ggplot(final_df, ggplot2::aes(x=time, y=value)) +
        geom_line(linewidth=0.5) +
        scale_y_continuous(breaks=seq(-5, 15, 5), limits=c(-5,15)) +
        theme_light() 
        
    
      if(id=="ID1") {
        P <- P + scale_x_continuous(breaks=indexes, labels=seqsplit, expand=c(0,1),
                                         sec.axis = sec_axis(~.,
                                                             breaks=indexes, labels=seq_along(indexes))) +
          theme(axis.title=element_blank(),
                axis.text.x.top = ggtext::element_markdown(color='white', fill="steelblue", 
                                                           padding=unit(c(1, 8, 1, 8), "pt")))
      }
      else {
        P <- P + scale_x_continuous(breaks=indexes, labels=seqsplit, expand=c(0,1)) +
          theme(axis.title=element_blank(),
                axis.text.x=ggtext::element_markdown(face="bold", color=xcolors))
      }
    
      mylist[[id]][["plot"]] <- P
    }
    

    enter image description here