Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Partial dashed line ggplot

Tags:

r

ggplot2

I am new to R and have managed to make a multiple line figure in ggplot. Along the x axis I have 5 time points that are discrete measures. I would like the latter half of the lineplot to be dashed (see attached image), could anybody help me edit my current code to achieve this please? Any help would be greatly appreciated. Thank you in advance!

enter image description here

ggplot(longdata, aes(x = Time, y = Strength, colour = Group)) +
  stat_summary(fun  = mean,
               geom = "point",)+ 
  stat_summary(fun = mean,
               geom = "line", 
               aes(group = Group), size =1,
               show.legend = FALSE) + 
               geom = "errorbar",
               width = .1, alpha = 0.3) +
  xlab("Testing Timepoint") +
  ylab("Isometric Strength (N.m)") +
  bbtheme +
  scale_colour_manual(name = "Group",
                      labels = c("Once-weekly", "Twice-weekly"),
                      values = c("red1", "navyblue")) +
  expand_limits(y = 0)
like image 897
bbplot Avatar asked Jan 01 '23 03:01

bbplot


2 Answers

here is a suggested approach. It is suggested you post your dataset or an example dataset for future questions to make it easier to provide a solution to your question. Pasting the output of dput(your.data.frame) is one of the best ways to share a data frame here.

With that being said, here's an example dataset. We'll draw a line plot with it.

df <- data.frame(x=1:10, y=1:10)
p <- ggplot(df, aes(x,y)) + theme_bw()
p + geom_line()

enter image description here

Truly amazing. Let's say you want to split that line so that it's a dotted line after x=5, and solid before x=5. You can either:

  1. Make another column in the dataset, and map the aesthetics of linetype on that column (this is the preferred option), or

  2. Set a statement in-line to map the aesthetics of linetype. Less preferred, but also works.

We'll do #2 above, since it's straightforward enough. Something like this can work:

p + geom_line(aes(linetype=eval(x>5)))

enter image description here

Crap. It "worked", but you now have a gap. ggplot is plotting the line between points, so if a point is included in one set it's not in another. I drew a solid line between x=1 and x=4, then a dotted line between x=5 and x=10. We wanted it to draw a solid line between x=1 and x=5 and then a dotted line between x=5 and x=10. You see the problem now? We needed x=5 to be included in both sets.

The solution here is that we need two calls to geom_line. There might be another way, but this is the way I would do it:

p + geom_line(data=subset(df, x<=5), linetype=1) +
    geom_line(data=subset(df, x>=5), linetype=2)

enter image description here

That worked. Now if you want to add a legend with some labels, you need to specify "I want to add a legend for linetype" to ggplot. You do that by including linetype= within aes() for each of the geom_line calls. You can then specify aspects of that legend manually through scale_linetype_manual. Like this:

p + geom_line(data=subset(df, x<=5), aes(linetype='solid line')) +
    geom_line(data=subset(df, x>=5), aes(linetype='dotted line')) +
    scale_linetype_manual('type of line', values=c(2,1))

enter image description here

EDIT: Dealing with non-numeric x axis?

After your note, I'll incorporate what u/jpsmith included in their response here too, which is how to deal with non-numeric x axes. As @jpsmith indicated, you need to use stat_summary there, since geom_line will complain if you attempt to use it instead. Ensure that your x-axis values are a factor first, then extract levels(longdata$Time). You may first want to be sure levels(longdata$Time) is in the correct order, but it appears from the plot you show that it is. It may then be worthwhile to prepare the inclusion points for each type of line, being sure to include "Post" in both sets:

solid.line.points <- c('Pre', 'Mid', 'Post')
dashed.line.points <- c('Post', 'Mid.detraining', 'Post.detraining')

Then, using stat_summary, something like as @jpsmith posted. Note some key changes though, as applied to your case:

stat_summary(data=subset(df,Time %in% solid.line.points),
    fun=mean, geom="line",
    inherit.aes=FALSE, aes(x=Time, y=Strength, linetype='solid line')) +
stat_summary(data=subset(df,Time %in% dashed.line.points),
    fun=mean, geom="line",
    inherit.aes=FALSE, aes(x=Time, y=Strength, linetype='dashed line'))

You need to apply a group= aesthetic, and I believe the group you use in your longdata dataset may not work here... or it might, not sure. This is why I suggest overriding the aesthetics in both stat_summary calls and specifying group= for each one. If you get issues drawing the lines, you may have to specify group=1 for one and group=2 for another (I have not tried it, since I don't have your dataset). See the answer on this question in SO for some notes on using geom_line with a discrete x axis and the note about the group= aesthetic.

Also note that I set fun=mean. I am assuming you are plotting a line between the mean of your set of points... but you would better know what you want to plot there to ensure you map the correct y values.

like image 52
chemdork123 Avatar answered Jan 02 '23 17:01

chemdork123


Building off of what @chemdork123 proposed, but accounting for your non-numeric X axis, you could use stat_summary (you also need to add a group=1 in the aes().

library(ggplot2)
df <- data.frame(x=LETTERS[1:10], y=1:10)
ggplot(df, aes(x,y, group=1)) +
  stat_summary(data=subset(df,x %in% c("E","F","G","H","I","J")), fun.y=sum, geom="line", aes(linetype='solid line')) +
  stat_summary(data=subset(df,x %in% c("A","B","C","D","E")), fun.y=sum, geom="line", aes(linetype='dashed line'))

enter image description here

like image 22
jpsmith Avatar answered Jan 02 '23 16:01

jpsmith