Henk Straten
Henk Straten

Reputation: 1445

How to add a custom legend for a bullet chart

I have the following dataset:

incidents.pct <- data.frame(
  measure=c("Total Events (%)", "Security Events (%)", "Filtered (%)", "Tickets (%)"),
  high=c(100,100,100,100),
  mean=c(45,40,50,30),
  low=c(25,20,10,5), 
  target=c(55,40,45,35),
  value=c(50,45,60,25))

That I use to create the following "bullet like" graph.

g <- ggplot(incidents.pct) +
  geom_bar(aes(measure, high),  fill="goldenrod2", stat="identity", width=0.5, alpha=0.2) +
  geom_bar(aes(measure, mean),  fill="goldenrod3", stat="identity", width=0.5, alpha=0.2) +
  geom_bar(aes(measure, low),   fill="goldenrod4", stat="identity", width=0.5, alpha=0.2) +        
  geom_point(aes(measure, target), colour="red", size=2.5) 

This works, however I would like to include a custom legend that explains the colours. So just a colour sign with fe "low", "medium", "value" etc...

Any advise on how to include this?

Upvotes: 2

Views: 783

Answers (3)

Prem
Prem

Reputation: 11983

Another option could be to reshape your data before plotting.

library(ggplot2)

#reshape data for plotting
library(tidyverse)
df <- incidents.pct %>%
  gather(fe, fe_value, -measure, -target, -value) 
df$fe <- factor(df$fe, levels=c("high", "mean", "low"))

ggplot(df, aes(x=measure, y=fe_value, fill=fe)) +
  geom_bar(stat="identity", alpha=0.3) +
  scale_fill_manual(values=c("goldenrod2", "goldenrod3", "goldenrod4")) +
  geom_point(aes(measure, target), colour="red", size=2.5) +
  theme_bw()

Output plot is: enter image description here

#sample data
> dput(incidents.pct)
structure(list(measure = structure(c(4L, 2L, 1L, 3L), .Label = c("Filtered (%)", 
"Security Events (%)", "Tickets (%)", "Total Events (%)"), class = "factor"), 
    high = c(100, 100, 100, 100), mean = c(45, 40, 50, 30), low = c(25, 
    20, 10, 5), target = c(55, 40, 45, 35), value = c(50, 45, 
    60, 25)), .Names = c("measure", "high", "mean", "low", "target", 
"value"), row.names = c(NA, -4L), class = "data.frame")

Upvotes: 1

Tino
Tino

Reputation: 2101

Seems that I'm a bit late and @Z.Lin's answer is just fine, but I post mine as well because I think it points out the nice property of ggplot2, namely the use of long data. Once you have your data in a tidy form, it is easy to print (yes, it takes more lines to make transform the data ;-)):

incidents.pct %>% 
  # ggplot2 likes long data:
  gather(key = key, value = val, high, mean, low) %>% 
  # give nice order to factors:
  mutate(key = factor(key, levels = c("high", "mean", "low"))) %>% 
  # arrange and group to take difference such that we can stack the values (sum up to 100):
  arrange(measure, desc(key)) %>% 
  group_by(measure) %>% 
  mutate(val = val-ifelse(is.na(lag(val)), 0, lag(val))) %>% 
  # plot it, giving aes in ggplot, not necessarily below:
  ggplot(aes(x = measure, y = val, fill = key)) +
  # use geom_col if you want to use identity anyway:
  geom_col(width = .5, alpha = .2) +
  # just change the y-value in the aes, don't add it to legend:
  geom_point(aes(y = target), colour = "red", size = 2.5, show.legend = FALSE) +
  # now define your colours:
  scale_fill_manual(values = c("goldenrod2", "goldenrod3", "goldenrod4"))

Upvotes: 0

Z.Lin
Z.Lin

Reputation: 29125

In ggplot, a legend is automatically generated for an aesthetic option if it's inside aes(). So the following workaround with scale_fill_manual() will give you a legend:

ggplot(incidents.pct) +
  geom_col(aes(measure, high, fill = "high"), width=0.5, alpha=0.2) +
  geom_col(aes(measure, mean, fill = "mean"), width=0.5, alpha=0.2) +
  geom_col(aes(measure, low, fill = "low"), width=0.5, alpha=0.2) +        
  geom_point(aes(measure, target), colour="red", size=2.5) +
  scale_fill_manual(name = "Legend",
                    values = c("high" = "goldenrod2", 
                               "mean" = "goldenrod3", 
                               "low" = "goldenrod4"),
                    breaks = c("high", "mean", "low"))

(Incidentally, geom_col() is equivalent to geom_bar(stat = "identity"), & looks neater.

plot

I would caution against using low alpha value with overlapping bars, however, as the legend's colours won't match the plot's colours exactly. It would be cleaner to pick three lighter shades, and leave alpha as 1.

Upvotes: 5

Related Questions