1

So I have a simple dataset(table really) of 2 variables, a date and a dollar value(profit or loss). I am trying to plot a profit or loss graph and have the area y>0 shaded green, and y<0 red (where y=Profit or Loss). I have tried a few ways with ifelse loops and geom_area but getting nowhere.

Data: As requested, The dates I am collecting data from include the 14th of April until the 1st of September.

dput(upl2$Unrealised.Profit.or.Loss)
c(87.5, -46, 163.5, 194.5, 251.5, 392, 276.5, 424, 354.5, 194, 
152, 104, 2, 0, 113, 78.5)

Code for prof or loss:

y <- upl2$Unrealised.Profit.or.Loss
loss <- y < 0 
prof <- y > 0
even <- y == 0 

Code for plot:

ggplot(data = upl2, aes(x=Date, y=Unrealised.Profit.or.Loss, group=1)) + 
geom_point() + 
theme(axis.text.x = element_text(angle = 60, vjust = 0.5, hjust = 0.5)) + 
geom_line(size=.75) + 
geom_hline(yintercept=0, size=.5, color = "Blue") + 
labs(x = "Date", y = "Unrealised Profit or Loss", title = "Unreaslied Profit or Loss as of 7/6/2021")

Any suggestions would be awesome,
Cheers

  • 1
    please `dput(upl2$Unrealised.Profit.or.Loss)` and paste with your question to share your data – markhogue Jun 08 '21 at 01:16
  • 1
    It's easier to help you if you include a simple [reproducible example](https://stackoverflow.com/questions/5963269/how-to-make-a-great-r-reproducible-example) with sample input and desired output that can be used to test and verify possible solutions. – MrFlick Jun 08 '21 at 01:27
  • Does this answer your question? [How to fill geom\_polygon with different colors above and below y = 0?](https://stackoverflow.com/questions/27135962/how-to-fill-geom-polygon-with-different-colors-above-and-below-y-0) – camille Jun 10 '21 at 23:33

1 Answers1

0

While shading the area between intersecting lines may sound like an easy task, to the best of my knowledge it isn't and I struggled with this issue several times. The reason is that in general the intersection points (in your case the zeros of the curve) are not part of the data. Hence, when trying to shade the areas with geom_area or geom_ribbon we end up with overlapping regions at the intersection points. A more detailed explanation of this issue could be found in this post which also offers a solution by making use of approx.

Applied to your use case

  1. Convert your Date variable to a date time as we want to approximate between dates.
  2. Make a helper data frame using approx which interpolates between dates and adds the "zeros" or intersection points to the data. Setting the number of points n needs some trial and error to make sure that there are no overlaps visible to the eye. To my eye n=500 works fine.
  3. Add separate variables for losses and profits and convert the numeric returned by approx back to a datetime using e.g. lubridate::as_datetime.
  4. Plot the shaded areas via two separate geom_ribbons.

As you provided no sample data my example code makes use of some random fake data:

library(ggplot2)
library(dplyr)
library(lubridate)

# Prepare example data

set.seed(42)

Date <- seq.Date(as.Date("2021-05-16"), as.Date("2021-06-07"), by = "day")
Unrealised.Profit.or.Loss <- runif(length(Date), -1, 1)

upl2 <- data.frame(Date, Unrealised.Profit.or.Loss)

# Prepare helper dataframe to draw the ribbons
upl2$Date <- as.POSIXct(upl2$Date)

upl3 <- data.frame(approx(upl2$Date,  upl2$Unrealised.Profit.or.Loss, n = 500))
names(upl3) <- c("Date", "Unrealised.Profit.or.Loss")

upl3 <- upl3 %>% 
  mutate(
    Date = lubridate::as_datetime(Date),
    loss = Unrealised.Profit.or.Loss < 0,
    profit = ifelse(!loss, Unrealised.Profit.or.Loss, 0),
    loss = ifelse(loss, Unrealised.Profit.or.Loss, 0))

ggplot(data = upl2, aes(x = Date, y = Unrealised.Profit.or.Loss, group = 1)) +
  geom_ribbon(data = upl3, aes(ymin = 0, ymax = loss), fill = "red") +
  geom_ribbon(data = upl3, aes(ymin = 0, ymax = profit), fill = "green") +
  geom_point() +
  geom_line(size = .75) +
  geom_hline(yintercept = 0, size = .5, color = "Blue") +
  scale_x_datetime(date_breaks = "day", date_labels = "%B %d") +
  theme(axis.text.x = element_text(angle = 60, vjust = 0.5, hjust = 0.5)) +
  labs(x = "Date", y = "Unrealised Profit or Loss", title = "Unreaslied Profit or Loss as of 7/6/2021")

enter image description here

stefan
  • 90,330
  • 6
  • 25
  • 51
  • Absolute legend thats exactly what I was looking for. Thanks again! – nautilus432 Jun 09 '21 at 00:32
  • Im getting this error, ```Error in as.POSIXlt.character(as.character(x), ...) : character string is not in a standard unambiguous format```. As well as ```Error in seq.int(0, to0 - from, by) : 'to' must be a finite number``` Just wondering what these mean as well as this line, ```upl3 <- data.frame(approx(upl2$Date, upl2$Unrealised.Profit.or.Loss, n = 500))```. What does the n=500 represent? Cheers again – nautilus432 Jun 09 '21 at 00:48
  • I now think I get what the n=500 means, by setting the observations for upl3, however whatever number I put in the ```Error in seq.int(0, to0 - from, by) : 'to' must be a finite number``` pops up, unsure of where this is actually coming from – nautilus432 Jun 09 '21 at 00:59
  • Just fixed the error's. Cheers again have been an absolute legend! Only thing is it doenst accurately plot the upl2 data profit./loss rather the upl3 approximated, is that meant to be happening? – nautilus432 Jun 09 '21 at 01:10
  • You are welcome. Hm. What do you mean by "it doenst accurately plot"? But it sounds that it is not related to the solution I offered. Maybe you could make an edit to your post to clarify the issue or ask a new question? Best S. – stefan Jun 10 '21 at 06:52
  • Hey, I mean by not accurate as it doesnt plot the data from upl2 rather the approximated values, which makes the actual graph vs shaded graph very different. However i changed around some things and it makes it close enough, just the shading is not perfect. Thanks for the help though. – nautilus432 Jun 11 '21 at 07:50