Change line width in ggplot, not size
Asked Answered
K

1

19

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.

Kinin answered 19/10, 2018 at 2:54 Comment(1)
I just want to mention that a similar question was posted here: #63062101. Some of the answers may be a solution for your problem too.Hying
R
11

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.

Receptive answered 25/10, 2018 at 6:20 Comment(1)
Thanks for the useful info! But it also suggests to me that there is no solution to my problem. Customizing lty with hexadecimal numbers, and ignoring dot-dashes, you can have 15^2 = 225 dash/space combinations. Sounds great! But if you widen the line, it quickly becomes an awkward balancing act between making the line clearly visible and keeping lty numbers low enough that it doesn't end up looking like the lower half of my second figure, i.e. bad. I'm just learning ggplot, so I do appreciate the insight!Kinin

© 2022 - 2024 — McMap. All rights reserved.