85

I feel like this should be obvious... all I'm trying to do is to remove the x-axis from the bottom of my graph and add it to the top.

Here is a reproducible example. Data plus code to make the following graph:

library(reshape2)
library(ggplot2)

data(mtcars)
dat <- with(mtcars, data.frame(mpg, cyl, disp, hp, wt, gear))
cor.matrix <- round(cor(dat, use = "pairwise.complete.obs", method = "spearman"), digits = 2)
diag(cor.matrix)<-NA

cor.dat <- melt(cor.matrix)
cor.dat <- data.frame(cor.dat)
cor.dat <- cor.dat[complete.cases(cor.dat),]


ggplot(cor.dat, aes(Var2, Var1, fill = value)) + 
  geom_tile(colour="gray90", size=1.5, stat="identity") + 
  geom_text(data=cor.dat, aes(Var2, Var1, label = value), color="black", size=rel(4.5)) +
  scale_fill_gradient(low = "white", high = "dodgerblue", space = "Lab", na.value = "gray90", guide = "colourbar") +
  scale_x_discrete(expand = c(0, 0)) +
  scale_y_discrete(expand = c(0, 0)) +
  xlab("") + 
  ylab("") +
  theme(panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.border = element_rect(fill=NA,color="gray90", size=0.5, linetype="solid"),
        axis.line = element_blank(),
        axis.ticks = element_blank(),
        panel.background = element_rect(fill="gray90"),
        plot.background = element_rect(fill="gray90"),
        legend.position = "none", 
        axis.text = element_text(color="black", size=14) )

enter image description here

But what I'm trying to produce is- it seems like this should be obvious (e.g. it's very easy to do in base-R) , but I haven't managed to find what I'm looking for in ggplot2.

enter image description here

jalapic
  • 13,792
  • 8
  • 57
  • 87
  • The axes in ggplot aren't nearly as flexible as in base R. For your example, you're not using any axis components except for labels. I'd say your best bet is to not plot an axis at all, and just use `geom_text` to put the labels where you want. – Gregor Thomas Nov 10 '14 at 06:46
  • 2
    Moving the x-axis is difficult, but it is possible by using the functions ggplot_gtable and ggplot_build. These functions "plot" the ggplot, but instead of to the screen, makes socalled "grob"-objects that represent the visual elements (a box, line, grid, etc.), but on a different level than the ggplot objects (scales, themes, aestetics, etc.). You can manipulate the grobs as you see fit, swapping them, resizing them etc. – MrGumble Nov 10 '14 at 07:32
  • Unless you're automating this, you may just want to make the figure then edit it after the fact. Would take a few seconds to move the axis in illustrator or inkscape. – Minnow Nov 29 '14 at 02:22
  • I found this page since I am working on a Shiny app; inkscape or Illustrator is out of the question! – tumultous_rooster Dec 05 '14 at 17:40

6 Answers6

169

You can move the x-axis labels to the top by adding

scale_x_discrete(position = "top") 
Matthias Haber
  • 1,706
  • 1
  • 9
  • 2
  • Note that this solution may not work as expected if you are using `coord_flip()` in your code. I think this is larger issue in ggplot2 where the 'x' and 'y' parameters of some of the functions like `scale...` and `labs` continue to mean 'x' and 'y' as defined in the aes() call and others like 'facet_wrap' switch their meaning when you flip the coordinates. You can work around this easily, by manually flipping your x and y variables in the aes() call. – ESELIA Feb 24 '22 at 14:27
6

I've used this work around. Used a duplicated the plot's x-axis and affixed the two together, then crop. I don't have the cleaned up code, but below is an example.

p.bot   <-  
ggplot(cor.dat, aes(Var2, Var1, fill = value)) + 
  geom_tile(colour="gray90", size=1.5, stat="identity") + 
  geom_text(data=cor.dat, aes(Var2, Var1, label = value), color="black", size=rel(4.5)) +
  scale_fill_gradient(low = "white", high = "dodgerblue", space = "Lab", na.value = "gray90", guide = "colourbar") +
  scale_x_discrete(expand = c(0, 0)) +
  scale_y_discrete(expand = c(0, 0)) +
  xlab("") + 
  ylab("") +
  theme(panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.border = element_rect(fill=NA,color="gray90", size=0.5, linetype="solid"),
        axis.line = element_blank(),
        axis.ticks = element_blank(),
        panel.background = element_rect(fill="gray90"),
        plot.background = element_rect(fill="gray90"),
        legend.position = "none", 
        axis.text.x = element_blank(),
        plot.margin = unit(c(1,0,0,0), "cm"),
        axis.text.y = element_text(color="black", size=14) )

p.top   <-  p.bot + theme(
    axis.text.x = element_text(color="black", size=14),
    axis.text.y = element_text(color="gray90", size=14)
    )  + coord_cartesian(ylim = c(0,0))



require(gtable)
#Extract Grobs
g1<-ggplotGrob(p.top)
g2<-ggplotGrob(p.bot)
#Bind the tables
g<-gtable:::rbind_gtable(g1, g2, "first")
#Remove a row between the plots
g <- gtable_add_rows(g, unit(-1.25,"cm"), pos=nrow(g1))
#draw
panels <- g$layout$t[grep("panel", g$layout$name)]
g$heights[panels] <- lapply(c(0,2), unit, "null")

grid.newpage()
grid.draw(g)

enter image description here

Aaron Katch
  • 451
  • 3
  • 13
5

check out the cowplot package

ggdraw(switch_axis_position(p + axis = 'x'))
Jerry T
  • 1,541
  • 1
  • 19
  • 17
  • Under the current version of cowplot, I needed to swap the plus for a comma: ggdraw(switch_axis_position(p, axis = 'x')) – Jessica Beyer Sep 12 '16 at 14:51
3

Nowadays, you can do this by adding "position = 'top'" in the scale_x_discrete.

code:

library(reshape2)
library(ggplot2)

data(mtcars)
dat <- with(mtcars, data.frame(mpg, cyl, disp, hp, wt, gear))
cor.matrix <- round(cor(dat, use = "pairwise.complete.obs", method = "spearman"), digits = 2)
diag(cor.matrix)<-NA

cor.dat <- melt(cor.matrix)
cor.dat <- data.frame(cor.dat)
cor.dat <- cor.dat[complete.cases(cor.dat),]


ggplot(cor.dat, aes(Var2, Var1, fill = value)) + 
  geom_tile(colour="gray90", size=1.5, stat="identity") + 
  geom_text(data=cor.dat, aes(Var2, Var1, label = value), color="black", size=rel(4.5)) +
  scale_fill_gradient(low = "white", high = "dodgerblue", space = "Lab", na.value = "gray90", guide = "colourbar") +
  scale_x_discrete(expand = c(0, 0),position = 'top') +
  scale_y_discrete(expand = c(0, 0),position = 'left') +
  xlab("") + 
  ylab("") +
  theme(panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.border = element_rect(fill=NA,color="gray90", size=0.5, linetype="solid"),
        axis.line = element_blank(),
        axis.ticks = element_blank(),
        panel.background = element_rect(fill="gray90"),
        plot.background = element_rect(fill="gray90"),
        legend.position = "none", 
        axis.text = element_text(color="black", size=14) )
Luis Vigo
  • 31
  • 1
1

see http://ggplot2.tidyverse.org/reference/sec_axis.html

scale_y_continuous(sec.axis = dup_axis())
Floern
  • 33,559
  • 24
  • 104
  • 119
smroecker
  • 11
  • 1
  • 2
    Consider expanding your answer by explaining what the code does/why it solves the OP's problem and summarizing the details that are available at the link your shared. – Joey Harwood Jan 08 '18 at 20:48
  • 1
    While this code snippet may be the solution, including an explanation really helps to improve the quality of your post. Remember that you are answering the question for readers in the future, and those people might not know the reasons for your code suggestion – Rahul Gupta Jan 09 '18 at 05:26
-1

if it is a continuous scale use

scale_x_continuous(position = "top") +

if it is a discrete scale use

scale_x_discrete(position = "top") +
Caleb
  • 1
  • 2
    How is this answer different from some of the others? Also the use of the `+` operator here is a bit confusing. I would either provide the full code chunk or omit this. If for example you used this function at the end of a `ggplot` object, it would cause an error. – Shawn Hemelstrand Jan 27 '23 at 02:02