5

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)
tjebo
  • 21,977
  • 7
  • 58
  • 94
bbplot
  • 51
  • 1
  • 2

2 Answers2

10

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.

chemdork123
  • 12,369
  • 2
  • 16
  • 32
  • Thank you for your help. I am struggling to amend this as my x axis does not have numbers so that I cannot use " x > 5" etc. I also cannot create a new column as I have had to melt the data into long form so that the column is an ID variable that does not change... the plot thickens :( – bbplot Apr 27 '20 at 20:48
  • Apologies for my inadequate formatting on here, by the way, this is my first post. I will be happy to upload my data frame but I am unsure how to edit a post. – bbplot Apr 27 '20 at 20:51
  • No worries. Thanks to @jpsmith for the suggestion. Note I've edited the answer to "factor" (pun intended) that in. – chemdork123 Apr 28 '20 at 18:14
  • Tjhank you so much for this i cannot express my gratitude enough! I am still not getting the desired dashed lines yet but i feel that i am close, it may just take some playing about. – bbplot Apr 28 '20 at 19:11
1

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

jpsmith
  • 11,023
  • 5
  • 15
  • 36