0

Sorry if this is a duplicate question but other answers I have tried have not resulted in my desired plot.

I would like to colour code this graph based upon whether the value is positive or negative which I have termed the "phase".

Where the geom_line() hits just above or below 0 I would like it to colour either "positive" or "negative" - I thought the following would work but it doesn't seem to. Can someone tell me where I am going wrong? Thanks.

original plot]1

Data

structure(list(Date = c(1876, 1877, 1878, 1879, 1880, 1881), 
    value = c(4.95, -10.0416666666667, 2.95, 12.85, 7.8, -4.76666666666667
    ), phase = structure(c(3L, 1L, 3L, 3L, 3L, 1L), .Label = c("negative", 
    "neutral", "positive"), class = "factor")), row.names = c(NA, 
-6L), class = c("data.table", "data.frame"), .internal.selfref = <pointer: 0x7ffb06817ce0>)

Code

## create a column to determine if the phase is negative, positive or neutral i.e. 0

dd_avg$phase<-factor(sign(dd_avg$value), (-1):1, c('negative', 'neutral', 'positive'))
dd_avg
 

## plot it up

ggplot(data=dd_avg, aes(x=Date, y=value, group=Date, colour=phase))+
  geom_line()

plot2

Sophie Williams
  • 338
  • 1
  • 3
  • 14
  • What goes wrong? Please can you include a figure of your output – Richard Telford Jun 28 '21 at 15:57
  • Hi Richard, if I use the code geom_line() nothing will plot and I get the error geom_path: Each group consists of only one observation. Do you need to adjust the group aesthetic? If I put geom_line(group=1) etc it produces the second plot where it creates two separate groups and connects the first and last point – Sophie Williams Jun 28 '21 at 16:08

1 Answers1

5

By manually assigning colours, you're triggering the automatic grouping that ggplot2 does, which will just draw seperate lines for positive and negative. However, even if you override the grouping, you're still faced with the problem that a line segment can only have a single colour and lines that cross the 0-line will likely give problems.

The usual solution to such problems is to interpolate the data to specify exact cross-over points. However, this is annoying to do. Instead, we can use ggforce::geom_link2() that already interpolates and use after_stat() to apply colouring after interpolation. If you need your cross-over points to be exact, you might want to interpolate yourself.

library(ggplot2)
library(ggforce)

df <- data.frame(
  x = seq(as.Date("1890-01-01"), as.Date("2020-01-01"), by = "1 year"),
  y = rnorm(131)
)

ggplot(df, aes(x, y)) +
  geom_link2(aes(colour = after_stat(ifelse(y > 0, "positve", "negative"))))

Created on 2021-06-28 by the reprex package (v1.0.0)

There are similar questions here and here where I've suggested the same solution.

teunbrand
  • 33,645
  • 4
  • 37
  • 63