Search code examples
rggplot2geom-text

Add a geom_text_repel layer to geom_point colored by scale_colour_gradient2


I have data that I'd like to ggplot with a gradient coloring scheme and then annotate some of the points.

My data:

df <- data.frame(id = rep(LETTERS,100),
                 val1 = rnorm(100*length(LETTERS)), val2 = rnorm(100*length(LETTERS)),
                 sig = runif(100*length(LETTERS),0,1),
                 col = NA,stringsAsFactors = F)

Here I select a few points I'd like to annotate and give them colors:

df$col[sample(nrow(df), 10, replace = F)] <- rainbow(10)

And here's the ggplot code I'm trying:

library(ggplot2)
library(ggrepel)
ggplot(df,aes(x=val1,y=val2,color=col))+
  geom_point(aes(color=sig),cex=2)+scale_colour_gradient2("Significance",low="darkred",mid="darkblue",high="darkred")+
  geom_text_repel(data=dplyr::filter(df,!is.na(col)),aes(x=dplyr::filter(df,!is.na(col))$val1,y=dplyr::filter(df,!is.na(col))$val2,label=dplyr::filter(df,!is.na(col))$id,colour=dplyr::filter(df,!is.na(col))$col))+
  theme_minimal()+theme(legend.position="none")

which throws this error:

Error: Discrete value supplied to continuous scale

Any idea?


Solution

  • Basically there are two approaches. One is to map the continuous variable to fill and the discrete text variable to color inside the aes call. And the other is to map the continuous variable to color inside aes and to map manually the text outside aes call.

    1st approach - mapping continuous scale to fill, and using a shape(pch = 21) that supports fill aesthetic. I used scale_fill_gradientn and manually defined where the colors should lie in the data range - values = scales::rescale(c(min(df$sig), median(df$sig), max(df$sig))).

    After that it is easy to map the discrete scale (repel labels) to the color aesthetic. However one needs to define the order of the levels to match the colors supplied in scale_colour_manual

    library(tidyverse)
    
    ggplot(df,aes(x = val1, y = val2))+
      geom_point(aes(fill = sig), cex=2, pch = 21)+
      scale_fill_gradientn("Significance",colors = c("darkred", "darkblue","darkred"),  values = scales::rescale(c(min(df$sig), median(df$sig), max(df$sig))))+
      geom_text_repel(data = dplyr::filter(df,!is.na(col)) %>%
                        mutate(col = factor(col, levels = col)),
                      aes(x = val1, y = val2, label = id, color = col), size = 6)+
      scale_colour_manual(values = dplyr::filter(df,!is.na(col))[,5])+
      theme_minimal()+
      theme(legend.position = "none")
    

    enter image description here

    2nd approach - specify color for geom_text_repel outside the aes call.

    ggplot(df,aes(x = val1, y = val2)) +
      geom_point(aes(color= sig), cex=2) + scale_colour_gradient2("Significance",low="darkred",mid="darkblue",high="darkred")+
      geom_text_repel(data = dplyr::filter(df,!is.na(col)), aes(x = val1, y = val2, label = id), color = dplyr::filter(df,!is.na(col))[,5], size = 6)+
      theme_minimal()+
      theme(legend.position = "none")
    

    enter image description here