Using cbind on an arbitrarily long list of objects
I would like to find a way to create a data.frame by using cbind()
to join together many separate objects. For example, if A, B, C & D are all vectors of equal length, one can create data.frame
ABCD with
ABCD <- cbind(A,B,C,D)
However, when the number of objects to be combined gets large, it becomes tedious to type out all of their names. Furthermore, Is there a way to call cbind()
on a vector of object names, e.g.
objs <- c("A", "B", "C", "D")
ABCD <- cbind(objs)
or on a list containing all the objects to be combined, e.g.
obj.list <- list(A,B,C,D)
ABCD <- cbind(obj.list)
Currently, the only workaround I can think of is to use paste()
, cat()
, write.table()
, and source()
to construct the arguments to cbind()
, write it as a script and source it. This seems like a very nasty kludge. Also, I have looked into do.call()
but can't seem to find a way to accomplish what I want with it.
The do.call
function is very useful here:
A <- 1:10
B <- 11:20
C <- 20:11
> do.call(cbind, list(A,B,C))
[,1] [,2] [,3]
[1,] 1 11 20
[2,] 2 12 19
[3,] 3 13 18
[4,] 4 14 17
[5,] 5 15 16
[6,] 6 16 15
[7,] 7 17 14
[8,] 8 18 13
[9,] 9 19 12
[10,] 10 20 11
First you need to get
the objects you want and store them together as a list; if you can construct their names as strings, you use the get
function. Here I create two variables, A
and B
:
> A <- 1:4
> B <- rep(LETTERS[1:2],2)
I then construct a character vector containing their names (stored as ns
) and get
these variables using lapply
. I then set the names of the list to be the same as their original names.
> (ns <- LETTERS[1:2])
[1] "A" "B"
> obj.list <- lapply(ns, get)
> names(obj.list) <- ns
> obj.list
$A
[1] 1 2 3 4
$B
[1] "A" "B" "A" "B"
Then you can use do.call
; the first argument is the function you want and the second is a list with the arguments you want to pass to it.
> do.call(cbind, obj.list)
A B
[1,] "1" "A"
[2,] "2" "B"
[3,] "3" "A"
[4,] "4" "B"
However, as aL3xa correctly notes, this makes a matrix, not a data frame, which may not be what you want if the variables are different classes; here my A
has been coerced to a character vector instead of a numeric vector. To make a data frame from a list, you just call data.frame
on it; then the classes of the variables are retained.
> (AB <- data.frame(obj.list))
A B
1 1 A
2 2 B
3 3 A
4 4 B
> sapply(AB, class)
A B
"integer" "factor"
> str(AB)
'data.frame': 4 obs. of 2 variables:
$ A: int 1 2 3 4
$ B: Factor w/ 2 levels "A","B": 1 2 1 2
You should bare in mind, though, that cbind
will return an atomic vector (matrix) when applied solely on atomic vectors (double
in this case). As you can see in @prasad's and @Aaron's answers, resulting object is a matrix. If you specify other atomic vectors (integer, double, logical, complex) along with character vector, they will get coerced to character. And then you have a problem - you have to convert them to desired classes. So,
if A, B, C & D are all vectors of equal length, one can create data.frame ABCD with
ABCD <- data.frame(A, B, C, D)
Perhaps you should ask "how can I easily gather various vectors of equal length and put them in a data.frame
"? cbind
is great, but sometimes it's not what you're looking for...
You can put all vectors in environment into list using eapply.
obj.list <- eapply(.GlobalEnv,function(x) if(is.vector(x)) x)
obj.list <- obj.list[names(obj.list) %in% LETTERS]