10

I quite like the look and feel of ggplot2 and use them often to display raster data (e.g facetting over timesteps for time-varying precipitation fields is very useful).

However, I'm still wondering whether it is easily possible to bin the continuous raster values into discrete bins and assign to each bin a single colour, that is shown in the legend (as many GIS systems do).

I tried with the guide = "legend", and breaks arguments of the scale_fill_gradient option. However these affect just the legend on the side of the graph, but the plotted values are still continuous.

library(ggplot2)
data <- data.frame(x=rep(seq(1:10),times = 10), y=rep(seq(1:10),each = 10), value = runif(100,-10,10))
ggplot(data = data, aes(x=x,y=y)) +
  geom_raster(aes(fill = value)) +
  coord_equal() +
  scale_fill_gradient2(low = "darkred", mid = "white", high = "midnightblue",
                       guide = "legend", breaks = c(-8,-4,0,4,8))

My question is mainly how to discretize the data that is plotted in ggplot, so that the reader of the graph can make quantitative conclusions on the values represented by the colors.

Secondly, how can I still use a diverging color palette (similar to scale_fill_gradient2), that is centered around zero or another specific value?

1
  • Another approach might be using levelplot from rasterVis package. Commented Aug 13, 2014 at 7:51

2 Answers 2

11

You should use the raster package to work with raster data. This package provides several function to work with categorical rasters. For example, with reclassify you can convert a continuous file into a discrete raster. The next example is adapted from this question:

library(raster)

f <- system.file("external/test.grd", package="raster")
r <- raster(f)
r <- reclassify(r, c(0, 500, 1,
                     500, 2000, 2))

On the other hand, if you want to use the ggplot2 functions, the rasterVis package provides a simple wrapper around ggplot that works with RasterLayer objects:

library(rasterVis)

gplot(r) +
    geom_raster(aes(fill = factor(value))) +
    coord_equal()

ggplot with raster

to define your own colors you can add then:

scale_fill_manual(values=c('red','green')))
Sign up to request clarification or add additional context in comments.

1 Comment

plus 1 since there is not more than 1 for fill = factor(value)
2

The best is indeed to modify the underlying data set by manually discretizing it. Below answer is based on the answer by joran.

library(ggplot2)
set.seed(1)
data <- data.frame(x     = rep(seq(1:10),times = 10), 
                   y     = rep(seq(1:10),each = 10),
                   value = runif(100,-10,10))

# Define category breaks
breaks <- c(-Inf,-3:3,Inf)
data$valueDiscr <- cut(data$value,
                       breaks = breaks,
                       right = FALSE)

# Define colors using the function also used by "scale_fill_gradient2"
discr_colors_fct <- 
  scales::div_gradient_pal(low = "darkred",
                           mid = "white", 
                           high = "midnightblue")
discr_colors <- discr_colors_fct(seq(0, 1, length.out = length(breaks)))
discr_colors
# [1] "#8B0000" "#B1503B" "#D18978" "#EBC3B9" "#FFFFFF" "#C8C0DB" "#9184B7" "#5B4C93" "#191970"

ggplot(data = data, aes(x=x,y=y)) +
  geom_raster(aes(fill = valueDiscr)) +
  coord_equal() +
  scale_fill_manual(values = discr_colors) +
  guides(fill = guide_legend(reverse=T))
  

enter image description here

Update 2021-05-31:

Based on the comment by @slhck one can indeed discretize the data in the aesthetic mapping as follows:

library(ggplot2)
set.seed(1)
data <- data.frame(x     = rep(seq(1:10),times = 10), 
                   y     = rep(seq(1:10),each = 10),
                   value = runif(100,-10,10))

# Define category breaks
breaks <- c(-Inf,-3:3,Inf)
discr_colors <- scales::div_gradient_pal(low = "darkred", mid = "white",  high = "midnightblue")(seq(0, 1, length.out = length(breaks)))
# [1] "#8B0000" "#B1503B" "#D18978" "#EBC3B9" "#FFFFFF" "#C8C0DB" "#9184B7" "#5B4C93" "#191970"

ggplot(data = data, aes(x=x,y=y)) +
  geom_raster(aes(fill = cut(value, breaks, right=FALSE))) +
  coord_equal() +
  scale_fill_manual(values = discr_colors) +
  guides(fill = guide_legend(reverse=T))

enter image description here

1 Comment

This kind of discretization could also be easily performed by rounding (or using ceiling/floor functions) within the aesthetic mapping.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.