Chris Chris - 3 months ago 14
R Question

R - ggplot - overlying line plot with single points, empty series for geom_points() causes error

Problem with empty series for geom_point

I am trying to create a repeatable routine for a line plot for simulated price series which are overlayed with points marking the buy (blue dots) and sell (red dots) points.




In some cases there are no buy or sell points which creates an empty object for geom_point so that it gives and error and prevents completing the chart.




Example which works (buy and sell points exist)

Time <- c(seq(1,12,1))
Prices <- c(36.23, 35.87, 36.18, 36.54, 35.96, 36.68, 37.16, 37.69, 38.15, 38.61, 39.91, 40.45)
TradingPrice_BUY <- c(36.23,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA)
TradingPrice_SELL <- c(NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,39.91,NA)
Trades<-data.frame(Time ,Prices, TradingPrice_BUY, TradingPrice_SELL)

library(ggplot2)
ggplot(data=Trades,aes(x=Time,y=Prices,group=1)) +
theme_bw() + geom_line() + xlab("Time") + ylab("Price") +
geom_point(data=Trades, aes(x=Time,y=TradingPrice_BUY),color="blue",shape=19) +
geom_point(data=Trades, aes(x=Time,y=TradingPrice_SELL),color="red",shape=19)






Example for case where sell points don't exist

Error: Discrete value supplied to continuous scale


Time <- c(seq(1,12,1))
Prices <- c(36.23, 35.87, 36.18, 36.54, 35.96, 36.68, 37.16, 37.69, 38.15, 38.61, 39.91, 40.45)
TradingPrice_BUY <- c(36.23,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA)
TradingPrice_SELL <- c(NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA)
Trades<-data.frame(Time ,Prices, TradingPrice_BUY, TradingPrice_SELL)

library(ggplot2)
ggplot(data=Trades,aes(x=Time,y=Prices,group=1)) +
theme_bw() + geom_line() + xlab("Time") + ylab("Price") +
geom_point(data=Trades, aes(x=Time,y=TradingPrice_BUY),color="blue",shape=19) +
geom_point(data=Trades, aes(x=Time,y=TradingPrice_SELL),color="red",shape=19)






I tried to work around it but got another error

Error: Aesthetics must be either length 1 or the same as the data (1): x, y, group


ggplot(data=Trades,aes(x=Time,y=Prices,group=1)) +
theme_bw() +geom_line()+xlab("Time") + ylab("Price")+
geom_point(data=subset(Trades,!is.na(Trades$TradingPrice_BUY)),aes(x=Time,y=TradingPrice_BUY),color="blue",shape=19) +
geom_point(data=subset(Trades,is.na(Trades$TradingPrice_BUY)), aes(x=1,y=Prices[1]),color=NA) +
geom_point(data=subset(Trades,!is.na(Trades$TradingPrice_SELL)), aes(x=Time,y=TradingPrice_SELL),color="red",shape=19) +
geom_point(data=subset(Trades,is.na(Trades$TradingPrice_SELL)),aes(x=1,y=Prices[1]),color=NA)






How to work around this issue?

Answer

When plotting with ggplot, you need to keep your data in a "tidy" format for things to work nicely. Rather than adding different layers for different point, you should reshape your data and let the aesthetics takes care of formatting for you. Here I used reshape2::melt to "tidy" up your data. For example

point_data <- na.omit(reshape2::melt(Trades[c(1,3,4)], "Time"))
ggplot(data=Trades,aes(x=Time, y=Prices)) + 
    theme_bw() + geom_line() + xlab("Time") + ylab("Price") +
    geom_point(data=point_data, aes(x=Time, y=value, color=variable), shape=19) + 
    scale_color_manual(values=c(
        "TradingPrice_BUY"="blue", 
        "TradingPrice_SELL"="red"), guide=FALSE)

This should work fine