2
votes

When the cbind function is used to combine 2 or more matrices, the resulting matrix inherits the column names. An easy example of this fact is the following. I have two (2x2) matrices m1 and m2. The columns of m1 are a and b; the columns of m2 are c and d. If I cbind m1 and m2, I obtain a matrix with 4 columns named: a, b, c and d.

> m1 <- matrix(1:10, ncol = 2)
> colnames(m1) <- letters[1:2]
> m2 <- matrix(11:20, ncol = 2)
> colnames(m2) <- letters[3:4]
> 
> M <- cbind(m1, m2)
> M
     a  b  c  d
[1,] 1  6 11 16
[2,] 2  7 12 17
[3,] 3  8 13 18
[4,] 4  9 14 19
[5,] 5 10 15 20

However, I just realized that if the matrices m1 and m2 contain time series data, the naming convention for the resulting matrix after a cbind changes.

> m3 <- ts(m1)
> m4 <- ts(m2)
> M2 <- cbind(m3, m4)
> M2
Time Series:
Start = 1 
End = 5 
Frequency = 1 
  m3.a m3.b m4.c m4.d
1    1    6   11   16
2    2    7   12   17
3    3    8   13   18
4    4    9   14   19
5    5   10   15   20

As you can see, the column names of the M2 are prefixed by the names of the matrices they originally belong to, which is my problem. I would like to keep the matrices in time series format, but avoid the new naming convention. As I read the documentation for cbind, I discovered the deparse.level argument, but it was of no help:

M2 <- cbind(m3, m4, deparse.level = 0)
M2

Of course, the easy workaround is to just create a character vector combining the column names of the original matrices and use it to name the columns of the new matrix; however, I was curious to know if something could be done about it.

> column_names <- c(colnames(m3), colnames(m4))
> colnames(M2) <- column_names
> M2
Time Series:
Start = 1 
End = 5 
Frequency = 1 
  a  b  c  d
1 1  6 11 16
2 2  7 12 17
3 3  8 13 18
4 4  9 14 19
5 5 10 15 20

Your help is very much appreciated.

1
as.ts(cbind.data.frame(m3,m4)) gives an identical result, but I'm not sure it's any simpler than what you are already doing.thelatemail
If you use "zoo" series instead of "ts" series then the naming will be as requested: library(zoo); z3 <- as.zoo(m3); z4 <- as.zoo(m4); cbind(z3, z4) ` .G. Grothendieck
@thelatemail thank you very much. I had never heard of the cbind.data.frame function. This is going to be very helpful!SavedByJESUS
@G.Grothendieck Thank you for your answer. Are zoo series just like time series? Do they work with the class time series functions in R or does the package come with its own set of functions? I'll definitely try to know more about it?SavedByJESUS
The zoo package is based on the ideas of ts and mostly provides methods for the same generic functions that ts uses. There are several vignettes (pdf documents) that come with the package if you want to learn more.G. Grothendieck

1 Answers

5
votes

First of all cbind is a generic function meaning that every time you use it you use a (slightly) different version of cbind according to the class of the object (ts in your case)

This can be seen by:

> library(pryr)
> ftype(cbind)
[1] "internal" "generic" 

And:

> methods(cbind)
[1] cbind.data.frame cbind.ts*        cbind.zoo  

So essentially every time you use cbind with a ts object you the cbind you use is essentially cbind.ts. Let's see the source code:

> getAnywhere(cbind.ts)
A single object matching ‘cbind.ts’ was found
It was found in the following places
  registered S3 method for cbind from namespace stats
  namespace:stats
with value

function (..., deparse.level = 1) 
{
    if (deparse.level != 1) 
        .NotYetUsed("deparse.level != 1")
    .cbind.ts(list(...), .makeNamesTs(...), dframe = FALSE, union = TRUE)
}
<bytecode: 0x0000000006429410>
<environment: namespace:stats>

You can see above the .NotYetUsed("deparse.level != 1") part of the code. After a quick look at the documentation about .NotYetUsed reveals that:

In order to pinpoint missing functionality, the R core team uses these functions for missing R functions and not yet used arguments of existing R functions (which are typically there for compatibility purposes).

i.e. you cannot use deparse.level with anything else than 1. And that is why you get the 'strange' prefixes (the matrices' names) in your column names (the .makeNamesTs probably does that.

Finally, in order to help with your question (because I rambled on for too long I think :)) you can use the cbind.data.frame method to begin with on your ts objects like this (this is the method applied to matrices):

> cbind.data.frame(m3,m4)
  a  b  c  d
1 1  6 11 16
2 2  7 12 17
3 3  8 13 18
4 4  9 14 19
5 5 10 15 20

But you will need to convert that again to ts as mentioned by @thelatemail in the comments unfortunately (so it is not that helpful I suppose).