3

I have a numeric vector and I would like to convert it to hex color codes. The colors should follow a gradient distribution from its possible minimum (red; 0), via a mid value which I define (the mean, black), to its possible max (green; 1).

With ggplot I would use the scale_*_gradientn function. But now I need the actual hex values, and I am struggling to calculate them.

library(tidyverse)
#> Warning: package 'dplyr' was built under R version 3.6.2

data <- data.frame("a"=runif(100),
                   "b"=runif(100))


# ggplot example ----------------------------------------------------------

data <- data.frame("a"=runif(100),
                   "b"=runif(100))

mean_a <- mean(data$a)

ggplot(data)+
  geom_point(aes(x=a,
                 y=b,
                 color=a),
             stat="identity")+
  scale_color_gradientn(colors=c("red","black","green"),
                        values=c(0, mean_a, 1))+
  theme(legend.position = NULL)

Mapping the scale_color_gradientn function is apparently not the way forward:

data %>% 
  mutate(color_values=map(a, scale_color_gradientn, 
                          colors=c("red","black","green"),
                          values=c(0, mean_a, 1))) %>% 
  head()
#>           a         b                      color_values
#> 1 0.2863037 0.9902960 <environment: 0x000000001d002f30>
#> 2 0.6169960 0.9527580 <environment: 0x000000001d038798>
#> 3 0.3126825 0.8807853 <environment: 0x000000001d06e098>
#> 4 0.5464037 0.2307841 <environment: 0x000000001d0a39a8>
#> 5 0.5162976 0.8147066 <environment: 0x000000001d0d92a8>
#> 6 0.7519632 0.6821084 <environment: 0x000000001d10cc98>

Created on 2020-02-17 by the reprex package (v0.3.0)

I came across this SO entry on the colorRamp function, however, it seems that it does not provide any option to define a manual 'mid' point.

I also came accross this post on colorspace package, which allows for the definition of a midpoint. However, again, I fail to apply it outside of ggplot.

Grateful for any hint!

zoowalk
  • 2,018
  • 20
  • 33

1 Answers1

4

You can get the hex values that follow the gradient of your colorbar in a ggplot as follows:

gg_build() solution

    set.seed(1)
    data <- data.frame("a"=runif(100),"b"=runif(100))

    mean_a <- mean(data$a)

    p1 <- ggplot(data)+
      geom_point(aes(x=a,
        y=b,
        color=a),
        stat="identity")+
      scale_color_gradientn(
        colors=c("red","black","green"),
        values=c(0, mean_a, 1))+
      theme(legend.position = NULL)

    # get plot build
    build <- ggplot_build(p1)

    # select hex value and x-value data
    hex_df <- build$data[[1]][, c("colour", "x")]

    # order hex value data to follow x-axis
    hex_df_ordered <- hex_df[order(hex_df$x),]

scales::gradient_n_pal() solution

Here is perhaps the approach you want using the scales package function gradient_n_pal, which is called by scale_color_gradientn:

    library(ggplot2)
    library(scales)
    library(ggpubr)

    # create data
    set.seed(1)
    data <- data.frame("a"=runif(100), "b"=runif(100))
    #get mean_a
    mean_a <- mean(data$a)
    # order the data by a
    data <- data[order(data$a),]
    # encode the values in a character variable for reference
    data$a_char <- as.character(data$a)

    # use the scales::gradient_n_pal to create a palette function
    pal <- scales::gradient_n_pal(colours = c("red","black","green"),
      values= c(0, mean_a, 1))
    # use data in a to generate palette-generated values in the data
    data$hex_from_scales <- pal(data$a)

    # plot with scale_color_gradientn
    p1 <- ggplot(data)+
      geom_point(aes(x=a,
        y=b,
        color=a),
        stat="identity", show.legend = F)+
      scale_color_gradientn(colors=c("red","black","green"),values=c(0, mean_a, 1))+
      ggtitle("Using scale_color_gradientn ")+
      theme(legend.position = NULL)

    # plot with scale_color_manual and gradient_n_pal generated hex values
    p2 <- ggplot(data)+
      geom_point(aes(x=a,
        y=b,
        color=a_char),
        stat="identity", show.legend = F)+
      scale_color_manual(values = data$hex_from_scales)+
      ggtitle("Using gradient_n_pal")+
      theme(legend.position = NULL)

    plots <- ggpubr::ggarrange(p1, p2, ncol = 2)

This code generates these plots:

Comparison of plots

Conclusion

Judging by the similarity of the two plots, it seems like you can get the hex values of your numeric vector with this snippet:

        # create a palette function
        pal <- scales::gradient_n_pal(colours = c("red","black","green"),values= c(0, mean_a, 1))
        # get palette-generated values from your vector
        data$hex_from_scales <- pal(data$a)

It is also notable that this solution generates slightly different hex codes as the ggplot_build() solution proposed above. My eyes tell me the differences are slight. Seeing is believing!

xilliam
  • 2,074
  • 2
  • 15
  • 27
  • @xiliam many thanks. this certainly provides the results I am looking for, but I was looking for a more direct method to calculate the hex values. It would be quite a detour to produce a ggplot everytime I need to calculate gradient colors' hex values. Particularly since I need the hex values for a different plot function (in mapdeck). I was looking for a function what actually does what ggplot calculates when using scale_color_gradientn. – zoowalk Feb 19 '20 at 22:47
  • 1
    @xiliam excellent. many thanks, the gradient_n_pal approach is what I have been looking for. BTW I noted that the the gradient_n_pal function doesn't work when using colors instead of colours. – zoowalk Feb 21 '20 at 07:34