Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Continuous color bar with separators instead of ticks

Tags:

r

legend

ggplot2

The picture should give a good idea of what I would want to achieve. Ideally, the resulting colour bar would keep a continuous nature, i.e., should be based on a continuous rather than discrete aesthetic, or at least come very close to behave like it.

This also means, not just the simple approach of just bringing the legend keys of a discrete fill close enough together.

Yes, I could create a fake legend, but I'd like a solution that modifies at the legend drawing level.

I would be also very happy with interesting grid hacks!

library(ggplot2)

ggplot(iris, aes(Sepal.Length, y = Sepal.Width, fill = Petal.Length))+
  geom_point(shape = 21) +
  scale_fill_continuous() +
  guides(fill = guide_colorbar(ticks.colour = "black"))

That's photoshopped, apologies!. I don't want to create the arrow, just the continuous scale with separators instead of ticksenter image description here

The ultimate goal is to reproduce a color bar as orignially proposed in this thread enter image description here

However, this is not about creating the discrete gradient bar, this is just about the separators!

The discrete gradient bar is not the problem, there are already solutions out there (e.g., https://stackoverflow.com/a/62544405/7941188, https://stackoverflow.com/a/62556763/7941188, https://stackoverflow.com/a/50540633/7941188)

like image 704
tjebo Avatar asked Jun 24 '20 15:06

tjebo


2 Answers

There definitely is an option with the extendable guide system introduced in ggplot v3.3.0. See example below:

library(ggplot2)

guide_longticks <- function(...) {
  guide <- guide_colorbar(...)
  class(guide) <- c("guide", "guide_longticks", "colorbar")
  guide
}

guide_gengrob.guide_longticks <- function(guide, theme) {
  dir <- guide$direction
  guide <- NextMethod()
  is_ticks <- grep("^ticks$", guide$layout$name)
  ticks <- guide$grobs[is_ticks][[1]]
  if (dir == "vertical") {
    ticks$x1 <- rep(tail(ticks$x1, 1), length(ticks$x1))
  } else {
    ticks$y1 <- rep(tail(ticks$y1, 1), length(ticks$y1))
  }
  
  guide$grobs[[is_ticks]] <- ticks
  guide
}

ggplot(iris, aes(Sepal.Length, y = Sepal.Width, fill = Petal.Length))+
  geom_point(shape = 21) +
  scale_fill_continuous() +
  guides(fill = guide_longticks(ticks = TRUE, ticks.colour = "black"))

Created on 2020-06-24 by the reprex package (v0.3.0)

EDIT:

Also try this alternative constructor if you want flat colours in between ticks:

guide_longticks <- function(...) {
  guide <- guide_colorsteps(...)
  class(guide) <- c("guide", "guide_longticks", "colorsteps", "colorbar")
  guide
}

EDIT2:

The following gengrob function would also delete the smaller ticks, if you want cleaner vector files. It kind of assumes that they are the last half of the ticks though:

guide_gengrob.guide_longticks <- function(guide, theme) {
  dir <- guide$direction
  guide <- NextMethod()
  is_ticks <- grep("^ticks$", guide$layout$name)
  ticks <- guide$grobs[is_ticks][[1]]
  n <- length(ticks$x0)
  if (dir == "vertical") {
    ticks$x0 <- head(ticks$x0, n/2)
    ticks$x1 <- rep(tail(ticks$x1, 1), n/2)
  } else {
    ticks$y0 <- head(ticks$y0, n/2)
    ticks$y1 <- rep(tail(ticks$y1, 1), n/2)
  }
  
  guide$grobs[[is_ticks]] <- ticks
  guide
}
like image 187
teunbrand Avatar answered Sep 19 '22 08:09

teunbrand


There's certainly a quick grid hack Tjebo, but it probably needs a bit of work to make it robust (indexing by name rather than by number):

library(ggplot2)
library(grid)

p <- ggplot(iris, aes(Sepal.Length, y = Sepal.Width, fill = Petal.Length))+
  geom_point(shape = 21) +
  scale_fill_continuous() +
  guides(fill = guide_colorbar(ticks.colour = "black"))

gt <- ggplot_gtable(ggplot_build(p))

x1 <- gt$grobs[[which(gt$layout$name == "guide-box")]]$grobs[[1]]$grobs[[5]]$x1
x1[1:7] <- x1[8:14]
gt$grobs[[which(gt$layout$name == "guide-box")]]$grobs[[1]]$grobs[[5]]$x1 <- x1
grid.newpage()
grid.draw(gt)

Created on 2020-06-24 by the reprex package (v0.3.0)

like image 23
Allan Cameron Avatar answered Sep 19 '22 08:09

Allan Cameron