Использование cbind для произвольно длинного списка объектов - PullRequest
26 голосов
/ 04 апреля 2011

Я хотел бы найти способ создания 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(), написать его в виде сценария и создать его исходный код. Это похоже на очень неприятный клудж. Кроме того, я изучил do.call(), но, похоже, не могу найти способ выполнить то, что я хочу с ним.

Ответы [ 4 ]

31 голосов
/ 04 апреля 2011

Функция 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
8 голосов
/ 04 апреля 2011

Сначала вам нужно 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, это создает матрицу, а не фрейм данных,что может быть не тем, что вы хотите, если переменные разных классов;здесь мой 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 голосов
/ 04 апреля 2011

Следует помнить, однако, что cbind вернет атомный вектор (матрицу), когда применяется только к атомным векторам (double в этом случае).Как вы можете видеть в ответах @ prasad и @ Aaron, полученный объект представляет собой матрицу.Если вы укажете другие атомарные векторы (целые, двойные, логические, сложные) вместе с вектором символов, они будут приведены к символу.И тогда у вас возникает проблема - вы должны преобразовать их в нужные классы.Итак,

, если A, B, C & D - все векторы одинаковой длины, можно создать data.frame ABCD с

ABCD <- data.frame(A, B, C, D)

Возможно, вам следует спросить "как я могу легко собрать различные векторы одинаковой длины и поместить их в data.frame"? cbind - это здорово, но иногда это не то, что вы ищете ...

1 голос
/ 05 апреля 2011

Вы можете поместить все векторы в среде в список, используя eapply.

obj.list <- eapply(.GlobalEnv,function(x) if(is.vector(x)) x)
obj.list <- obj.list[names(obj.list) %in% LETTERS]
...