xts icon indicating copy to clipboard operation
xts copied to clipboard

Problem with index class in plot.xts()

Open olliefr opened this issue 8 years ago • 2 comments

I am experiencing a problem in setting custom X axis labels for plot.xts() using axis(). I did some debugging and I have more or less found why this happens, but I have no idea why xts does what it does. Please advise, if you can.

First, I load the data set:

dow <- read.csv('data/DJA.csv', header = TRUE, stringsAsFactors = FALSE)
names(dow) <- c('Date', 'Value')
dow$Value <- as.numeric(dow$Value)
dow$Date <- as.Date(dow$Date, format = "%m/%d/%Y")

The head(dow) yields the following

             Value
1914-07-30 52.3197
1914-12-12 54.6200
1914-12-14 56.7600
1914-12-15 55.0700
1914-12-16 55.3500
1914-12-17 55.3600

And the data structure looks like this in the environment inspector:

xts-debugging-1

Now, I would like to plot it, so I do

plot(dow, xaxt='n')

xts-debugging-2

So plot figures out this is an xts object and plots it. Now I would like to set the values for X axis.

I do something like this, the exact line is not that important

at = format(index(dow), '%m') == '01' & format(index(dow), '%d') == '10'

This gives me a logical vector, which I can use to index into the values

axis(side=1, at=index(dow)[at], labels = format(index(dow)[at], '%Y'))

This does not work. It produces either an empty axis or a single value in the middle of the axis which does not make sense.

It does, however, work if I call plot.xts(), providing explicit value for x and y:

plot(x=index(dow), y=dow$Value, xaxt='n')

And the code for the labels is the same as the above.

So, to solve this I had an intuition that in the former case when plot.xts() indexes the dow object, it uses something wrong as an index, and then cannot match the values that I pass into axis() with the ones that it believes to be the values of the index. This turns out to be the case.

In the latter case, when the index and the values are passed in separately at some point index.xts() gets called which has the following code executed (I stepped through it in the debugger):

value <- indexClass(x)
 if (value[[1]] == "Date") 
    return(structure(.index(x)%/%86400, class = "Date"))

The value is indeed, a "Date", so the .index(x) values undergo this transformation before being used to produce the labels.

In the former case, when a single dow object is being passed, the plot.xts() calls .index(x) (through attr("index", x) or some such which returns a vector of plain nonsense) to extract the index and ignores that its type is a Date!

In plot.xts() the following line of code is run: xycoords <- xy.coords(.index(x), x[, 1])

And head(.index(x)) returns something like

-1749081600 -1737417600 -1737244800 -1737158400 -1737072000 -1736985600

And this somehow becomes the "labels" that it is using to match against those I provide with axis() call, and of course they never match.

I am working under assumption that xts-time series objects usually are indexed by Date values, so if it can plot them correctly using plot(dow) syntax, then it should also allow to set custom labels without requiring something like plot(x=index(dow), y=dow$Value) ![xts-debugging-1](https://cloud.githubusercontent.com/assets/967105/14938347/6de6935e-0f18-11e6-9f52-69c0d8613822.JPG).

If I got the essence of the problem right, why does it do the index transformation in one case, but not the other?

olliefr avatar Apr 30 '16 20:04 olliefr

What version of xts are you using? In the current master branch, y is set to NULL and not used. If you specify x and y args a different plot method is dispatched. Note the differences of the plots when running the following code snippet

library(xts)
data(sample_matrix)
sample.xts <- as.xts(sample_matrix)
# plot the Close
plot(x=index(sample.xts[,"Close"]), y=sample.xts$Close)
plot(sample.xts[,"Close"])

I am working under assumption that xts-time series objects usually are indexed by Date values...

This is not necessarily true, see ?xts and ?.index for a detailed explanation. Here is a snippet from the .index help file

The main accessor methods to an xts object's index is via the index and index<- replacement method. The structure of the index internally is now a numeric value corresponding to seconds since the epoch (POSIXct converted to numeric).

What are you trying to do with custom x axis labels? The plot.xts in the current master branch has several arguments for formatting the axes that should eliminate (most of the time) the need for users to specify and draw custom axes.

rossb34 avatar Apr 30 '16 21:04 rossb34

xts version 0.9-7.

I'll try to better explain what I meant. If the argument to plot.xts is indexed by values of class Date, is there any reason not to do

value <- indexClass(x)
 if (value[[1]] == "Date") 
    return(structure(.index(x)%/%86400, class = "Date"))

regardless of whether 'y=' parameter was used?

After all, a date value is a date value, regardless of whether y axis values are explicit (via y=) or implicit.

I'm trying to learn to make custom x axis labels, as in fully custom, so that I would not be constrained by any pre-built functionality.

olliefr avatar May 03 '16 09:05 olliefr

Closing a stale issue.

olliefr avatar Dec 05 '22 03:12 olliefr

@olliefr thanks for following up on this! Are you able to get the result you want with the current version of xts? If not, it might be worth opening a feature request.

joshuaulrich avatar Dec 05 '22 16:12 joshuaulrich

@joshuaulrich sadly, I cannot answer this question because I no longer work with this tech stack 😆 To be frank, I don't even remember how I came across this in the first place many years ago. I just wanted to close the stale issue because OCD 🥲 Thank you for a quick reply though 😊

olliefr avatar Dec 05 '22 17:12 olliefr

Okay, no worries! Just wanted to make sure you had a fix and see if there was something we could do to make this easier.

And reviewing / prioritizing issues takes time, so I really appreciate the clean-up.

joshuaulrich avatar Dec 05 '22 17:12 joshuaulrich