1

I have a two-panel lattice lineplot. I want to use the directlabels package to automatically label the lines in each plot. But I want to use a different positioning method for each plot. Specifically, I want to use the first.bumpup method for the first panel, and the last.bumpup method for the second panel. Here is a minimal example:

library(directlabels)
library(lattice)
myDF <- data.frame(
  y     = rep(1:4,         2),
  x     = rep(rep(1:2, 2), 2),
  group = rep(c('a', 'b'), each = 2),
  panel = rep(1:2,         each = 4))
myPlot <- xyplot(y ~ x | panel, groups = group, data = myDF, type = 'l')
direct.label(
  p      = myPlot,
  method = 'first.bumpup')

This code produces a plot in which labels appear on the left-hand side of each panel:

figure produced by example code

I want labels on the left-hand side of the left-hand panel (as in this example), but on the right-hand side of the right-hand panel. What is the simplest way to produce that sort of figure?

I've checked the advanced examples in the directlabels documentation, and they make me think that it may be possible to use different methods for different panels by creating a custom positioning method or a custom panel. But I cannot quite see how to do it.

user697473
  • 2,165
  • 1
  • 20
  • 47

2 Answers2

2

I took a crack at this with ggplot2 (only because I know ggplot2 much better than I know lattice). Let me know what you think. Below are two approaches. The first actually doesn't use directlabels. The placement rule is relatively simple, so I just used geom_text for label placement. The second method does use directlabels, but is more complicated.

Place labels using geom_text

library(dplyr)   # For chaining operator (%>%)
library(ggplot2)
library(cowplot) # For cowplot theme

ggplot(myDF, aes(x, y, colour=group)) + 
  geom_line() +
  geom_text(data=myDF %>% group_by(panel) %>%
              filter(ifelse(panel==1, x==min(x), x==max(x))),
            aes(x + 0.07*(panel-mean(panel)), y, label=group)) +
  facet_grid(~panel) +
  scale_x_continuous(breaks=seq(1,2,0.2)) +
  theme_cowplot() +
  guides(colour=FALSE)

In the code above, inside geom_text we keep only the lowest x value for the first panel and the highest x value in the second panel and then place the group labels at the y values that pair with the x values. The x + 0.07*(panel-mean(panel)) is just to move the labels slightly away from the ends of the lines.

enter image description here

Place labels using mapply and directlabels

Here's a more complicated method using directlabels. My approach was to plot each "facet" separately using mapply, so that I could use a different directlabels method for each panel, but then lay the two plots out together as if they were two facets of the same overall plot. If you like the result, maybe you can adapt it to a lattice plot if none of the ggplot2 versions meet your needs.

library(directlabels)
library(ggplot2)
library(gridExtra)
library(cowplot)

pl = mapply(function(pnl, m) {

  # Create plot for each level of panel
  p = ggplot(myDF[myDF$panel==pnl, ], aes(x, y, colour=group)) + 
    geom_line() +
    facet_grid(~panel) +
    scale_x_continuous(breaks=seq(1,2,0.2)) +
    theme_cowplot()

  # # Tweak margins of panel 1
  # if(pnl==1) p = p + theme(plot.margin=unit(rep(0,4),"lines"))

  # Remove y-axis title, labels and ticks for panel 2 and tweak margins
  if(pnl==2) p = p + theme(axis.title.y=element_blank(), 
                           axis.text.y=element_blank(),
                           axis.ticks.y=element_blank())

  # Add directlabels with different method for each panel
  direct.label(p, method=m)
  }, 
  pnl=unique(myDF$panel), m=c("first.bumpup", "last.bumpup"), SIMPLIFY=FALSE)

Because I removed the y-axis title, labels, and ticks in panel 2, that panel is wider than panel 1. plot_grid has an align argument that allows us to align the two plots so that they have the same width, but I don't know of a way to get rid of the space between the plots. grid.arrange will also lay out that plots, but we have to adjust the widths manually (you can adjust the widths manually with plot_grid as well).

# Lay out each panel using plot_grid from cowplot package
plot_grid(plotlist=pl, ncol=2, align="v")

enter image description here

# Lay out each panel using grid.arrange from gridExtra package
grid.arrange(grobs=pl, ncol=2, widths=c(10,9))

enter image description here

eipi10
  • 91,525
  • 24
  • 209
  • 285
  • Thank you, @eipi10. Your examples helped, even though I am much more familiar with lattice than with ggplot. To me, using `mapply` is the key. And I suspect that even finer adjustment of panel width and positioning can be achieved via `grid.layout`. (http://stackoverflow.com/questions/11069202/exact-positioning-of-plots-with-grid-layout is helpfully fussy on this point.) I've adapted your code to work with lattice plots, and I'll post that code in another answer to the original question. – user697473 Dec 17 '16 at 18:21
1

Here is an adaptation of eipi10's second solution that creates the desired effect in a lattice plot:

library(directlabels)
library(gridExtra)
library(lattice)

myDF <- data.frame(
  y     = rep(1:4,         2),
  x     = rep(rep(1:2, 2), 2),
  group = rep(c('a', 'b'), each = 2),
  panel = rep(1:2,         each = 4))

plotFunction <- function(panelNumber, labelMethod) {
  myPlot = xyplot(
    y ~ x, 
    groups = group, 
    data   = myDF[myDF$panel==panelNumber, ], 
    type   = 'l')
  direct.label(
    p      = myPlot,
    method = labelMethod)  
} 

panelList = mapply(
  FUN         = plotFunction, 
  panelNumber = unique(myDF$panel), 
  labelMethod = c('first.bumpup', 'last.bumpup'), 
  SIMPLIFY    = FALSE)

grid.arrange(grobs = panelList, ncol = 2)
user697473
  • 2,165
  • 1
  • 20
  • 47