Reputation: 3519
I am trying to get something similar to Glen_b's answer to this question. Basically, I want an arrow at the top of my boxplot to indicate that there are more outliers out of scale using ggplot.
I can get the main part of the plot to look as I'd like, but I am running into problems getting a sensible legend.
I have put together an example:
library(plyr)
library(dplyr)
library(ggplot2)
#create test data frame with extreme outlier
mpg_test <- mpg
mpg_test[1,"hwy"] = 250
pmax = 50
pmin = min(mpg_test$hwy)
outliersabovepmax <- filter(mpg_test, hwy > pmax) %>% mutate(hwy= pmax)
#basic plot without cropping/adding arrows
p <- ggplot(mpg_test, aes(x = class, y=hwy, colour = class)) + geom_boxplot()
p
I can get the main body of the plot to look as I'd like:
#plot that I want
p2 <- p +
geom_segment(data = outliersabovepmax,
aes(xend = class, y = hwy-1, yend = hwy+1,
linetype = "Outlier above"),
arrow = arrow(), show.legend = T) +
coord_cartesian(ylim = c(pmin, pmax))
But I'd like the legend to not show the arrow on the colour legend.
I would normally expect to be able to do something along the lines of:
p2 +
guides(colour = guide_legend(override.aes = list(linetype = "blank")))
but because the boxplot is also using linetype I just end up with black squares if I do this. I also don't want to set show.legend = F
for geom_segment
as I want to have the arrow in the legend.
As a bonus I'd like the arrow in the legend to be rotated to face up. But this is not as important.
Plot that I get:
Plot that I'd like (made in paint):
Upvotes: 3
Views: 1061
Reputation: 29095
Maybe something like this?
p +
# hide legend in actual segment layer
geom_segment(data = outliersabovepmax,
aes(xend = class, y = hwy-1, yend = hwy+1),
arrow = arrow(), show.legend = F)+
# have invisible segment layer that shows legend
geom_segment(data = outliersabovepmax,
aes(xend = class, y = hwy-1, yend = hwy+1, linetype = "Outlier above"),
arrow = arrow(), alpha = 0, show.legend = T) +
# override alpha for linetype legend
guides(linetype = guide_legend(override.aes = list(alpha = 1))) +
coord_cartesian(ylim = c(pmin, pmax))
And if you want to change the layout of geom_segment
's legend key, you can dig into the underlying code for GeomSegment
:
library(grid)
GeomSegment2 <- ggproto("GeomSegment2", GeomSegment,
draw_key = function (data, params, size) {
data$linetype[is.na(data$linetype)] <- 0
segmentsGrob(
# vertical instead of horizontal line
0.5, 0.1, 0.5, 0.9, #0.1, 0.5, 0.9, 0.5,
gp = gpar(col = alpha(data$colour, data$alpha),
lwd = data$size * .pt,
lty = data$linetype,
lineend = "butt"),
arrow = params$arrow)
})
geom_segment2 <- function (mapping = NULL, data = NULL, stat = "identity",
position = "identity", ..., arrow = NULL, arrow.fill = NULL,
lineend = "butt", linejoin = "round", na.rm = FALSE,
show.legend = NA, inherit.aes = TRUE) {
layer(data = data, mapping = mapping, stat = stat, geom = GeomSegment2,
position = position, show.legend = show.legend, inherit.aes = inherit.aes,
params = list(arrow = arrow, arrow.fill = arrow.fill,
lineend = lineend, linejoin = linejoin, na.rm = na.rm,
...))
}
Usage:
p +
# hide legend in actual segment layer
geom_segment(data = outliersabovepmax,
aes(xend = class, y = hwy-1, yend = hwy+1),
arrow = arrow(), show.legend = F)+
# have invisible segment layer that shows legend
geom_segment2(data = outliersabovepmax,
aes(xend = class, y = hwy-1, yend = hwy+1, linetype = "Outlier above"),
arrow = arrow(), alpha = 0, show.legend = T) +
# override alpha for linetype legend
guides(linetype = guide_legend(override.aes = list(alpha = 1))) +
coord_cartesian(ylim = c(pmin, pmax))
Upvotes: 1