jbaums
jbaums

Reputation: 27408

xyplot bottom axis when last row has fewer panels than columns

Consider a lattice xyplot that has relation='fixed', alternating=FALSE, and as.table=TRUE.

If the last row of panels is incomplete (i.e. there are fewer panels than columns of the layout), the x-axis is not plotted. For example, panel 4 in the plot below does not have x-axis ticks/labels.

library(lattice)
d <- data.frame(x=runif(100), y=runif(100), grp=gl(5, 20))
xyplot(y~x|grp, d, as.table=TRUE, scales=list(alternating=FALSE, tck=c(1, 0)))

enter image description here

How can I add that axis?

Ideally I want axes only at bottom and left sides, and the incomplete row of panels at bottom (unlike when using as.table=FALSE, which plots the incomplete row at the top). For the example above, I'd like the axis plotted on the bottom border of panel 4, rather than in line with the x-axis of panel 5.

I know that this is easily solved with, e.g., a base graphics approach. I'm specifically interested in a lattice solution.

Upvotes: 4

Views: 530

Answers (2)

jbaums
jbaums

Reputation: 27408

Here's another approach based on code provided in a (now deleted) answer by @user20650. It uses grid directly, focussing on panels of the active trellis plot that are missing axes (or at least assumed to be missing axes), and adding them. We also assume that the x-scale is fixed.

The function (which also exists as a gist here):

add_axes <- function() {
  library(grid)
  library(lattice)
  l <- trellis.currentLayout()
  pan <- which(l[nrow(l), ]==0)
  if(length(pan) > 0) {
    g <- grid.ls(print=FALSE)
    # use an existing panel as a template for ticks 
    ticks <- grid.get(g$name[grep("ticks.bottom.panel", g$name)][[1]])
    # use an existing panel as a template for labels
    labels <- grid.get(g$name[grep("ticklabels.bottom.panel", g$name)][[1]])
    ax <- grobTree(ticks, labels)
    invisible(sapply(pan, function(x) {
      trellis.focus("panel", x, nrow(l)-1, clip.off=TRUE)
      grid.draw(ax)
      trellis.unfocus()  
    }))
  }
}

An example:

library(lattice)
d <- data.frame(x=runif(100), y=runif(100), grp=gl(5, 20))
xyplot(y~x|grp, d, as.table=TRUE, scales=list(tck=c(1,0), alternating=FALSE), 
       layout=c(4, 2), xlim=c(-0.1, 1.1))

enter image description here

add_axes()

enter image description here

Upvotes: 3

Tal J. Levy
Tal J. Levy

Reputation: 598

I am not a lattice expert, but I believe this might work. The idea was originally posted here. First I will regenerate the example:

library(lattice)
set.seed(1)
d <- data.frame(x=runif(100), y=runif(100), grp=gl(5, 20))

Next, lets define a function that will control the panel settings:

trellis.par.set(clip = list(panel = "off"))
myPan <- function(...){
    panel.xyplot(...)
    if(panel.number() == 4) {
        at = seq(0,1,by = 0.2)
        panel.axis("bottom", at = at, outside = T,
                    labels = T, half = F)
    }
    if(panel.number() == 5) {
        at = seq(0,1,by = 0.2)
        panel.axis("bottom",at = at, outside = T,
                   labels = T, half = F)
    }
}

Now to the plot:

xyplot(y~x|grp, d, as.table=TRUE, 
       scales = list(
           x = list(draw = F, relation="same"),
           y = list(tck=c(1,0), alternating=F)),
       layout = c(2,3),
       panel = myPan)

As can be seen, in the xyplot command we asked not to draw the x axis (draw = F) but later panel calls myPan function. There we specifically demand to draw x-axis for panels 4 and 5.

output

enter image description here

Hope it can give you some direction for improvements.

Upvotes: 6

Related Questions