In a geom_col plot, I would like to use outline color for one variable related to each rectangle and fill for a different variable. The trouble is that the line surrounds the whole rectangle rather than existing inside the rectangle. So where bars meet vertically, only the line color of the upper or of the lower bar shows. I want them both to show, within their respective bars. Is this feasible? Example that doesn’t do what I want:
df.toy <- data.frame(x = rep(1:2, each = 3),
y = c(1, 2, 1, 1, 3, 1),
color = c("a", "b", "b", "b", "c", "a"),
fill = c("d", "e", "e", "e", "e", "e"))
ggplot() + geom_col(data = df.toy, aes(x = x,
y = y, color = color, fill = fill), linewidth = 2) +
scale_fill_manual(values = c("yellow", "orange")) +
scale_color_manual(values = c("blue", "red", "green"))
Result:
Possibly similar posts: geom_bar define border color with different fill colors is about the effect of putting an argument within aes(), or a different topic. https://github.com/tidyverse/ggplot2/issues/4126 seemed to address a similar issue related to linetype rather than (line) color, but I didn’t understand it well.
AFAIK, most geoms’ strokes expand from the midline, so when you have two overlapping borders, the strokes will naturally overlap instead of expanding separately. One kludgey way around this would be to manually make your boxes a little less high so that their height is correct once the line has been thickened. Beware, though, this approach won't work consistently as you change your plot size; each ratio of height offset to linetype
will only work perfectly for a given plot size.
library(dplyr)
df.toy |>
arrange(color |> desc()) |>
mutate(mid = lag(cumsum(y),1,0) + y/2, .by = x) |>
ggplot() +
geom_tile(aes(x = x, y = mid, height = y-0.1, width = 0.8,
color = color, fill = fill),
linewidth = 2) +
scale_fill_manual(values = c("yellow", "orange")) +
scale_color_manual(values = c("blue", "red", "green"))