Vivian Griffey
Vivian Griffey

Reputation: 13

ggplot2: combine fill and alpha legends

There are many questions out there pertaining to this topic, but none of the answers I have tried have worked for me so far.

I have a plot that is a heatmap with fill and alpha mapped to different values, i.e. different variables in my data create different colors and alpha values. I want to get a finished product here to see if this figure is worthwhile, so let's not discuss whether this is a good idea at the moment.

What I want to do is combine my fill and alpha legend such that I have the four different transparencies of blue, the four different transparencies of red, and for yellow. I can get those legends separately, or just one of them, but not two in one.

My best guess for code thus far has been

dummy <- data.frame(model=c(rep("X",23),rep("Y",23)), 
                    longvarname=rep(c("CBH","NDMI","CovType","CH","CBD","NDVI_NF_750","Slope","TPI_Valley_1200", "TPI_Ridge_1200",
                                      "TPI_Ridge_100","TPI_Valley_100", "TSHarv","Treat","RxBurn",
                                      "TSTreat","TSRx","Deficit","SpecHumid","MaxRH","MinTemp","MaxTemp", "MaxGustDir", "MaxGustSpd"),2),
                    vargrp=rep(c(rep("Veg",6), rep("Topo",5), rep("Mgmt",5),rep("Clim",7)),2),
                    value=runif(46, min=0, max=1),
                    binary_slope=sample(c("negative","positive", "zero"), 46, replace=TRUE))

ggplot(dummy, aes(x=model, y=longvarname)) +
  geom_tile(aes(fill=binary_slope, alpha=value))+ 
  scale_alpha_binned(breaks=c(0.4, 0.6, 0.8, 1))+
  facet_grid(vargrp~., scales='free_y', space="free_y")+
  xlab("Model")+
  ylab("Variable")+
  scale_fill_manual(values=c("midnightblue","yellow1","red4"))+
  # guides(fill=guide_legend(override.aes = list(fill=c(rep("#191970",4),
  #                                                     rep("#FFEA00",4),
  #                                                     rep("#8b0000",4)),
  #                                              alpha=rep(c(0.4,0.6,0.8,1),3))))+
  theme(panel.background = element_blank(),
        axis.text.x = element_text(angle = 45, hjust=1),
        strip.text.y = element_blank(), 
        axis.ticks = element_blank()) 

The above code produces both legends which you can see in the example I attached. If you uncomment the guides() lines, the error I am getting is Error in [[<-.data.frame(*tmp*, i, value = c("#191970", "#191970", : replacement has 12 rows, data has 3. But most of my efforts have just resulted in only the fill legend at alpha=1. Another thought I had which I thought might get me there was in guides(), putting the alpha hex codes in front of each color hex code and then making alpha guide = "none", but no dice.

Thanks very much for your help!

Upvotes: 1

Views: 884

Answers (1)

stefan
stefan

Reputation: 123783

Instead of making use of both fill and alpha one option would be to make use of just fill like so:

  1. Add a column with your desired fill colors to your dataset using e.g. a left_join.
  2. Manually compute your alpha levels using e.g. cut.
  3. Adjust the transparency of th colors according to the alpha values using colorspace::adjust_transparency
  4. Map the resulting colors on the fill aes and make use of scale_fill_identity. Add guide=guide_legend to get a legend.

library(ggplot2)
library(dplyr)
library(colorspace)

cols <- c(negative = "midnightblue", positive = "yellow1", zero = "red4")
cols <- tibble::enframe(cols, name = "binary_slope", value = "fill")

dummy <- left_join(dummy, cols, by = "binary_slope")
dummy <- mutate(dummy,
  alpha = cut(value, breaks = c(0, 0.4, 0.6, 0.8, 1), labels = c(0.4, 0.6, 0.8, 1)),
  alpha = as.numeric(as.character(alpha)),
  fill = colorspace::adjust_transparency(fill, alpha)
)

ggplot(dummy, aes(x = model, y = longvarname)) +
  geom_tile(aes(fill = fill)) +
  scale_fill_identity(guide = guide_legend()) +
  facet_grid(vargrp ~ ., scales = "free_y", space = "free_y") +
  xlab("Model") +
  ylab("Variable") +
  theme(
    panel.background = element_blank(),
    axis.text.x = element_text(angle = 45, hjust = 1),
    strip.text.y = element_blank(),
    axis.ticks = element_blank()
  )

Upvotes: 1

Related Questions