3

Let's say we have a simple plot of the following kind.

library(ggplot2)
df = data.frame(y=c(0,1.1,2.3,3.1,2.9,5.8,6,7.4,8.2,9.1),x=seq(1,100, length.out=10))
ggplot(df,aes(x=x,y=y)) + geom_point()

x perfectly correlates with z. The relation is: Constant=x^2*z=1.23 therefore I could rewrite the data.frame like this:

df = cbind(df,1.23/df$x^2)

The question is:

How can I display both variables xand zone the x-axis? It could be one at the bottom and one at the top of the graph or both at the bottom.

Remi.b
  • 17,389
  • 28
  • 87
  • 168
  • 2
    it's not possible in ggplot2. It could be hacked with grid, but every time one does that, hadley kills a Camel. – baptiste Aug 29 '13 at 11:34
  • If you want secondary axes don't use `ggplot2`. It's much easier with base graphics (or possibly lattice). – Roland Aug 29 '13 at 12:18
  • 1
    If anybody comes here after such a long time: this is now supported by ggplot2 http://ggplot2.tidyverse.org/reference/sec_axis.html The doc only shows a secondary y example, but it works as well with `scale_x_continuous` – xav Mar 26 '18 at 10:47

2 Answers2

6

Here's a dangerous attempt. Previous version with a log-scale was just wrong.

library(ggplot2)
df = data.frame(y=c(0,1.1,2.3,3.1,2.9,5.8,6,7.4,8.2,9.1),
                x=seq(1,100, length.out=10))
df$z = 1.23/df$x^2


## let's at least remove the gridlines
p1 <- ggplot(df,aes(x=x,y=y)) + geom_point() +
  scale_x_continuous(expand=c(0,0)) +
  theme(panel.grid.major=element_blank(),
        panel.grid.minor = element_blank())

## make sure both plots have expand = c(0,0) 
## otherwise data and top-axis won't necessarily be aligned...
p2 <- ggplot(df,aes(x=z,y=y)) + geom_point() +
  scale_x_continuous(expand=c(0,0))

library(gtable)
g1 <- ggplotGrob(p1)
g2 <- ggplotGrob(p2)
tmp <- gtable_filter(g2, pattern="axis-b")

## ugly tricks to extract and reshape the axis
axis <- tmp[["grobs"]][[1]][["children"]][["axis"]] # corrupt the children
axis$layout <- axis$layout[2:1,]
axis$grobs[[1]][["y"]] <- axis$grobs[[1]][["y"]] - unit(1,"npc") + unit(0.15,"cm")
## back to "normality"    

g1 <- gtable_add_rows(g1, sum(tmp$heights), 2)
gtableAddGrobs <- gtable_add_grob # alias, making sure @!hadley doesn't see this
g1 <- gtableAddGrobs(g1, 
                     grobs=list(gtable_filter(g2, pattern="xlab"),axis), 
                     t=c(1,3), l=4)
grid.newpage()
grid.draw(g1)

enter image description here

baptiste
  • 75,767
  • 19
  • 198
  • 294
0

A both-on-the-bottom approach can be done with the excellent cowplot library.

library(ggplot2)
library(cowplot)

data <- data.frame(temp_c=runif(100, min=-5, max=30), outcome=runif(100))

plot <- ggplot(data) + 
  geom_point(aes(x=temp_c, y=outcome)) + 
  theme_classic() + 
  labs(x='Temperature (Celsius)')

x2plot <- ggplot(data) + 
  geom_point(aes(x=temp_c, y=outcome)) + 
  theme_classic() + 
  scale_x_continuous(label=function(x){round(x*(9/5) + 32)}) + 
  labs(x='Temperature (Fahrenehit)')

x <- get_x_axis(x2plot)
xl <- get_plot_component(x2plot, "xlab-b")

plot_grid(plot, ggdraw(x), ggdraw(xl), align='v', axis='rl', ncol=1, 
          rel_heights=c(0.8, 0.05, 0.05))

enter image description here

Amadou Kone
  • 907
  • 11
  • 21