Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I add triangles to a ggplot2 colorbar in R to indicate out of bound values?

I'd like to make a plot using ggplot2 where some of the fill values are clipped, i.e. values above or below the limits of the color scale are displayed as the minimum/maximum color. I can get this to work like this, using a combination of limit and oob (out of bounds):

library(ggplot2)
library(scales)
ggplot() + ... + scale_fill_viridis(na.value="white", limit=c(0, 10), oob=squish)

But there is no information in the colorbar that indicates there are values present outside of the limits. How can I reproduce this matplotlib example in ggplot: https://stackoverflow.com/a/32072348

Specifically, how to get the triangles at the end of the colorbar?

like image 981
Ruben Avatar asked Sep 12 '25 01:09

Ruben


1 Answers

EDIT:

I've now released a package that has this functionality. You can use it as follows. This is if you've already limited and squished your range at the scale level:

library(legendry)
#> Loading required package: ggplot2

ggplot(mtcars, aes(mpg, wt, colour = drat)) +
  geom_point() +
  scale_colour_viridis_c(
    limits = c(3, 4), oob = scales::oob_squish,
    guide = "colbar"
  )

This is if you insist on the triangles, but don't want to limit/squish at the scale level.

ggplot(mtcars, aes(mpg, wt, colour = drat)) +
  geom_point() +
  scale_colour_viridis_c(
    guide = guide_colbar(
      show = TRUE, oob = "squish"
    )
  )

Created on 2024-11-24 with reprex v2.1.1

OLD ANSWER:

As far as I'm aware there is not a package that implements triangle ends for colourbars in ggplot2 (but please let me know if there is!). However, we can implement our own. We'd need a constructor for our custom guide and a way to draw it. Most of the stuff is already implemented in guide_colourbar() and methods for their class, so what we need to do is just tag on our own class and expand the guide_gengrob method. The code below should work for vertically oriented colourbars. You'd need to know some stuff about the grid package and gtable package to follow along.

library(ggplot2)
library(gtable)
library(grid)

my_triangle_colourbar <- function(...) {
  guide <- guide_colourbar(...)
  class(guide) <- c("my_triangle_colourbar", class(guide))
  guide
}

guide_gengrob.my_triangle_colourbar <- function(...) {
  # First draw normal colourbar
  guide <- NextMethod()
  # Extract bar / colours
  is_bar <- grep("^bar$", guide$layout$name)
  bar <- guide$grobs[[is_bar]]
  extremes <- c(bar$raster[1], bar$raster[length(bar$raster)])
  # Extract size
  width  <- guide$widths[guide$layout$l[is_bar]]
  height <- guide$heights[guide$layout$t[is_bar]]
  short  <- min(convertUnit(width, "cm",  valueOnly = TRUE),
                convertUnit(height, "cm", valueOnly = TRUE))
  # Make space for triangles
  guide <- gtable_add_rows(guide, unit(short, "cm"),
                           guide$layout$t[is_bar] - 1)
  guide <- gtable_add_rows(guide, unit(short, "cm"),
                           guide$layout$t[is_bar])
  
  # Draw triangles
  top <- polygonGrob(
    x = unit(c(0, 0.5, 1), "npc"),
    y = unit(c(0, 1, 0), "npc"),
    gp = gpar(fill = extremes[1], col = NA)
  )
  bottom <- polygonGrob(
    x = unit(c(0, 0.5, 1), "npc"),
    y = unit(c(1, 0, 1), "npc"),
    gp = gpar(fill = extremes[2], col = NA)
  )
  # Add triangles to guide
  guide <- gtable_add_grob(
    guide, top, 
    t = guide$layout$t[is_bar] - 1,
    l = guide$layout$l[is_bar]
  )
  guide <- gtable_add_grob(
    guide, bottom,
    t = guide$layout$t[is_bar] + 1,
    l = guide$layout$l[is_bar]
  )
  
  return(guide)
}

You can then use your custom guide as the guide argument in a scale.

g <- ggplot(mtcars, aes(mpg, wt)) +
  geom_point(aes(colour = drat))

g + scale_colour_viridis_c(
    limits = c(3, 4), oob = scales::oob_squish,
    guide = my_triangle_colourbar()
  )

There isn't really a natural way to colour out-of-bounds values differently, but you can make very small slices near the extremes a different colour.

g + scale_colour_gradientn(
    colours = c("red", scales::viridis_pal()(255), "hotpink"),
    limits = c(3, 4), oob = scales::oob_squish,
    guide = my_triangle_colourbar()
  )

Created on 2021-07-19 by the reprex package (v1.0.0)

like image 102
teunbrand Avatar answered Sep 14 '25 16:09

teunbrand