Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Customising ggplot legend

Tags:

r

ggplot2

Suppose I have some data and would like to plot the forecasts and 95% prediction intervals using ggplot. The following code works and gives an acceptable legend.

ExampleData = data.frame(t = 1:10, f = rep(1,10), Lower = rep(0.5, 10), Upper = rep(1.5, 10))
library (ggplot2)
ggplot(data = ExampleData)+
  geom_point(aes(x=t, y =f, colour = "Forecasts"))+
  geom_point(aes(x=t, y =Lower), shape=95)+
  geom_segment(aes(x=t, y = Lower, xend=t, yend = f))+
  geom_point(aes(x=t, y =Upper), shape=95)+
  geom_segment(aes(x=t, y = f, xend=t, yend = Upper))+
  geom_vline(aes(xintercept = 11, colour = "95% PI"))+
  scale_colour_manual(values = c("95% PI" = "black","Forecasts" = "red"),
                      guide = guide_legend(override.aes = list(
                        linetype = c('solid','blank'),
                        shape = c(NA,16))))+
  scale_x_continuous(name="Time", limits=c(1, 10), breaks = c(0,5,10))+
  labs(title="Example")+
  labs(y = "Forecasts")+
  theme_bw()

rplot of the example

I would also like to add a horizontal line, at say y = 0.75. However, the legend does not seem to be able to accommodate a horizontal and vertical line. Is there a way that I can use ggplot to do this? What about including shorter, horizontal lines, also? For example, the upper and lower points are represented by short horizontal lines.

like image 383
JLO Avatar asked Sep 07 '25 03:09

JLO


1 Answers

Update taking into account OP's comment:

If the aesthetics color, fill, group, ... are defined, the element is automatically added to the legend. Therefore, something like geom_line(aes(color = 'foo')) will produce a legend element named foo, but geom_line(color = 'black') won't.

library (ggplot2)
ExampleData = data.frame(t = 1:10, f = 1, Lower = 0.5, Upper = 1.5)

g <- ggplot(data = ExampleData, aes(x = t))

g <- g + geom_point(aes(y = f, color = "95% PI"), size = 0)
g <- g + geom_errorbar(aes(ymin = Lower, ymax = Upper, color = "95% PI"))
g <- g + geom_point(aes(y = f), color = "red")
g <- g + geom_line(aes(y = 0.75, color = "Threshold"))

g <- g + scale_colour_manual(values = c("95% PI"    = "black",
                                        "Threshold" = "green"),
                             guide = guide_legend(override.aes = list(
                               linetype = c('blank', 'solid'),
                               shape    = c('|',      NA),
                               size     = c(5,        0.5))))

g <- g + theme_bw()
g <- g + scale_x_continuous(name="Time", breaks = c(0, 5, 10))
g <- g + labs(title = "Example")
g <- g + labs(y = "Forecasts")

print(g)

This will produce an output like this: Results of the RPlot above

To get the vertical line in the legend, I added an invisible geom_point (size to 0, see below for the reason of this). Note that the color aesthetics is defined, therefore the legend will be populated by this element. To add an horizontal line to the plot at fix y value, and to the legend, the easiest is to use a geom_line element and to set the color aesthetics. Finally, to display the red points and exclude them from the legend, you must not set the color aesthetics.

g <- g + geom_point(aes(y = f, color = "95% PI"), size = 0)
g <- g + geom_errorbar(aes(ymin = Lower, ymax = Upper, color = "95% PI"))
g <- g + geom_line(aes(y = 0.75, color = "Threshold"))
g <- g + geom_point(aes(y = f), color = "red")

Regarding the legend, to have a vertical bar, rather than an horizontal bar (default), you can use the shape |. This is a rather small shape by default (at least on my computer), so you may have to increase its size a bit. You can do this by playing with the guides. Note that geom_errorbar or geom_segment do not have the shape property. To use a shape rather than a line, you need to add the invisible geom_point first.

g <- g + scale_colour_manual(values = c("95% PI"    = "black",
                                        "Threshold" = "green"),
                             guide = guide_legend(override.aes = list(
                               linetype = c('blank', 'solid'),
                               shape    = c('|',      NA),
                               size     = c(5,        0.5))))

Previous answer:

Assuming you want the points, vertical segments, the horizontal segments and the horizontal lines all in the legend, here is my suggestion:

ExampleData = data.frame(t = 1:10, f = rep(1,10), Lower = rep(0.5, 10), Upper = rep(1.5, 10))
library (ggplot2)

tipw <- 0.2
g <- ggplot(data = ExampleData)

g <- g + geom_segment(aes(x = t, y = Lower, xend = t, yend = Upper, color = "95% PI"))
g <- g + geom_segment(aes(x = t - tipw / 2, y = Lower, xend = t + tipw / 2, yend = Lower, color = "Minimum"))
g <- g + geom_segment(aes(x = t - tipw / 2, y = Upper, xend = t + tipw / 2, yend = Upper, color = "Maximum"))
g <- g + geom_point(aes(x = t, y = f, color = "Forecasts"))
g <- g + geom_line(aes(x = t, y = 0.75, color = "Some value"))

g <- g + scale_x_continuous(name="Time", limits=c(1 - tipw/2, 10 + tipw/2), breaks = c(0, 5, 10))
g <- g + labs(title = "Example")
g <- g + labs(y = "Forecasts")

g <- g + scale_colour_manual(values = c("95% PI"     = "black",
                                        "Forecasts"  = "red",
                                        "Minimum"    = "orange",
                                        "Maximum"    = "green",
                                        "Some value" = "light blue"),
                             guide = guide_legend(override.aes = list(
                                     linetype = c('solid', 'blank', 'solid', 'solid', 'solid'),
                                     shape    = c(NA,      16,      NA,      NA,      NA))))
g <- g + theme_bw()

print(g)

Rplot of the example

tipw <- 0.2
g <- ggplot(data = ExampleData)

Creates the plot with your data. The tipw variable is the width of the tips (using the same unit as the x-axis). See below.

g <- g + geom_segment(aes(x = t, y = Lower, xend = t, yend = Upper, color = "95% PI"))
g <- g + geom_segment(aes(x = t - tipw / 2, y = Lower, xend = t + tipw / 2, yend = Lower, color = "Minimum"))
g <- g + geom_segment(aes(x = t - tipw / 2, y = Upper, xend = t + tipw / 2, yend = Upper, color = "Maximum"))
g <- g + geom_point(aes(x = t, y = f, color = "Forecasts"))
g <- g + geom_line(aes(x = t, y = 0.75, color = "Some value"))

Adds the lines, segments and points. The elements will be stacked in the order you write them. So here the short horizontal bars at the tips will be drawn first, then the vertical bar, then the points on top of the bars and then the arbitrary horizontal line on top of the rest.

Remember you can do some logic in aesthetics of ggplot. The short horizontal bars are defined here as segments starting from x = t - tipw / 2 and ending to x = t + tipw / 2. The long horizontal bar can be manually defined to y = 0.75. This works because you can always use a vector of length 1 for aesthetics and the value will be used for all points (aes(x = t, y = 0.75) gives the same result as aes(x = t, y = rep(0.75, length(t)))).

g <- g + scale_x_continuous(name = "Time", limits=c(1 - tipw/2, 10 + tipw/2), breaks = c(0, 5, 10))

You must include the xstart and xend of the horizontal segments for them to be drawn. That's why the limits are extended by tipw/2. Otherwise the horizontal bar on the far left and far right won't be visible, and a warning will be thrown.

Warning messages:
1: Removed 2 rows containing missing values (geom_segment). 
2: Removed 2 rows containing missing values (geom_segment). 
like image 79
Slagt Avatar answered Sep 09 '25 02:09

Slagt