user2352714
user2352714

Reputation: 356

How can I plot an outline or border around a dashed line in ggplot2?

I have a plot where I am trying to compare the linear regression lines for various groups of interest to the linear fit for the sample as a whole.

library(ggplot2);library(curl)
df<-read.csv(curl("https://raw.githubusercontent.com/megaraptor1/mydata/main/example.csv"))
df$group<-as.factor(df$group)
ggplot(df %>% group_by(group),
       aes(x,y,col=group))+
  geom_point(size=2.5,shape=21,aes(fill=group),col="black")+
  scale_x_continuous(breaks=seq(1,3.25,0.25))+
  scale_y_continuous(breaks=seq(0,17,1))+
  geom_smooth(data=df %>% group_by(group),
              formula=y~x,method="lm",level=0.9,se=F)+  
  geom_smooth(data=df,linetype="dashed",fill="white",colour="black",size=.75)+
  geom_smooth(data=df,linetype="dashed",fill="white",colour="black",size=1)+
  labs(color="group",fill="group")+
  guides(color=guide_legend(ncol=2),fill=guide_legend(ncol=2))+
  theme_classic()+
  theme(legend.position = "none")

enter image description here

In this graph the fit for all specimens is represented by a black dashed line. However this line doesn't stand out very much from the surrounding data and is hard to see. I would like to put a white outline surrounding the dashed black line in order to make it pop and stand out from the surrounding data.

However, I have been unable to do so via ggplot2. I know it is possible to do so via a non-dashed line by plotting a slightly thicker white line behind the black line. But if I try to do so with a dashed line it does not produce the desired affect (see above code), because changing the thickness of the line also changes the length and spacing of the dashes, and hence the two do not line up and make the black dashed line pop out.

Is there any way to set the border of a dashed line in ggplot to plot a white border around a black dashed line?

Upvotes: 2

Views: 1530

Answers (3)

TarJae
TarJae

Reputation: 79184

Or we could just add alpha=0.2 to geom_point()

ggplot(df %>% group_by(group),
       aes(x,y,col=group))+
  geom_point(size=2.5,shape=21,aes(fill=group),col="black", alpha = 0.2)+
  scale_x_continuous(breaks=seq(1,3.25,0.25))+
  scale_y_continuous(breaks=seq(0,17,1))+
  geom_smooth(data=df %>% group_by(group),
              formula=y~x,method="lm",level=0.9,se=F)+  
  geom_smooth(data=df,linetype="dashed",fill="white",colour="black",size=.75)+
  geom_smooth(data=df,linetype="dashed",fill="white",colour="black",size=1)+
  labs(color="group",fill="group")+
  guides(color=guide_legend(ncol=2),fill=guide_legend(ncol=2))+
  theme_classic()+
  theme(legend.position = "none")

Output: enter image description here

Upvotes: 0

teunbrand
teunbrand

Reputation: 38023

We could reparameterise the data as segments to represent a dashed line. First we get the data we need for the fit.

library(tidyverse)
library(ggplot2)

df<-read.csv(curl::curl("https://raw.githubusercontent.com/megaraptor1/mydata/main/example.csv"))
df$group<-as.factor(df$group)

line <- layer_data(ggplot(mapping = aes(x, y)) + geom_smooth(data = df))
#> `geom_smooth()` using method = 'loess' and formula 'y ~ x'

Then, we can interpolate the line at fixed distances to yield us the dashed segments.

dash_length <- 0.025
xrange <- range(line$x)
xstart <- seq(xrange[1], xrange[2], by = dash_length * 2)
xend <- xstart + dash_length

linedat <- data.frame(
  x = xstart,
  xend = xend,
  y = approx(line$x, line$y, xout = xstart, rule = 2)$y,
  yend = approx(line$x, line$y, xout = xend, rule = 2)$y
)

You can then add these dashed segments as a wide white line and a thinner black line to give the appearance of a white outline.

ggplot(df %>% group_by(group),
       aes(x,y,col=group))+
  geom_point(size=2.5,shape=21,aes(fill=group),col="black")+
  scale_x_continuous(breaks=seq(1,3.25,0.25))+
  scale_y_continuous(breaks=seq(0,17,1))+
  geom_smooth(data=df %>% group_by(group),
              formula=y~x,method="lm",level=0.9,se=F)+  
  geom_segment(
    data = linedat,
    aes(x = x, y = y, xend = xend, yend = yend),
    colour = "white", size = 2, lineend = "round"
  ) +
  geom_segment(
    data = linedat,
    aes(x = x, y = y, xend = xend, yend = yend),
    colour = "black", size = 1
  ) +
  labs(color="group",fill="group")+
  guides(color=guide_legend(ncol=2),fill=guide_legend(ncol=2))+
  theme_classic()+
  theme(legend.position = "none")

Created on 2021-04-05 by the reprex package (v1.0.0)

Note that this method can give skewed results if the line is not straight.

Upvotes: 3

Rui Barradas
Rui Barradas

Reputation: 76641

The trick is to plot the smoothers first, then plot the dashed line on top of them.
In order to have a white background and make the black dashed line stand out, plot a larger white line before the dashed black line.

library(ggplot2)
library(curl)

df <- read.csv(curl("https://raw.githubusercontent.com/megaraptor1/mydata/main/example.csv"))
df$group <- factor(df$group)

ggplot(df, aes(x, y, color = group)) +
  geom_point(aes(fill = group), size = 2.5, shape = 21, color = "black") +
  geom_smooth(formula = y ~ x, method = "lm", se = FALSE) +  
  # First plot the background solid white line
  geom_smooth(
    method = loess,
    formula = y ~ x,
    se = FALSE,
    linetype = "solid",
    colour = "white",
    size = 2
  ) +
  # Then a dashed black line on top of it
  # This line is narrower than the background one
  geom_smooth(
    method = loess,
    formula = y ~ x,
    se = FALSE,
    linetype = "dashed",
    colour = "black",
    size = 1.5
  ) +
  #
  scale_x_continuous(breaks = seq(1,3.25,0.25)) +
  scale_y_continuous(breaks = seq(0,17,1)) +
  theme_classic() +
  theme(legend.position = "none")

enter image description here

Upvotes: 1

Related Questions