4

I would like to create a Plotly graph in R that is colored green when it is positive and red when it is negative.

I attempted to do this using two separate traces producing the fist plot below which is discontinuous. I then attempted to create a colored trace using the color column which I created by the code below. These are the only implementations that I can think of using the current version of plotly.

> str(results)
'data.frame':   804 obs. of  7 variables:
 $ date  : Date, format: "2014-03-06" "2014-03-07" "2014-03-10" ...
 $ 5yr   : num  32.9 32.5 32.9 32.8 32.8 ...
 $ 3y5   : num  32.4 32.1 32.5 32.4 32.4 ...
 $ spread: num  -0.488 -0.431 -0.438 -0.388 -0.452 ...
 $ pos   : num  NA NA NA NA NA NA NA NA NA NA ...
 $ neg   : num  -0.488 -0.431 -0.438 -0.388 -0.452 ...
 $ color : chr  "red" "red" "red" "red" ...

results$spread <- results[,3] - results[,2]
results$neg <- ifelse(results$spread < 0 , results$spread, NA)
results$pos <- ifelse(results$spread >= 0 , results$spread, NA)

plot_ly(results,
    x = ~dates,
    y = ~pos,
    type = 'scatter',
    mode = 'lines',
    line = list(color = 'green')) %>%
  add_trace(results,
            x = ~dates,
            y = ~neg,
            type = 'scatter',
            mode = 'lines',
            line = list(color = 'red')) %>%
  layout(xaxis = list(title = 'Date'),
         yaxis = list(title = 'Price'))

plotly graph produced with traces

plot_ly(results,
    x = ~dates,
    y = ~spread,
    type = 'scatter',
    mode = 'lines',
    color = ~color) %>%
  layout(xaxis = list(title = 'Date'),
         yaxis = list(title = 'Price'))

plotly graph produced with color

2 Answers 2

2

This was an interesting one. But after a while I realized you can get what you want by inserting a zero value at every zero crossing of your plot:

I think the code is self-explanatory (with the comments)

Here is the code - (with some faked data):

library(plotly)

#fake up some data
set.seed(123)
n <- 100
sdate <- as.Date("2014-03-06")
dt <- seq.Date(sdate,by="days",length.out=n)
results <- data.frame(dates=dt,v1=rnorm(n,32.6,0.2),v2=rnorm(n,32.6,0.2))
results$spread <- results[,3] - results[,2]


# find all the zero crossings
spd <- results$spread
lagspd <- c(spd[1],spd[1:(length(spd)-1)])
crs <- sign(spd)!=sign(lagspd)
results$crs <- crs

# now insert a zero row where there is a crossing
insertZeroRow <- function(df,i){ 
  n <- nrow(df)
  ndf1 <- df[1:i,] # note these overlap by 1
  ndf2 <- df[i:n,] # that is the row we insert
  ndf1$spread[i] <- 0
  ndf <- rbind(ndf1,ndf2)
}


i <- 1
while(i<nrow(results)){
  if (results$crs[i]){
    results <- insertZeroRow(results,i)
    i <- i+1
  }
  i <- i+1
}

# plot it now

results$neg <- ifelse(results$spread <= 0 , results$spread, NA)
results$pos <- ifelse(results$spread >= 0 , results$spread, NA)

plot_ly(results,
        x = ~dates,
        y = ~pos,
        type = 'scatter',
        mode = 'lines',
        line = list(color = 'green')) %>%
  add_trace(results,
            x = ~dates,
            y = ~neg,
            type = 'scatter',
            mode = 'lines',
            line = list(color = 'red')) %>%
  layout(xaxis = list(title = 'Date'),
         yaxis = list(title = 'Price'))

And here is the result:

enter image description here

Note you could make it better by interpolating the dates and spread value to get the correct x-axis crossing point, but I think it would not make a huge difference in most cases. If you did that you would need a date type that can represent hours of the day too (like as.POSIXct), in order to be able to specify the correct x-axis value.

Update:

Just to clear up any confusion, adding the zero rows are necessary. If you comment out the insertZeroRow call, you get this:enter image description here

Sign up to request clarification or add additional context in comments.

5 Comments

So this problem stems from the conditional assignment of the results$pos and results$neg series it seems?
No, not really. It stems from the fact that NAs are supposed to be represented as gaps in the lines. So you have to extend your curves down to zero (on the x-axis).
this works well but for large series, the issue with inserting using a while loop will be slow especially if there are a lot of crossing the x-axis.
The issue with this answer as well is that you are adding a data point at every zero crossing, this causes graphs to not be smooth due to the waypoint that is has to take through zero
Well, yes, but if that is an issue you can make a small modification to make it intersect at the point that would make it smooth. I even mentioned that in the answer.
0

basically you can change your first implementation in this part of code:

results$spread <- results[,3] - results[,2]
results$neg <- ifelse(results$spread < 0 , results$spread, NA)
results$pos <- ifelse(results$spread >= 0 , results$spread, NA)

adding = in the second line of code:

results$spread <- results[,3] - results[,2]
results$neg <- ifelse(results$spread <= 0 , results$spread, NA)
results$pos <- ifelse(results$spread >= 0 , results$spread, NA)

try, it should work removing the discontinuities

2 Comments

Doesn't work like that. Try it with my complete working sample. Comment out the insertZeroRow and see what you get.
See my second plot for example.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.