43

I have a plot created in ggplot2 that uses scale_fill_gradientn. I'd like to add text at the minimum and maximum of the scale legend. For example, at the legend minimum display "Minimum" and at the legend maximum display "Maximum". There are posts using discrete fills and adding labels with numbers instead of text (e.g. here), but I am unsure how to use the labels feature with scale_fill_gradientn to only insert text at the min and max. At the present I am apt to getting errors:

Error in scale_labels.continuous(scale, breaks) : 
Breaks and labels are different lengths

Is this text label possible within ggplot2 for this type of scale / fill?

# The example code here produces an plot for illustrative purposes only.
# create data frame, from ggplot2 documentation
df <- expand.grid(x = 0:5, y = 0:5) 
df$z <- runif(nrow(df))

#plot
ggplot(df, aes(x, y, fill = z)) + geom_raster() + 
scale_fill_gradientn(colours=topo.colors(7),na.value = "transparent")
Martin Schmelzer
  • 23,283
  • 6
  • 73
  • 98
nofunsally
  • 2,051
  • 6
  • 35
  • 53
  • related: https://stackoverflow.com/questions/59531865/colorbar-guide-with-string-labels/59532260#59532260 – tjebo Jan 13 '21 at 09:32

2 Answers2

85

For scale_fill_gradientn() you should provide both arguments: breaks= and labels= with the same length. With argument limits= you extend colorbar to minimum and maximum value you need.

ggplot(df, aes(x, y, fill = z)) + geom_raster() + 
      scale_fill_gradientn(colours=topo.colors(7),na.value = "transparent",
                           breaks=c(0,0.5,1),labels=c("Minimum",0.5,"Maximum"),
                           limits=c(0,1))

enter image description here

Didzis Elferts
  • 95,661
  • 14
  • 264
  • 201
  • 4
    Is this answer still valid? I am using the following command to no avail. The min and max values do not show on the color bar. Here is my code `scale_fill_gradientn(colours=pal(100), breaks=breaks, limits=lim, labels=breaks, guide = guide_colorbar(title=expression(paste('hr', sep="")), title.position = "right", title.vjust = 0.8, barwidth = 75))` – dataanalyst Feb 23 '17 at 19:49
  • 3
    Code is still valid. Your code can't be checked as it is not reproducible. – Didzis Elferts Feb 24 '17 at 05:58
  • 3
    @Gandalf, there must have been a breaking change sometime. `limits` used to transform the scale now its a hard critereon and every value outside will become the NA-Color. Try it out with `df$z <- runif(nrow(df)) * 9`. – Andre Elrico Feb 26 '19 at 16:55
  • The problem with NA values and limits can be dealt with using a bit more automatism... For one suggestion how to - [see my answer](https://stackoverflow.com/a/65698823/7941188) – tjebo Jan 13 '21 at 09:27
5

User Didzis Elfert's answer slightly lacks "automatism" in my opinion (but it is of course pointing to the core of the problem +1 :). Here an option to programatically define minimum and maximum of your data.

Advantages:

  • You will not need to hard code values any more (which is error prone)
  • You will not need hard code the limits (which also is error prone)
  • Passing a named vector: You don't need the labels argument (manually map labels to values is also error-prone).
  • As a side effect you will avoid the "non-matching labels/breaks" problem
library(ggplot2)
foo <- expand.grid(x = 0:5, y = 0:5)
foo$z <- runif(nrow(foo))

myfuns <- list(Minimum = min, Mean = mean, Maximum = max)

ls_val <- unlist(lapply(myfuns, function(f) f(foo$z)))

# you only need to set the breaks argument! 
ggplot(foo, aes(x, y, fill = z)) +
  geom_raster() +
  scale_fill_gradientn(
    colours = topo.colors(7),
    breaks = ls_val
  )


# You can obviously also replace the middle value with sth else

ls_val[2] <- 0.5
names(ls_val)[2] <- 0.5

ggplot(foo, aes(x, y, fill = z)) +
  geom_raster() +
  scale_fill_gradientn(
    colours = topo.colors(7),
    breaks = ls_val
  )

tjebo
  • 21,977
  • 7
  • 58
  • 94