Background:
I have a kind of Gantt chart, composed of horizontal segments with different events marked by symbols of different shapes. I want the symbols to have exactly the same height as the segment (potential topic for next question!), and symbols should be center aligned within each segment.
Issue:
The problem is that different shapes seem to have different alignment. In my small example, shape
0, 3, 4, 5 are center aligned (four first symbols from left). In contrast, the circle and the two triangles are offset.
d1 <- data.frame(x = -1, xend = 7, y = 1, yend = 1)
d2 <- data.frame(x = 0:6, y = 1)
library(ggplot2)
ggplot(data = d1, aes(x = x, y = y)) +
geom_segment(aes(xend = xend, yend = yend), size = 8, color = "grey80") +
geom_segment(aes(xend = xend, yend = yend), color = "red") +
geom_point(data = d2, shape = c(0, 3, 4, 5, 1, 2, 6), size = 8) +
theme_void()
Zoom in on PDF output:
I have also desperately tried a geom_text
equivalent with unicode symbols. However, the alignment is now even harder to fathom.
geom_text(data = d2,
label = c("\u25A1", "\uFF0B","\u2715","\u25C7", "\u25CB", "\u25B3", "\u25BD"),
size = 8, vjust = "center")
No obvious hints in ?geom_point
, ?aes_linetype_size_shape
or ?pch
. I have googled "r plot align center justify symbol shape pch" - have I missed any keywords?
Question: How can I align different shapes without hardcoding?
Hold the Shift (or Ctrl) key and click the objects you want to distribute. On the Format tab, click the Align command, then select Distribute Horizontally or Distribute Vertically. The objects will be evenly spaced from one another.
Turn on the snap-to optionsClick the shape or other object, and then on the Shape Format tab, under Arrange, click Align. Do one or both of the following: To snap shapes or other objects to the closest intersection of the grid, click Snap to Grid. Gray highlighting indicating the option is selected turns on.
It's not really an answer, but didn't fit in a comment.
To me, the circle isn't worse than the square, and looking at all first 26 symbols (pch = 0:25
), it seems that (theoretically, not sure about various devices) only the triangles wouldn't fit your purpose.
I think it's generally reasonable that the point they represent sits at their mass center, because that's where the eye would expect it with the most common use cases of such symbols.
Proof for the mass center is here: https://github.com/wch/r-source/blob/91dda45a5e4e418d0efed17db858736a973d4996/src/main/engine.c
void GESymbol(...
case 2: /* S triangle - point up */
xc = RADIUS * GSTR_0;
r = toDeviceHeight(TRC0 * xc, GE_INCHES, dd);
yc = toDeviceHeight(TRC2 * xc, GE_INCHES, dd);
xc = toDeviceWidth(TRC1 * xc, GE_INCHES, dd);
xx[0] = x; yy[0] = y+r;
xx[1] = x+xc; yy[1] = y-yc;
xx[2] = x-xc; yy[2] = y-yc;
gc->fill = R_TRANWHITE;
GEPolygon(3, xx, yy, gc, dd);
break;
So you could of course modify the source here to say:
yy[0] = y + (r+yc)/2;
yy[1] = y - (r+yc)/2;
yy[2] = y - (r+yc)/2;
I have a feeling that this problem doesn't have a solution (at least not one that is reasonably feasible). The issue itself seems to be rooted in grid which is what ggplot2 is built upon. For example:
library(grid)
grid.newpage()
vp <- viewport()
pushViewport(vp)
grid.rect(x = 0.5 , y = 0.5 , width= 1 , height = 0.14)
grid.points(x = 0.1 , y = 0.5, pch = 0 ,size = unit(1,"in"))
grid.points(x = 0.3 , y = 0.5, pch = 24 ,size = unit(1,"in"))
grid.points(x = 0.5 , y = 0.5, pch = 25 ,size = unit(1,"in"))
Because of this I think it is highly unlikely that there will be any ggplot2 options that will fix it. As RolandASc points out I think your best bet is to modify the source data to adjust for the offset of the symbols though I believe this in practice is very risky and personally wouldn't advise doing it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With