Dennis Murphy
2010-Aug-22 14:09 UTC
[R] lattice::xyplot() with one factor for points and another for lines - solution
Hi: Yesterday, I posted a question regarding how to handle different graphical behavior between two factors in xyplot() [package lattice]. After a public and private reply from Deepayan Sarkar, the problem has been resolved nicely, including the addition of a stacked legend for the two factors in question. The latter requires package latticeExtra. library(lattice) library(latticeExtra) # Test data: d <- data.frame(time = rep(1:8, each = 6), val = rnorm(48), gp1 = factor(rep(1:6, 8)), gp2 = factor(rep(rep(c('A', 'B'), each = 3), 8))) mypch <- 1:6 mycol <- 1:6 # Define individual keys for each of the two factors # Details on xyplot() help page mykey1 <- list(title = 'Group 1', cex.title = 1.2, text = list(levels(d$gp1), cex = 0.8), points = list(pch = 1:6, col = 1:6), cex = 0.8) mykey2 <- list(title = 'Group 2', cex.title = 1.2, text = list(levels(d$gp2)), lines = list(lty = 1, col = c('blue', 'orange'))) # Create a grob by merging the two keys - from latticeExtra (Felix Andrews) mylegend <- mergedTrellisLegendGrob(list(fun = draw.key, args = list(key = mykey1)), list(fun = draw.key, args = list(key = mykey2)), vertical = TRUE) # Generate the plot: with(d, xyplot(val ~ time, pch = mypch, col = mycol, lty = 1, col.line = c('blue', 'red'), subscripts = TRUE, panel = function(x, y, ..., groups) { panel.superpose(x, y, ..., groups = gp1, panel panel.points) panel.superpose(x, y, ..., groups = gp2, panel = panel.loess) }, par.settings = list(layout.widths = list(key.right = 1.6)), legend = list(right = list(fun = mylegend)))) The panel function uses panel.superpose to separate the task of plotting points according to the levels of gp1 from the task of producing loess fits to the groups in gp2. In particular, panel.loess is not 'group aware', so the panel.superpose code is necessary to plot loess smooths by group in this context. Note the subscripts = TRUE argument *outside* of the panel function so that the panel function can operate on the groups. The legend statement uses mylegend as the argument to fun, where fun can either be a grob (which mylegend happens to be here) or a function; in the latter case, the function needs to be followed by args = list(...), where ... is a placeholder for the assigned values of the function arguments. [See the call to mergedTrellisLegendGrob(), for example.] Finally, par.settings is used in this context to provide extra horizontal space on the right to comfortably contain the legend contents. Thanks to Deepayan for his lucid and informative replies. I hope this will be of use to others. Regards, Dennis [[alternative HTML version deleted]]