2

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.

SavedByJESUS
  • 3,262
  • 4
  • 32
  • 47
  • 3
    `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 Apr 08 '15 at 23:01
  • 2
    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 Apr 08 '15 at 23:32
  • @thelatemail thank you very much. I had never heard of the `cbind.data.frame` function. This is going to be very helpful! – SavedByJESUS Apr 09 '15 at 01:55
  • @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 Apr 09 '15 at 01:58
  • 2
    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 Apr 09 '15 at 02:38
  • Thank you once again @G.Grothendieck. I just realize objects of class `zoo` work with the very important `lag` function. Will surely learn more about it. – SavedByJESUS Apr 09 '15 at 03:19

1 Answers1

5

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).

LyzandeR
  • 37,047
  • 12
  • 77
  • 87
  • 1
    That doesn't keep the matrices in `ts` format though, which the OP explicitly wants. Thus you need to wrap it in `as.ts` as per my comment. – thelatemail Apr 08 '15 at 23:08
  • 1
    @thelatemail Thanks for the comment. You are absolutely right yes. I mention that at the end as per your comment but indeed it doesn't help the OP much in what he wants. I just wanted to give a little bit more insight as to what happens when using the `cbind` function. – LyzandeR Apr 08 '15 at 23:12
  • 2
    No problems, everything you've written is useful information and I upvoted. I just thought it needed a final mention to wrap it all up. – thelatemail Apr 08 '15 at 23:20
  • @LyzandeR Nothing is as satisfying as such a detailed and informative answer. You have my gratitude! Thank you for answering my question and teaching me R at the same time! – SavedByJESUS Apr 09 '15 at 01:59
  • 1
    Thank you very much for your kind and generous comments. It certainly helps to know that it was well received :). I am really happy I could be of help :). – LyzandeR Apr 09 '15 at 08:28