Использование cbind в произвольно длинном списке объектов
Я хотел бы найти способ создания data.frame с помощью cbind()
для объединения многих отдельных объектов. Например, если A, B, C и D - все векторы равной длины, можно создать data.frame
ABCD с
ABCD <- cbind(A,B,C,D)
Однако, когда количество объектов, которые будут объединены, становится большим, становится утомительно печатать все их имена. Кроме того, существует способ вызова cbind()
для вектора имен объектов, например.
objs <- c("A", "B", "C", "D")
ABCD <- cbind(objs)
или в списке, содержащем все объекты для объединения, например
obj.list <- list(A,B,C,D)
ABCD <- cbind(obj.list)
В настоящее время единственным обходным решением, которое я могу придумать, является использование paste()
, cat()
, write.table()
и source()
для построения аргументов cbind()
, записать его как script и указать его, Это кажется очень неприятным клочем. Кроме того, я просмотрел do.call()
, но не может найти способ выполнить то, что я хочу с ним.
Ответы
Ответ 1
Функция do.call
очень полезна здесь:
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
Ответ 2
Сначала вам нужно get
объекты, которые вы хотите, и сохраните их вместе в виде списка; если вы можете построить их имена в виде строк, вы используете функцию get
. Здесь я создаю две переменные, A
и B
:
> A <- 1:4
> B <- rep(LETTERS[1:2],2)
Затем я создаю вектор символов, содержащий их имена (сохраненные как ns
) и get
эти переменные, используя lapply
. Затем я устанавливаю имена списка так же, как их исходные имена.
> (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"
Затем вы можете использовать do.call
; первый аргумент - это функция, которую вы хотите, а второй - список с аргументами, которые вы хотите передать ему.
> do.call(cbind, obj.list)
A B
[1,] "1" "A"
[2,] "2" "B"
[3,] "3" "A"
[4,] "4" "B"
Однако, как правильно отмечает aL3xa, это делает матрицу, а не фрейм данных, которая может быть не такой, какой вы хотите, если переменные являются разными классами; здесь my A
был принужден к символьному вектору вместо числового вектора. Чтобы создать кадр данных из списка, вы просто вызываете data.frame
на нем; то классы переменных сохраняются.
> (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
Ответ 3
Однако вы должны иметь в виду, что cbind
вернет атомный вектор (матрицу) при применении исключительно к атомным векторам (double
в этом случае). Как вы можете видеть в ответах @prasad и @Aaron, результирующий объект является матрицей. Если вы укажете другие атомные векторы (целочисленные, двойные, логические, сложные) вместе с символьным вектором, они будут принуждаться к символу. И тогда у вас есть проблема - вы должны преобразовать их в нужные классы. Таким образом,
если A, B, C и D - все векторы равной длине, можно создать data.frame ABCD с
ABCD <- data.frame(A, B, C, D)
Возможно, вам следует спросить "как я могу легко собрать различные векторы равной длины и поместить их в data.frame
"? cbind
отлично, но иногда это не то, что вы ищете...
Ответ 4
Вы можете поместить все векторы в среду в список, используя eapply.
obj.list <- eapply(.GlobalEnv,function(x) if(is.vector(x)) x)
obj.list <- obj.list[names(obj.list) %in% LETTERS]