xts
xts copied to clipboard
Allow for log scale of the y axis
Allow for log scale of the y axis. This is supported by base::plot and PerformanceAnalytics::Chart.TimeSeries so we should support it in plot.xts as well. Thanks to Gei Lin for the feature request via R-SIG-Finance.
just some notes for myself:
- use
devtools::load_all()to build and load the package from source w/ namespace and bindings that work for interactive debugging. just modifying andsource()-ing files runs into a plethora of issues. set_windowis called for each plot. it sets up the coordinate system so that subsequent plotting code renders in the correct place. there is only 1set_windowcall for eaxh xts plot, so the coordinates have to account for headers ANDmulti.panelscale_ranges()produces a unifiedylimfor all headers and multi.plot regions so that they each render in the correct location using their native coordinates- ultimately,
log='y''needs to be passed toplot.window()fromset_window(). - As an example, this happens quite easily during the
cs$add_frame(0,ylim=c(0,1),asp=0.5)which sets up the frame for the header. The parameters of this call are intertwined w/ the subsequenttext(xlim[1],0.5,..)call that actually produces the title.. While the particular frame for the title should not be log scale the negativeylimfrom the title affects subsequent processing. there are a few other similar situation - each call to
plot.xtsis treated as a single plot from anRperspective. i.e. the title, subtitles, labels, subcharts formulti.panelare all a single plot. if caller has already partitioned the screen usinglayout, the whole call to plot.xts renders intot he current plot area. eg:layout(matrix(1:2)); plot(getSymbols("SPY", auto.assign = F), multi.panel = T);plot(getSymbols("SPY", auto.assign = F), multi.panel = T). Same holds true if a nox xts plot is rendered into one of the plots areas
ideas:
- maybe bult the coordinate transform bottom up, instead of top down. i.e. calculate the header above the main area, instead of the main area below the header. on multi.panel, render panels above the last instead of below the first. just a thought
We can't simply use plot.window(..., log = "y") because the range for the raw data passed in may include values <= 0, which means log() returns NaN. We will need to scale the data so it only has positive values. And that means we will need to adjust the y-axis labels to represent the actual (not the positive-adjusted) values.
Taking the log of a value <= 0 can also be a problem if we add a panel to the plot. We use the xts plot object asp values to (re-)set the global/device ylim for each panel in order to put the panels in the correct relative locations on the device. This can create negative global ylim value(s) for one or more of the panels, which will cause plot.window(..., log = "y") to error.
So we may need to manually map in/out of log space. Here are some thoughts on what needs to happen if we go that route:
- Scale the raw data so the min is always 1 before taking the log, and map the y-axis labels (based on the raw data) to their respective scaled values
- May have to use
axTicks()for becausepretty()does not support log scales - Test if log needs to be global to the plot, or can be by panel (I think it can be by panel)
dont think the data itself should ever have values <= 0 . neither base nor quantmod plotting currently supports that kind of input. if more talking about negative ranges in the plot area introduced by the multi.plot > 1, then i probably reluctantly agree
Hmm, guess that wasn't clear. I'm referring to when the raw data has negative values. And that causes plot.window(..., log = "y") to error. So we can't just add log = "y" and be done. For example, plot.window(c(-1, 1), c(-1, 1), log = "y")
EDIT: never mind. I don't know what made me think you could plot negative values in log-scale with plot.default(). For example, this warns about the negative values and doesn't plot them: plot(-1:10, log = "y").