Reputation: 238
The overriding effort here is to change the legend shape for geom_line
to a geom_point
"dot", but retain the geom_line
in the plot. I would be grateful if someone could demonstrate how to create a cleaner geom_line2()
function (see below) which shows the geom_point
"dot" object in the legend.
I know that one could layer geom_point
and effect guides(color = FALSE)
for the geom_line
. However, I'd like to understand how to do this in the ggproto
code. I also know that with geom_point
one can override the legend shape, but this approach does not work for geom_line
.
I attempted to tinker with the GeomPath
object to change the draw_key = draw_key_path
to draw_key = draw_key_point
, but that failed for many reasons.
I also thought I might be able to achieve the desired effect using guides(color = guide_geom())
but, as is broadly advertised, this is an undocumented function. I was unable to make progress even after looking at the source code on GitHub.
The code snippet at the bottom produces the desired effect but is hacky.
# this is the default
library(tidyverse)
library(grid)
mydf <- tibble(month = rep(seq(1, 10, 1), 4),
olympian = sort(rep(c("apollo", "jove", "mercury", "minerva"), 10)),
weight = c(seq(51, 60, 1), seq(60, 78, 2),
seq(69.5, 56, -1.5), seq(55, 46, -1)))
ggplot(mydf, aes(month, weight, color = olympian)) +
geom_line(size = 1) +
theme_light() +
labs(title = "Default geom_line legend shape")
# here is what worked to achieve the effect
GeomLine2 <- GeomPath
GeomLine2$draw_key <- function(data, params, size) {
data$linetype[is.na(data$linetype)] <- 0
segmentsGrob(0.5, 0.5, 0.5, 0.5, # NOTE CHG HERE
gp = gpar(
col = alpha(data$colour, data$alpha),
lwd = 5, # NOTE CHG HERE
lty = data$linetype,
lineend = "round" # NOTE CHG HERE
),
arrow = params$arrow
)
}
geom_line2 <- function (mapping = NULL, data = NULL,
stat = "identity", position = "identity",
na.rm = FALSE, show.legend = NA, inherit.aes = TRUE,
...)
{
layer(data = data, mapping = mapping, stat = stat, geom = GeomLine2,
position = position, show.legend = show.legend, inherit.aes =
inherit.aes,
params = list(na.rm = na.rm, ...))
}
ggplot(mydf, aes(month, weight, color = olympian)) +
geom_line2(size = 1) +
theme_light() +
labs(subtitle = "Hacked geom_line legend shape")
This code also produces the undesirable side effect of setting all future calls to ggplot2::geom_line
to this same legend effect.
PS ... this answer would be helpful but it is for old ggplot2
Upvotes: 2
Views: 1679
Reputation: 2416
You can do it using the key_glyph
argument (added with ggplot2 3.2.0 in mid-2019):
ggplot(mydf, aes(month, weight, color = olympian)) +
geom_line(key_glyph = "point") +
theme_light() +
labs(title = "Updated geom_line legend shape")
Upvotes: 1
Reputation: 31452
Don't do it! (NB This is my favourite and it is not intended to be facetious or unhelpful). ggplot
tries to force you to do things in a specific way usually because there is a good reason. Representing lines with dots is less clear than using lines, so should typically be avoided. the most important aspect of a graph is how clearly does it convey information, not how pretty does it look. Don't compromise clarity for aesthetics.
But, if you really have a good reason why you are sure dots will be better then ...
Don't use ggplot. ggplot, as mentioned, prefers to to use its own philosophy of how graphs should be. Other tools are more forgiving and can easily accomodate this. E.g. this will be easy to do in base graphics
If you really need ggplot, and you really need dots to represent lines, then you could create a graph that uses geom_point instead of geom_line, and then use gtable to replace the legend in your line graph with the one from the points graph
g = ggplot(mydf, aes(month, weight, color = olympian)) + theme_light()
gt.l = ggplot_gtable(ggplot_build(g + geom_line(size = 1)))
gt.p = ggplot_gtable(ggplot_build(g + geom_point(size = 2)))
gt.l$grobs[15] = gt.p$grobs[15]
grid.draw(gt.l)
Note: if you don't know which grob needs to be replaced, you can search for "guide"
in the grob names to find the right one:
gt.l$grobs[grep('guide', gt.l$layout$name)] = gt.p$grobs[grep('guide', gt.p$layout$name)]
Upvotes: 3