rbokeh icon indicating copy to clipboard operation
rbokeh copied to clipboard

How to query y-axis limits of an already existing plot?

Open scizmeli opened this issue 8 years ago • 1 comments

I am trying to create an Rbokeh-powered shiny app where I keep on adding new data of different lengths on the same timeseries graph (x data is POSIXct). Since all the data are of different length,I do not use the data= option in ly_xx() plotting functions. As the last layer, I also need to plot centered rectangles depicting time events (that should cover the whole figure height). I try to achieve that with ly_crect(). I already have time event widths somewhere handy as a part of my dataset. But I have to compute rectangle heights myself.

First approach was to set y=NULL in ly_crect but this only plots horizontal lines. I would have expected that the rectangles automatically fill the whole figure height. This did not work.

As the second approach, I think of programmatically querying the yaxis limits of the already drawn data so that I can specify the rectangle heights. But I cannot find any way to achieve that (see below). How is it possible? thanks a bunch Servet

xx=Sys.time()+seq(1, 1500, by=100)
yy=runif(length(xx))*600

x=xx[c(1, 10, 15)]
xx2 = xx[c(3,5,10)]
yy2 = runif(length(xx2))*450

aa <- figure(width = 600) %>% 
  ly_lines(x=xx,y=yy) %>%
  ly_lines(x=xx2, y=yy2, color="red")
aa

#Here I need to be able to query programmatically y axis limits to draw full-height centered rectangles 
#But Neither of the following commands do work for querying the y axis limits so I have to set by hand (y values of 300 and height values of 600):

figure_data(aa)
aa$x$spec$ylim

aa <- aa %>% ly_crect(x=x,y=c(300,300,300),width=c(100000,20000, 30000), height=c(600,600,600))
aa

scizmeli avatar Jan 06 '17 14:01 scizmeli

Sorry for the delay and thanks for the reproducible example.

Unless you are dealing with extremely large data, recreating the entire figure from scratch would be the best solution here. That way you will know the overall height needed for the rectangles each time before you draw them. Here's an example.

get_y_lims <- function(dl) {
  ymin <- min(sapply(dl, function(a) min(a$y, na.rm = TRUE)))
  ymax <- max(sapply(dl, function(a) max(a$y, na.rm = TRUE)))
  # add some padding to the limits
  c(ymin, ymax) + c(-1, 1) * 0.07 * (ymax - ymin)
}

make_figure <- function(dl, x) {
  ylims <- get_y_lims(dl)

  p <- figure(width = 600, ylim = ylims)

  for (i in seq_along(dl))
    p <- ly_lines(p, x, y, data = dl[[i]])

  ly_rect(p,
    xleft = x,
    xright = x + c(10, 20, 30),
    ybottom = ylims[1],
    ytop = ylims[2])
}

data_list <- list()

# first set of data
data_list[[1]] <- data.frame(
  x = Sys.time() + seq(1, 1500, by = 100),
  y = runif(length(xx)) * 600
)

x <- data_list[[1]]$x[c(1, 10, 15)]

make_figure(data_list, x)

# now add your next set of data...
data_list[[2]] <- data.frame(
  x = data_list[[1]]$x[c(3, 5, 10)],
  y = runif(3) * -450 # make the change in y limits more drastic...
)

# redraw the figure
make_figure(data_list, x)

Another alternative if you want to stick more closely to the code you have, is to set the rectangle height to be something insanely huge that you know you'll never exceed. After you do that, you will need to explicitly set the y axis limits of your plot so that the rectangle doesn't influence the limits calculation. You can do this with the following:

aa <- aa %>% y_range(c(0, 600)

Then it is up to you to keep track of what you want the y axis limits to be, but you can keep calling y_range() on your object as many times as you wish to update its limits.

hafen avatar Feb 17 '17 08:02 hafen