r.user.05apr
r.user.05apr

Reputation: 5456

How to fix ggplot continuous color range

I can't fix the colors of my heat-maps according to their values. Same values should have same colors. The goal is to keep all values below a certain threshold (0.05) in (constant) gray. For values greather than this threshold, the colors should gradually change from "firebrick1" to "firebrick4".

For example, "Plant 5"/"202004" = 70.6 is red if I use variable utilization2 and gray if I use variable utilization. How can I fix that?

library(tidyverse)
library(rlang)

MONTHS <- str_c("2020", sprintf("%02d", 1:12))
PLANTS <- str_c("Plant ", 1:5)

crossing(month = MONTHS, plant = PLANTS) %>%
  mutate(utilization = runif(nrow(.), 70, 100)) %>%
  mutate(utilization2 = if_else(plant == "Plant 2", utilization * 0.67, utilization)) -> d

draw_plot <- function(fill) {
  fill <- ensym(fill)
   d %>%
    ggplot(mapping = aes(x = month, y = plant, fill = !!fill)) +
    geom_tile(aes(width = 0.85, height = 0.85)) +
    geom_text(aes(label = round(!!fill, 1)), color = "white") +
    scale_x_discrete(expand=c(0,0)) +
    scale_y_discrete(expand=c(0,0)) +
    scale_fill_gradientn(colours = c("darkgray", "firebrick1", "firebrick4"), 
                         values = c(0, 0.05, 1)) +
    labs(x = "Month", y = "Production plant", title = str_c("fill = ", fill), color = "Utilization") +
    theme_light() +
    theme(legend.position = "none")
}
draw_plot(utilization)
draw_plot(utilization2)

enter image description here

enter image description here

Upvotes: 2

Views: 445

Answers (2)

Georgery
Georgery

Reputation: 8117

library(tidyverse)
library(rlang)

MONTHS <- str_c("2020", sprintf("%02d", 1:12))
PLANTS <- str_c("Plant ", 1:5)

crossing(month = MONTHS, plant = PLANTS) %>%
    mutate(utilization = runif(nrow(.), 70, 100)) %>%
    mutate(utilization2 = if_else(plant == "Plant 2", utilization * 0.67, utilization)) -> d

draw_plot <- function(fill) {
    fill <- ensym(fill)
    d %>%
        ggplot(mapping = aes(x = month, y = plant, fill = !!fill)) +
        geom_tile(aes(width = 0.85, height = 0.85)) +
        geom_text(aes(label = round(!!fill, 1)), color = "white") +
        scale_x_discrete(expand=c(0,0)) +
        scale_y_discrete(expand=c(0,0)) +
        scale_fill_gradientn(colours = c("darkgray", "firebrick1", "firebrick4"),
                             values = c(0, 0.05, 1), limits = c(min(d$utilization, d$utilization2), max(d$utilization, d$utilization2))) +
        labs(x = "Month", y = "Production plant", title = str_c("fill = ", fill), color = "Utilization") +
        scale_color_identity() +
        theme_light() +
        theme(legend.position = "none")
}
draw_plot(utilization)
draw_plot(utilization2)

The point is that scale_fill_gradientn() sets the limits of the scale to max and min of the vector of interest. You have to set them manually. In this case I chose both the max and min of both columns (limits = c(min(d$utilization, d$utilization2), max(d$utilization, d$utilization2))).

Upvotes: 2

teunbrand
teunbrand

Reputation: 37933

The colours are interpolated between the values, so a trick you could do is is to set both 0 and 0.05 as gray, and begin the next colour at a very small increment to 0.05.

draw_plot <- function(fill) {
  fill <- ensym(fill)
  d %>%
    ggplot(mapping = aes(x = month, y = plant, fill = !!fill)) +
    geom_tile(aes(width = 0.85, height = 0.85)) +
    geom_text(aes(label = round(!!fill, 1)), color = "white") +
    scale_x_discrete(expand=c(0,0)) +
    scale_y_discrete(expand=c(0,0)) +
    scale_fill_gradientn(colours = c("darkgray", "darkgray", "firebrick1", "firebrick4"), 
                         values = c(0, 0.05, 0.05 + .Machine$double.eps, 1)) +
    labs(x = "Month", y = "Production plant", title = str_c("fill = ", fill), color = "Utilization") +
    theme_light() +
    theme(legend.position = "none")
}
draw_plot(utilization)
draw_plot(utilization2)

Maybe this is not necessary to mention, but the fill scale rescales all fill values to a range between 0-1 depending on the limits (see ?scales::rescale), so the 0.05 you put in the values argument is the bottom 5% of the range value and not unscaled data values in utilization that are below 0.05. If you want to have consistent fill scales over multiple plots, you'd have to set the limits argument manually.

Upvotes: 1

Related Questions