rggplot2viridis

Is there a way to add a solid color after a threshold with scale_color_viridis_c from ggplot? Or to layer legends/color_bars with ggnewscale?


I'm trying to create a a plot for the accuracy of a set of models that differ in their combination of two parameters. The accuracy doesn't exceed 0.98 for any model, and most of the models have an accuracy between 0.94 and 0.98 (btw it's just practice, no real research). I found that using scale_color_viridis_c() with limits = c(.96, .98) and values = c(.25, 1) yields a nice gradient, that visualizes the accuracy distrivution nicely, but this way I could not have the color_bar go up to 1 - it ends at 0.98.

My first approach was setting limits = c(0.96, 1) and values = c(.125, 0.5), but that caused every value above 0.98 to register as NAs the same as the values below 0.96. Then I tried layering this first plot with a second one that would be the same as the first one, but color the datapoints with an accuracy > 0.98 black (i.e. nothing) and "fill" the upper NAs (i.e. 0.98 to 1) of the first color_bar with black, and have everything lower transparent.

I created an example:

library(ggplot2)
library(ggnewscale)

set.seed(123)

# variable 1
v1 <- numeric()
for (i in 1:50) {
  v1 <- c(v1, rep(i, 56))
}

# variable 2
v2 <- rep(seq(50, 600, 10), 50)

# creating preliminary df
df <- data.frame(v1 = v1, v2 = v2)

# creating mock accuracy data
acc <- apply(df, 1, function(x) {
  temp_acc <- runif(1, 1, 10) * x[1]^0.6 / x[2]^0.65 * -1
})
acc <- (-1 * (acc / (10 * min(acc)))) - runif(1, -0.01, 0.01) + 0.99
acc <- acc - (max(acc) - 0.98) - 0.0005

#create complete df
df$acc <- acc

#plotting
g <- ggplot(data =  df, aes(x  = v1, y = v2, color = acc)) +
  geom_point() + scale_color_viridis_c(values = c(.125, 0.5), na.value = "grey80",
                                       breaks = seq(0.96, 1, 0.005), limits = c(0.96, 1),
                                       guide = guide_colorbar(barheight = 10)) +
  new_scale_color() +
  geom_point(aes(color = acc), alpha = 0.2) +
  scale_color_gradientn(colors = "black",
                        values = c(0.5, 1), na.value = NA,
                        breaks = seq(0.96, 1, 0.005), limits = c(0.96, 1),
                        guide = guide_colorbar(barheight = 10))
g

This does almost exactly what I want - the plot part is perfect, but I want the two color_bars to be combined into one. Is there a way to achieve this? Or to approach it from a different angle: Is there a way to set two thresholds for a continuous color scale, and have data above/below those thresholds be colored differently whith a combined color_bar?

This is only for aesthetic reasons, and I could easily accomplish this task with image editing software, even MSPaint, but I'd like to be able to get it all done in R based on solely my data, no artificial editing.

Thanks in advance!

David

plot with mock data


Solution

  • I figured out how to solve my problem, or at least a workaround that works well enough for me - the key was grabbing the the color values from viridis() directly, as you would with rainbow(). I tried it before and it didn't work because I didn't have library(viridis) loaded or specified it as viridis::viridis() so I wrongly concluded that there is nothing like viridis().

    But as it turns out, you can, so here's my code:

    library(ggplot2)
    library(ggnewscale)
    library(viridis)
    
    set.seed(123)
    
    # variable 1
    v1 <- numeric()
    for (i in 1:50) {
      v1 <- c(v1, rep(i, 56))
    }
    
    # variable 2
    v2 <- rep(seq(50, 600, 10), 50)
    
    # creating preliminary df
    df <- data.frame(v1 = v1, v2 = v2)
    
    # creating mock accuracy data
    acc <- apply(df, 1, function(x) {
      temp_acc <- runif(1, 1, 10) * x[1]^0.6 / x[2]^0.65 * -1
    })
    acc <- (-1 * (acc / (10 * min(acc)))) - runif(1, -0.01, 0.01) + 0.99
    acc <- acc - (max(acc) - 0.98) - 0.0005
    
    # create complete df
    df$acc <- acc
    
    # grabbing the scale colors from viridis() and appending grey80,
    # yielding a color vector that has the continuous viridis color of the length I need it,
    # and then switches to a solid color of my choice
    color_vector <- c(viridis(length(acc)), rep("grey80", times = length(acc)))
    
    # plotting
    g <- ggplot(data =  df, aes(x  = v1, y = v2, color = acc)) +
      geom_point() + scale_color_gradientn(colors = color_vector,
                                           values = c(0.2, 1), na.value = "grey45",
                                           guide = guide_colorbar(barheight = 15),
                                           breaks = seq(0.95, 1, 0.005),limits = c(0.95, 1))
    g
    

    I actually changed the (nonexistent) values above 0.98 to be represented in "grey80" instead of "black", and the NA.values (i.e. everything below 0.96) from "grey80" to "grey45" - I found it to be more visually unambiguous that way.

    Here's how it looks: imgurlink because I can't add pictures yet

    And thanks nonetheless for your suggestions, they will come in handy in other situations! :)