Search code examples
rcontour

Control Label of Contour Lines in `contour()`


I am using image() and contour() to create a "heatmap" of probabilities - for example:

enter image description here

I was asked to change the labels such that they "do not overlap the lines, and the lines are unbroken." After consulting ?contour(), I tried changing to method = "edge" and method = "simple", but both fail print the labels (although the lines are unbroken), and cant seem to find posts regarding similar issues elsewhere.

enter image description here

Any advice on how to manipulate the labels to appear adjacent to (not on top of) unbroken lines would be much appreciated. I would prefer base R but also would welcome options from more flexible packages or alternative base R functions.

Minimal code to recreate example figure is here:

# Generate Data
Rs <- seq(0.02, 1.0, 0.005)
ks <-  10 ^ seq(-2.3, 0.5, 0.005) 
prob <- function(Y,R,k) { 
  exp(lgamma(k*Y+Y-1) - lgamma(k*Y) - lgamma(Y+1) + (Y-1) * log(R/k) - (k*Y+Y-1) * log(1+R/k))
}
P05 <- matrix(NA, ncol = length(ks), nrow = length(Rs))
for(i in 1:length(Rs)) {
  for(j in 1:length(ks)) {
    P05[i,j] <- 1 - sum(prob(1:(5 - 1), Rs[i], ks[j]))
  }
}
colfunc <- colorRampPalette(c("grey25", "grey90"))
lbreaks <- c(-1e-10, 1e-5, 1e-3, 5e-3, 1e-2, 2e-2, 5e-2, 1e-1, 1.5e-1, 1)

## Create Figure
image(Rs, ks, P05,
      log="y", col = rev(colfunc(length(lbreaks)-1)), breaks = lbreaks, zlim = lbreaks,
      ylim = c(min(ks), 2), xlim = c(0,1))
contour(Rs, ks, P05, levels = lbreaks, labcex = 1, add = TRUE)

Solution

  • There is an easy(ish) way to do this in ggplot, using the geomtextpath package.

    First, convert your matrix to an x, y, z data frame:

    df <- expand.grid(Rs = Rs, ks = ks)
    df$z <- c(P05)
    

    Now plot a filled contour, and then geom_textcontour. By default the text will break the lines, as in contour, but if you set the vjust above one or below zero the lines will close up as they don't need to break for the text.

    I've added a few theme and scale elements to match the aesthetic of the base graphics function. Note the text and line size, color etc remain independently adjustable.

    library(geomtextpath)
    
    ggplot(df, aes(Rs, ks, z = z)) +
      geom_contour_filled(breaks = lbreaks) +
      geom_textcontour(breaks = lbreaks, color = 'black', size = 5,
                       aes(label = stat(level)), vjust = 1.2) +
      scale_y_log10(breaks = c(0.01, 0.02, 0.05, 0.1, 0.2, 0.5, 1, 2), 
                    expand = c(0, 0)) +
      scale_fill_manual(values = rev(colfunc(9)), guide = 'none') +
      scale_x_continuous(expand = c(0, 0)) +
      theme_classic(base_size = 16) +
      theme(axis.text.y = element_text(angle = 90, hjust = 0.5),
            axis.ticks.length.y = unit(3, 'mm'),
            plot.margin = margin(20, 20, 20, 20))
    

    enter image description here