Sunday, December 15, 2013

plotting y and log(y) in one figure

Sometimes I have the desire to plot both on the linear and on the log scale. To save space just two figures is not my solution. I want to reuse the x-axis, legend, title. This post examines possibilities to do so with standard plot tools, lattice and ggplot2.

Data

Data is completely artificial.
library(ggplot2)
library(lattice)

datastart <- data.frame(x=rep(1:5,2),
    y=c(1,2,10,50,1, .1,9,8,20,19),
    type=rep(c('a','b'),each=5))
datastart
   x    y type
1  1  1.0    a
2  2  2.0    a
3  3 10.0    a
4  4 50.0    a
5  5  1.0    a
6  1  0.1    b
7  2  9.0    b
8  3  8.0    b
9  4 20.0    b
10 5 19.0    b

standard plot tools

The trick here is to make two plots. The top plot has no x-axis, the bottom one no title. To make the two plot surfaces equal in size the room reserved for x-axis and title is the same. 

par(mfrow=c(2,1),mar=c(0,4.1,4,2))
plot(y~x,
    data=datastart,
    axes=FALSE,
    frame.plot=TRUE,
    xlab='',
    main='bla bla',
    col=c('red','green')[datastart$type])
legend(x='topleft',
    legend=c('a','b'),
    col=c('red','green'),
    pch=1)
axis(side=2,las=1)
par(mar=c(4,4.1,0,2))
plot(y~x,
    data=datastart,
    axes=FALSE,
    frame.plot=TRUE,
    xlab='x',
    log='y',
    ylab='log(y)',
    col=c('red','green')[datastart$type]
)
axis(side=2,las=1)
axis(side=1)

lattice

As far as I understand, lattice does not have the tools to fiddle this extreme with axis. The trick then is to add a copy of the data on logarithmic scale and manually control the labels. Lattice does not understand that the x-axis are same, but some suppression is possible.
data1=datastart
data2=datastart
data1$lab='linear'
data2$lab='log'
data2$y <- log10(data2$y)
dataplot <- rbind(data1,data2)

at2 <- axisTicks(range(data2$y),log=TRUE,nint=4)
at1 <- axisTicks(range(data1$y),log=FALSE,nint=5)
atx <- axisTicks(range(data1$x),log=FALSE,nint=5)
dataplot$lab <- factor(dataplot$lab,levels=c('linear','log'))
xyplot(y  ~ x | lab,groups=type,data=dataplot,layout=c(1,2),
    main='bla bla',
    as.table=TRUE,
    auto.key=TRUE,
    scales=list(relation='free',
        x=list(at=list(NULL,atx)),
        y=list(at=list(at1,log10(at2)),
            labels=list(format(at1),format(at2))
        )
    ))

ggplot2

The trick here is that ggplot2 can have a free y-axis, but you cannot set the labels per axis. Instead it is a common y-axis which has adapted labels. ggplot chooses the range for the y-axis itself, you have to make sure that the labels you feed it match that range. To make that fit in the end I just shifted the whole log part to a different range. Some of the code of lattice is re-used.

dataplot2 <- dataplot
offset <- -10
breaks=c(-11,-10,-9)
dataplot2$y[dataplot2$lab=='log'] <- dataplot2$y[dataplot2$lab=='log'] +offset 
p <- ggplot(dataplot2, aes(x, y,colour=type)) + geom_point()
p + facet_grid(lab ~ .,
        scales='free_y') +
    labs(title = 'bla bla') +
    scale_y_continuous(
        breaks = c(breaks,at1),
        labels=c(format(10^(breaks-offset)),format(at1)))

1 comment: