I want to plot the share of three variables of each geographical point.
I thought this would be easily done with RGB values and I managed to do it simply like this:
data <- data.frame(
Variable = c("Point1", "Point2", "Point3"),
R = c(0.4, 0.6, 0.3),
G = c(0.3, 0.2, 0.4),
B = c(0.3, 0.2, 0.3),
x = c(1, 2, 4),
y = c(5, 6, 7)
)
color_plot <- rgb(data$R, data$G, data$B)
ggplot() +
geom_point(data = data, aes(x = x, y = y, color = color_plot))
The problem is that the color values are discrete, making the legend that ggplot produces useless. Is there an elegant way to add a meaningful (continuous) color scale to this plot? Ideally, the names of R, G and B would be on the scale.
Since the three variable's values all add to one, you can use a ternary RGB as your legend. Whilst this technically works, it is very difficult for the viewer to cross reference the colors, and in reality I would probably use a different method, depending on how many points you have on the plot.
However, if you have a ternary RGB as a grob called my_legend
, you can add it as a custom legend to your plot like this:
ggplot(data) +
geom_point(aes(x = x, y = y, color = color_plot), size = 4) +
scale_color_identity() +
guides(custom = guide_custom(title = "Relative share", grob = my_legend,
width = unit(5, "cm"),
height = unit(5 * sin(pi/3), "cm"))) +
theme_bw(16) +
theme(legend.title = element_text(hjust = 0.5))
The difficult part is creating my_legend
. You could do this using ggtern
or create one from scratch, as I have done here:
cols <- do.call("rbind", lapply(seq(0, 1, 0.01), function(r) {
do.call("rbind", lapply(seq(0, 1 - r, 0.01), function(g) {
data.frame(red = r, green = g, blue = 1 - r - g)
}))
}))
cols$rgb <- rgb(cols)
cols$x <- cols$red * 0.5 - cols$green * 0.5 + 0.5
cols$y <- sin(pi/3) / 2 * (1 + cols$blue - (cols$red + cols$green))
my_legend <- (ggplot(cols, aes(x, y, colour = rgb)) +
geom_point(size = 1) +
annotate("text", x = c(-0.2, 1.2, 0.5), y = c(-0.1, -0.1, sin(pi/3) + 0.2),
label = c("G", "R", "B"), size = 5) +
annotate("segment",
x = c(0, 1, 0.5) + c(0, 0.1, -0.1) * sin(pi/3),
xend = c(1, 0.5, 0) + c(0, 0.1, -0.1) * sin(pi/3),
y = c(-0.1, 0.05, sin(pi/3) + 0.05),
yend = c(-0.1, sin(pi/3) + 0.05, 0.05),
arrow = arrow(length = unit(2, "mm"), type = "closed")) +
scale_color_identity() +
theme_void()) |>
ggplotGrob()