I am confused by zero-length xts objects with a non-zero width, here for convenience addressed as empty xts objects.
I think they are a good way to model securities for which there are no observations, e.g. delisted securities.
x=xts(matrix(numeric(0), dimnames=list(NULL, "Delist1")), as.Date(numeric(0)))
x
# Delist1
However, when it comes to merge empty securities, they simply disappear:
y=xts(1:3, as.Date(1:3))
names(y)="List1"
merge(x,y)
# List1
# 1970-01-02 1
# 1970-01-03 2
# 1970-01-04 3
This is particularly inconvenient when you have several time series, some of which might be empty:
z=xts(matrix(numeric(0), dimnames=list(NULL, "Delist2")), as.Date(numeric(0)))
L=list(x,y,z) # etc.
Reduce(merge, L)
# List1
# 1970-01-02 1
# 1970-01-03 2
# 1970-01-04 3
You lose the information about empty time series, while you would like to have a full column of NAs for each empty series.
Perhaps the simple rule of the thumb is simply not to use empty xts objects and use NAs:
x=c(Delist1=NA)
z=c(Delist2=NA)
L=list(x,y,z) # etc.
X=Reduce(merge.xts, L)
setNames(X, sapply(L, names))
# Delist1 List1 Delist2
# 1970-01-02 NA 1 NA
# 1970-01-03 NA 2 NA
# 1970-01-04 NA 3 NA
However, the first two series can't be NAs:
L=list(x,z,y) # etc.
Reduce(merge.xts, L)
# Error use:
do.call(merge.xts, L)
Anyway, if all elements of L are empty, do.call(...) still does not work and extra fixes are required.
Summing up:
- How should empty xts objects be used?
- Are they intended to model time series without observations?
UPDATE
This is a long comment to the solution proposed by @userR
In my fictional time window as.Date(1:3), it would be simple to create/define an empty time series with a NA for each date and this might work for theoretical cases.
In real world scenarios, when you query data along a given time window, you never get all days filled.
To clarify, say you query data for securities A and B over the period 2000-02-01/2000-02-04.
The NA-based empty series should be like:
(delist=xts(rep(NA,4), as.Date("2000-02-01")+0:3))
# [,1]
# 2000-02-01 NA
# 2000-02-02 NA
# 2000-02-03 NA
# 2000-02-04 NA
Actual, data returned by provider, might be like:
A
# [,1]
# 2000-02-01 1
# 2000-02-02 2
# 2000-02-03 3
B
# [,1]
# 2000-02-01 1
# 2000-02-02 NA
# 2000-02-03 3
Assume there is no 2000-02-04 date because this is not a trading day.
Merging gives:
merge(A,B, delist)
# A B delist
# 2000-02-01 1 1 NA
# 2000-02-02 2 NA NA
# 2000-02-03 3 3 NA
# 2000-02-04 NA NA NA
Clearly the NA in [2,2] is an actual missing value, the last row of NAs is artificially induced by the definition of delist.
Instead, the single NA approach does not involve these problems:
delist=NA
merge(A,B, delist)
# A B delist
# 2000-02-01 1 1 NA
# 2000-02-02 2 NA NA
# 2000-02-03 3 3 NA
We cannot know exactly in advance the trading dates returned. Different markets/exchanges/securities implement different conventions, so identifying trading days before querying for them would be at least impractical, if at all possible.
UPDATE: Persistence
There is also a more subtle issue.
Above, securities A and B have a persistent representation based on actual observations. When the context requires it, for example in a merge(), NAs are added to fill misalignment gaps.
Defining, as above, a third security C based on the dates of A and B makes for a weak definition because, when you change the portfolio mix, you are also changing the definition of this security, but C is always the same security for which the data source returned no data.
So, IMHO, C can be modelled as a NA, a NULL, a zero-length xts, etc., but not as a context sensitive value.
delistseries only depends on the number of rows in your longest series returned from your query? If this is the case, can you not createdelistafter you query your data? This way you can have the property of not artificially adding more NAs than necessary while being able to storedelistas axtsobject. You can do something like thisxts(matrix(rep(NA, length(y)), dimnames=list(NULL, "Delist1")), order.by = index(y))- acylamdelistalways has something to merge to, otherwise what is the point of analyzingdelistalone? You can always definedelistprogrammatically based on the longest series. That way you don't have to know how long is the longest series, the program knows. After your merge, thedelistcolumn no longer depends on the initial definition, so removing any existing series would not break anything. I though agree that redefiningmerge()to accept an empty xts is a better idea, but I can't think of a solution thus far. - acylam