2

There always seems to be some small but noticeable offset between where tick marks end and where the panel starts.

minimum working example

This is more evident when axis limits are specified so that the axis ends on a tick (see above). e.g., On the y-axis, the highest tick is too high and the lowest tick is too low.

Code to reproduce:

library(ggplot2)
p <- 
  ggplot(mtcars,aes(mpg, wt)) + 
    geom_point() + 
    scale_y_continuous(limits=c(1,5), expand=c(0,0))

p <- p + theme_few()
p <- p + theme(axis.ticks=element_line(size=0.5),axis.ticks.length = unit(0.5, "cm")) # enlarged for clarity, although this happens regardless

How can I fix this?

Edit:

In using the accepted answer on my own data, I noticed this also changes how points at the axis extrema are clipped. Is there any way to correctly align ticks and the panel, but not change this behavior? I don't want to remove the points, just still hide the portions outside the panel.

minimum working example

tjebo
  • 21,977
  • 7
  • 58
  • 94
user2455117
  • 153
  • 2
  • 10

2 Answers2

8

it's not the position that's off, but the panel border that is being clipped,

library(ggplot2)
p <- ggplot(mtcars,aes(mpg, wt)) + geom_point() + scale_y_continuous(limits=c(1,5),expand=c(0,0))
p <- p + theme_bw()
p <- p + theme(axis.ticks=element_line(size=3),
               panel.border = element_rect(size = 3),
               axis.ticks.length = unit(0.5, "cm"))

g <- ggplotGrob(p)

g$layout$clip <- "off"
library(gridExtra)
grid.arrange(p, g, ncol=2)

enter image description here

Edit: The above observation wasn't meant to be a solution; as noted in the comments turning off the clipping for the whole panel is problematic for the points that leak out of the panel area. A possible workaround is to add a new grob to the gtable, e.g. by extracting it from panel and adding it on top without clipping.

library(ggplot2)
p <- ggplot(mtcars,aes(mpg, wt)) + geom_point(size=20) + scale_y_continuous(limits=c(1,5),expand=c(0,0))
p <- p + theme_bw()
p <- p + theme(axis.ticks=element_line(size=2),
               panel.border = element_rect(size = 2),
               axis.ticks.length = unit(0.5, "cm"))

g <- ggplotGrob(p)
panel_id <- g$layout$name == "panel"
border <- tail(g$grobs[panel_id][[1]][["children"]], 1)
g <- gtable::gtable_add_grob(g, border, 
                             t = min(g$layout$t[panel_id]),
                             l = min(g$layout$l[panel_id]),
                             name = "border",
                             clip = "off")

library(gridExtra)
grid.arrange(p, g, ncol=2)
baptiste
  • 75,767
  • 19
  • 198
  • 294
  • Is there any way to retain this panel width but still clip data points at the limit? (Not remove them altogether, just hide the portion outside.) **[edited original post to embed an example image]** – user2455117 Dec 06 '17 at 14:33
  • I wasn't really suggesting removing the clipping; as you found out it yields unpleasant side-effects. IMO the proper solution would be to get the panel border to be drawn on top of the panel (and therefore not clipped), but I'm pretty sure that's already been reported several times. – baptiste Dec 08 '17 at 22:11
  • I wasn't able to find a way to manually order ggplot2 elements, inside or outside of gridExtra. Could you reference an example? Otherwise, the only (admittedly hacky) way I can think to do that is to overlay a blank unclipped panel on top of the original plot. – user2455117 Dec 11 '17 at 14:40
  • 3
    I just want to let you know that the easier way of turning the panel clipping off is to use `+ coord_cartesian(clip = "off")`. – teunbrand Feb 06 '21 at 14:03
1

Easier is to turn off clipping with coord_cartesian(clip = "off")

library(ggplot2)

ggplot(mtcars,aes(mpg, wt)) + 
  geom_point() + 
  scale_y_continuous(limits=c(1,5), expand=c(0,0)) +
  coord_cartesian(clip = "off") +
  theme_classic()
#> Warning: Removed 3 rows containing missing values (`geom_point()`).

Created on 2023-03-18 with reprex v2.0.2

tjebo
  • 21,977
  • 7
  • 58
  • 94