Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Change line width in ggplot, not size

Tags:

r

ggplot2

I see several posts about changing line width in ggplot. The answers, while informative and effective for the OP, change the line size. That is, ggplot seems to treat lines as a series of units, and size increases both the length and width of each unit, making other adjustments coarser.

I'm hoping there is a way to make lines wider (thicker) without also affecting length and, in turn, the scale of dashes.

I borrowed some code from https://cran.r-project.org/web/packages/ggplot2/vignettes/ggplot2-specs.html to illustrate what I mean:

library(ggplot2)

#A simple plot with manually set dashes.
#The first numeral is units of dash length, the second units in the gap in hexadecimal. 
lty <- c("11", "18", "1f", "81", "88", "8f", "f1", "f8", "ff")
linetypes <- data.frame(y = seq_along(lty), lty = lty) 

ggplot(linetypes, aes(0, y)) + 
  geom_segment(aes(xend = 5, yend = y, linetype = lty)) + 
  scale_linetype_identity() + 
  geom_text(aes(label = lty), hjust = 0, nudge_y = 0.2) +
  scale_x_continuous(NULL, breaks = NULL) + 
  scale_y_reverse(NULL, breaks = NULL)

Default size dashed lines.

#Altering the size changes the line width AND dash spacing.

ggplot(linetypes, aes(0, y)) + 
  geom_segment(aes(xend = 5, yend = y, linetype = lty), 
     size = 3) + 
  scale_linetype_identity() + 
  geom_text(aes(label = lty), hjust = 0, nudge_y = 0.3) +
  scale_x_continuous(NULL, breaks = NULL) + 
  scale_y_reverse(NULL, breaks = NULL)

Size 3 dashed lines.

Essentially what I want is thick lines with relatively thin and finely adjusted gaps. I think this is the same question asked another way: Is there a way to make lines that are all, say, "dashed" vary in width but not in the relative location of the dashes? Like this (which I totally faked):

Lines of different width, but the same size.

like image 654
Alexander F. Wall Avatar asked Oct 19 '18 02:10

Alexander F. Wall


1 Answers

From the ?par helpfile for "Line Type Specification" (bold added for emphasis):

Line types can either be specified by giving an index into a small built-in table of line types (1 = solid, 2 = dashed, etc, see lty above) or directly as the lengths of on/off stretches of line. This is done with a string of an even number (up to eight) of characters, namely non-zero (hexadecimal) digits which give the lengths in consecutive positions in the string. For example, the string "33" specifies three units on followed by three off and "3313" specifies three units on followed by three off followed by one on and finally three off. The ‘units’ here are (on most devices) proportional to lwd, and with lwd = 1 are in pixels or points or 1/96 inch.

This suggests that if we specify linetypes and line widths that vary appropriately (e.g. the thicker line is twice as wide as the thinner line, with on-off stretches half as long), we can achieve the desired effect of seemingly identical dash lengths at different line widths.

Note: the allowed characters in the linetype specification are c(1:9, "A":"F")), which means the shortest possible stretch length is 1 unit, while the longest is 15 units. This limits the number of different lines that can be created.

It works as expected with the base R plotting functions:

plot.new()
plot.window(xlim=c(0, 5), ylim=c(1, 3))
abline(h = 3, lty = "22", lwd = 8)
abline(h = 2, lty = "44", lwd = 4)
abline(h = 1, lty = "88", lwd = 2)
axis(2, at = 3:1, labels=c("22", "44", "88"), tick = FALSE, las = 1)

base R

For ggplot2, on the other hand, as far as I've been able to ascertain, the choice of graphics file device matters. The dashes align neatly for vector-based devices, but not necessarily for bitmap-based ones:

p <- ggplot(data.frame(y = seq(1, 3),
                       lty = c("11", "22", "44"),
                       lwd = c(8, 4, 2)), 
            aes(0, y)) + 
  geom_segment(aes(xend = 5, yend = y, 
                   linetype = lty,
                   lwd = lwd)) + 
  geom_text(aes(label = lty), hjust = 0, nudge_x = -0.3) +

  scale_linetype_identity() + 
  scale_size_identity() +
  scale_x_continuous(NULL, breaks = NULL) + 
  scale_y_reverse(NULL, breaks = NULL, expand = c(0.5, 0))

# bitmap devices
ggsave("test.bmp", p + ggtitle(".bmp"))
ggsave("test.jpg", p + ggtitle(".jpg"))
ggsave("test.png", p + ggtitle(".png"))
ggsave("test.tiff", p + ggtitle(".tiff"))
ggsave("test.wmf", p + ggtitle(".wmf"))

# vector devices
ggsave("test.eps", p + ggtitle(".eps"))
ggsave("test.pdf", p + ggtitle(".pdf"))
ggsave("test.svg", p + ggtitle(".svg"))

output from different devices

I haven't found anything that addresses this directly within ggplot2. Still, if you need one of the bitmap formats but identically spaced dashes are a top priority, you can output your plots to one of the vector-based devices first, and convert from one file format to another thereafter.

like image 131
Z.Lin Avatar answered Oct 08 '22 04:10

Z.Lin