Skip to content

How to get vertical lines in legend key using ggplot2 for geom_pointrange() type graphic #1389

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
swihart opened this issue Oct 25, 2015 · 6 comments
Labels
feature a feature request or enhancement
Milestone

Comments

@swihart
Copy link

swihart commented Oct 25, 2015

As outlined on SO:

For ggplot2 graphics that have a symbol for a point estimate and a vertical line representing a range about that estimate (95% confidence interval, Inter-quartile Range, Minimum and Maximum, etc) I cannot get the legend key to show the symbol with a vertical line. Since geom_pointrange() only has arguments for ymin and ymax, I would think the intended (default) functionality of geom_pointrange(show_guide=T) would be to have vertical lines (I say default because I understand that with coord_flip one could make horizontal lines in the plot). I also understand that having vertical lines in the legend key when the legend position is right or left will have the vertical lines "run together"...but for legends in the top or bottom having a vertical line through the symbol means that the key will match what appears in the plot.

Yet the approaches I've tried still put horizontal lines in the legend key:

## set up
library(ggplot2)
set.seed(123)
ru <- 2*runif(10) - 1
dt <- data.frame(x   = 1:10, 
                 y   = rep(5,10)+ru, 
                 ylo = rep(1,10)+ru, 
                 yhi = rep(9,10)+ru,
                 s   = rep(c("A","B"),each=5),
                 f   = rep(c("facet1", "facet2"), each=5))

Default show_guide=T for geom_pointrange yields desired plot but has horizontal lines in legend key where vertical is desired (so as to match the plot):

ggplot(data=dt)+
  geom_pointrange(aes(x     = x, 
                      y     = y, 
                      ymin  = ylo, 
                      ymax  = yhi, 
                      shape = s), 
                  size=1.1,
                  show_guide=T)+
  theme(legend.position="bottom")

enter image description here

An attempt with geom_point and geom_segment together yields desired plot but has horizontal lines in legend key where vertical is desired (so as to match the plot):

ggplot(data=dt)+
  geom_point(aes(    x = x, 
                     y = y, 
                 shape = s), 
             size=3,
             show_guide=T)+
  geom_segment(aes(   x = x, 
                   xend = x, 
                      y = ylo, 
                   yend = yhi), 
               show_guide=T)+
  theme(legend.position="bottom")

enter image description here

An attempt with geom_point and geom_vline together yields desired legend key but does not respect the ymin and ymax values in the plot:

ggplot(data=dt)+
  geom_point(aes(x=x, y=y, shape=s), show_guide=T, size=3)+
  geom_vline(aes(xintercept=x, ymin=ylo, ymax=yhi ), show_guide=T)+
  theme(legend.position="bottom")

enter image description here

How do I get the legend key of the 3rd graph but the plot of one of the first two?

Attempted answers on (as outlined on SO):

1. Using geom_point(show_guide=T) + geom_segment(show_guide=F) + geom_vline(show_guide=T) where vline is plotted out of range of data and then coord_cartesian() excludes the vline.

My solution involves plotting a vertical line with geom_vline(show_guide=T) for an x-value that is out of the bounds of the displayed x-axis along with plotting geom_segment(show_guide=F):

ggplot(data=dt)+
  geom_point(aes(x=x, y=y, shape=s), show_guide=T, size=3)+
  geom_segment(aes(x=x, xend=x, y=ylo, yend=yhi), show_guide=F)+
  geom_vline(xintercept=-1, show_guide=T)+
  theme(legend.position="bottom")+
  coord_cartesian(xlim=c(0.5,10.5))

enter image description here

2. Using grid and gtable:

library(grid)
library(gtable)

gg <-     
ggplot(data=dt)+
  geom_pointrange(aes(x     = x, 
                      y     = y, 
                      ymin  = ylo, 
                      ymax  = yhi, 
                      shape = s), 
                  size=1.1,
                  show_guide=T)+
  theme(legend.position="bottom")

gb <- ggplot_build(gg)
gt <- ggplot_gtable(gb)

seg <- grep("segments", names(gt$grobs[[8]]$grobs[[1]]$grobs[[4]]$children))
gt$grobs[[8]]$grobs[[1]]$grobs[[4]]$children[[seg]]$x0 <- unit(0.5, "npc")
gt$grobs[[8]]$grobs[[1]]$grobs[[4]]$children[[seg]]$x1 <- unit(0.5, "npc")
gt$grobs[[8]]$grobs[[1]]$grobs[[4]]$children[[seg]]$y0 <- unit(0.1, "npc")
gt$grobs[[8]]$grobs[[1]]$grobs[[4]]$children[[seg]]$y1 <- unit(0.9, "npc")

seg <- grep("segments", names(gt$grobs[[8]]$grobs[[1]]$grobs[[6]]$children))
gt$grobs[[8]]$grobs[[1]]$grobs[[6]]$children[[seg]]$x0 <- unit(0.5, "npc")
gt$grobs[[8]]$grobs[[1]]$grobs[[6]]$children[[seg]]$x1 <- unit(0.5, "npc")
gt$grobs[[8]]$grobs[[1]]$grobs[[6]]$children[[seg]]$y0 <- unit(0.1, "npc")
gt$grobs[[8]]$grobs[[1]]$grobs[[6]]$children[[seg]]$y1 <- unit(0.9, "npc")

grid.newpage()
grid.draw(gt) 

enter image description here

@has2k1
Copy link
Contributor

has2k1 commented Oct 25, 2015

You should probably redefine draw_key_pointrange.

draw_key_pointrange <- function(data, params, size) {
  grobTree(
    segmentsGrob(0.5, 0.1, 0.5, 0.9,
      gp = gpar(
        col = alpha(data$colour, data$alpha),
        lwd = data$size * .pt,
        lty = data$linetype,
        lineend = "butt"
      ),
      arrow = params$arrow
    ),
    draw_key_point(transform(data, size = data$size * 4), params)
  )
}

@hadley
Copy link
Member

hadley commented Nov 9, 2015

Minimal reprex:

df <- data.frame(x = 1:3, y = 1:3)
ggplot(df, aes(x, y, colour = factor(x))) +
  geom_pointrange(aes(ymin = y - 1, ymax = y + 1))

@hadley hadley added the feature a feature request or enhancement label Nov 9, 2015
@hadley hadley added this to the v1.1.0 milestone Nov 9, 2015
@hadley
Copy link
Member

hadley commented Nov 9, 2015

Fixed in 1ee49e2

@hadley hadley closed this as completed Nov 9, 2015
@ajmoralesa
Copy link

ajmoralesa commented Oct 13, 2017

How could the opposite be achieved (horizontal line ranges in legends), if we used horizontal geom_pointrange ?

Minimal reprex (showing vertical lines in legends):

df <- data.frame(x = 1:3, y = 1:3)
ggplot(df, aes(x, y, colour = factor(x))) +
  geom_pointrange(aes(ymin = y - 1, ymax = y + 1))+
  coord_flip()

@hadley
Copy link
Member

hadley commented Oct 13, 2017

Try ggstance?

@smouksassi
Copy link

require(ggstance)
require(ggplot2)
 df <- data.frame(x = 1:3, y = 1:3)
ggplot(df, aes(x, y, colour = factor(x))) +
     geom_pointrangeh(aes(xmin = x - 1, xmax = x + 1))

yes this works ( luckily ggstance has the geom_pointrangeh version ! )

@lock lock bot locked as resolved and limited conversation to collaborators Jun 18, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
feature a feature request or enhancement
Projects
None yet
Development

No branches or pull requests

5 participants