Reputation: 28928
I'd like to plot a single line which is multi-coloured, and the colours are based on the corresponding value in a factor. For instance, a time series of daily stock close price, where the days it has gone up by more than a certain amount are in blue, and the days it has gone done by a lot are in red, and the other days it is in boring black.
My data is in an xts
object (with the factor put in there with as.numeric(myfactor)
), and I'd like to be using the quantmod chartSeries
or chart_Series
functions. But if that is impossible then something using plot
would be enough.
Some sample data:
library(xts)
x = xts( data.frame( v=(rnorm(50)+10)*10, type=floor(runif(50)*4) ),
order.by=as.Date("2001-01-01")+1:50)
And I can plot it like this:
library(quantmod)
chartSeries(x$v)
addTA(x$type, type='p')
I.e. I felt it'd be easier to match the information in the bottom chart with the top one if coloured line segments were being used.
Upvotes: 5
Views: 1860
Reputation: 59365
So here's a ggplot
solution. It's a bit more involved, but offers more sophisticated formatting options.
library(quantmod)
sp500 <- getSymbols("^GSPC", from="2015-01-01", auto.assign=FALSE)
sp500 <- Cl(sp500) # extract close
sp500 <- merge(sp500, dailyReturn(sp500)) # add daily returns
sp500 <- merge(lag(Cl(sp500),1), sp500) # merge with close lagged by 1 day
names(sp500) <- c("ymin", "ymax", "return")
library(ggplot2)
library(scales)
df <- with(sp500,
data.frame(xmin=c(lag(index(sp500),1)),
xmax=index(sp500),
ymin, ymax, return))
df$status <- with(df,ifelse(return>0.01,"up",ifelse(return< -0.01,"down","neutral")))
ggplot(df) +
geom_segment(aes(x=xmin, xend=xmax, y=ymin, yend=ymax, color=status)) +
scale_color_manual(values=c(up="green", down="red", neutral="grey50"),
breaks=c("up","down"),
labels=c("Gain > 1%", "Loss > 1%")) +
scale_x_date(breaks=date_breaks("months"), labels=date_format("%b"))+
labs(x=NULL, y="Closing Price", title="S&P 500") +
theme(panel.background =element_rect(fill="black"),
panel.grid = element_blank())
As with the other answer the basic idea is to draw segments color coded based on the magnitude of the gain/loss. So we start by extracting closing prices, add a column of returns, and then add another column of closing prices lagged by 1 day. Then we create a data.frame from this with two columns of dates, also lagged by 1 day. Then we add a column (status
) to indicate if the gain/loss was > 1%. Then we use this to drive geom_segment(...)
, color coding by status
. In the scale_color_manual(...)
call, we set the colors to red and green, and exclude the neutral color from the legend. The rest is all formatting.
Upvotes: 2
Reputation: 263352
This code started out as a minor mod of the example code on ?segments
and that is why the title of the graph looks odd ,but I decided to leave it in anyway. The logic is that the terms inside the "[.]" will "pick" colors based on the difference between successive values formed as the difference between tail(y,-1)
and head(y,-1)
, with the default being "black" and the the thresholds being 1 in this case, but that could easily be changed:
set.seed(123)
x <- 1:12; y <- rnorm(12)
plot(x, y, main = "arrows(.) and segments(.)")
s <- seq(length(x)-1)
arrows(x[s], y[s], x[s+1], y[s+1],
col= c("black", "red", "blue")[1+ # default=1
(tail(y,-1)-head(y,-1) < -1) + # big down (1+1)
2*(tail(y,-1)-head(y,-1) > 1) ] ) # big up (1+2)
If you just wanted boring line segments you can use the segments
function rather than arrows
.
I realize on reading the question once again that you said you already had a factor variable in your xts-object, although my understanding is that xts-objects might not be capable of holding factor-type columns, since they are elaborations of the zoo-class and the coredata is an R matrix (hence no level attributes). But perhaps the quant-guys have a workaround for that? So that would be yet another reason for you to post some data, and PLEASE do use dput
for presenting the object. Building xts-objects from console output is a real hassle.
Upvotes: 1